From 708afedeb4e2ab0c540bbb00279f52667564ea37 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Wed, 30 Aug 2017 14:01:20 +0100 Subject: [PATCH 001/206] Refactor out common code in AutocompleteManager. (first pass) --- .../auto-complete/AutoCompleteManager.coffee | 48 ++++++------------- .../aceEditor/auto-complete/Helpers.coffee | 33 +++++++++++++ 2 files changed, 47 insertions(+), 34 deletions(-) create mode 100644 services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/Helpers.coffee diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee index 55104341e8..65d5113088 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee @@ -1,21 +1,13 @@ define [ "ide/editor/directives/aceEditor/auto-complete/CommandManager" "ide/editor/directives/aceEditor/auto-complete/EnvironmentManager" + "ide/editor/directives/aceEditor/auto-complete/Helpers" "ace/ace" "ace/ext-language_tools" -], (CommandManager, EnvironmentManager) -> +], (CommandManager, EnvironmentManager, Helpers) -> Range = ace.require("ace/range").Range aceSnippetManager = ace.require('ace/snippets').snippetManager - getLastCommandFragment = (lineUpToCursor) -> - if m = lineUpToCursor.match(/(\\[^\\]+)$/) - return m[1] - else - return null - - getCommandNameFromFragment = (commandFragment) -> - commandFragment?.match(/\\(\w+)\{/)?[1] - class AutoCompleteManager constructor: (@$scope, @editor, @element, @labelsManager, @graphics, @preamble) -> @suggestionManager = new CommandManager() @@ -48,15 +40,11 @@ define [ Preamble = @preamble GraphicsCompleter = getCompletions: (editor, session, pos, prefix, callback) -> - upToCursorRange = new Range(pos.row, 0, pos.row, pos.column) - lineUpToCursor = editor.getSession().getTextRange(upToCursorRange) - commandFragment = getLastCommandFragment(lineUpToCursor) + context = Helpers.getContext(editor, pos) + {lineUpToCursor, commandFragment, lineBeyondCursor, needsClosingBrace} = context if commandFragment match = commandFragment.match(/^~?\\(includegraphics(?:\[.*])?){([^}]*, *)?(\w*)/) if match - beyondCursorRange = new Range(pos.row, pos.column, pos.row, 99999) - lineBeyondCursor = editor.getSession().getTextRange(beyondCursorRange) - needsClosingBrace = !lineBeyondCursor.match(/^[^{]*}/) commandName = match[1] currentArg = match[3] graphicsPaths = Preamble.getGraphicsPaths() @@ -78,15 +66,11 @@ define [ labelsManager = @labelsManager LabelsCompleter = getCompletions: (editor, session, pos, prefix, callback) -> - upToCursorRange = new Range(pos.row, 0, pos.row, pos.column) - lineUpToCursor = editor.getSession().getTextRange(upToCursorRange) - commandFragment = getLastCommandFragment(lineUpToCursor) + context = Helpers.getContext(editor, pos) + {lineUpToCursor, commandFragment, lineBeyondCursor, needsClosingBrace} = context if commandFragment refMatch = commandFragment.match(/^~?\\([a-z]*ref){([^}]*, *)?(\w*)/) if refMatch - beyondCursorRange = new Range(pos.row, pos.column, pos.row, 99999) - lineBeyondCursor = editor.getSession().getTextRange(beyondCursorRange) - needsClosingBrace = !lineBeyondCursor.match(/^[^{]*}/) commandName = refMatch[1] currentArg = refMatch[2] result = [] @@ -108,15 +92,13 @@ define [ references = @$scope.$root._references ReferencesCompleter = getCompletions: (editor, session, pos, prefix, callback) -> - upToCursorRange = new Range(pos.row, 0, pos.row, pos.column) - lineUpToCursor = editor.getSession().getTextRange(upToCursorRange) - commandFragment = getLastCommandFragment(lineUpToCursor) + context = Helpers.getContext(editor, pos) + {lineUpToCursor, commandFragment, lineBeyondCursor, needsClosingBrace} = context if commandFragment - citeMatch = commandFragment.match(/^~?\\([a-z]*cite[a-z]*(?:\[.*])?){([^}]*, *)?(\w*)/) + citeMatch = commandFragment.match( + /^~?\\([a-z]*cite[a-z]*(?:\[.*])?){([^}]*, *)?(\w*)/ + ) if citeMatch - beyondCursorRange = new Range(pos.row, pos.column, pos.row, 99999) - lineBeyondCursor = editor.getSession().getTextRange(beyondCursorRange) - needsClosingBrace = !lineBeyondCursor.match(/^[^{]*}/) commandName = citeMatch[1] previousArgs = citeMatch[2] currentArg = citeMatch[3] @@ -160,8 +142,8 @@ define [ onChange: (change) -> cursorPosition = @editor.getCursorPosition() end = change.end - range = new Range(end.row, 0, end.row, end.column) - lineUpToCursor = @editor.getSession().getTextRange(range) + context = Helpers.getContext(editor, end) + {lineUpToCursor, commandFragment, lineBeyondCursor, needsClosingBrace} = context if lineUpToCursor.match(/.*%.*/) return lastCharIsBackslash = lineUpToCursor.slice(-1) == "\\" @@ -170,8 +152,6 @@ define [ if lastTwoChars.match(/^\\[^a-z]$/) @editor?.completer?.detach?() return - commandFragment = getLastCommandFragment(lineUpToCursor) - commandName = getCommandNameFromFragment(commandFragment) if commandName in ['begin', 'end'] return # Check that this change was made by us, not a collaborator @@ -310,5 +290,5 @@ define [ currentLineOffset = i + 1 break currentLine = text.slice(currentLineOffset, pos) - fragment = getLastCommandFragment(currentLine) or "" + fragment = Helpers.getLastCommandFragment(currentLine) or "" return fragment diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/Helpers.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/Helpers.coffee new file mode 100644 index 0000000000..2744d1fa29 --- /dev/null +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/Helpers.coffee @@ -0,0 +1,33 @@ +define [ + "ace/ace" + "ace/ext-language_tools" +], () -> + Range = ace.require("ace/range").Range + + Helpers = + getLastCommandFragment: (lineUpToCursor) -> + if m = lineUpToCursor.match(/(\\[^\\]+)$/) + return m[1] + else + return null + + getCommandNameFromFragment: (commandFragment) -> + commandFragment?.match(/\\(\w+)\{/)?[1] + + getContext: (editor, pos) -> + upToCursorRange = new Range(pos.row, 0, pos.row, pos.column) + lineUpToCursor = editor.getSession().getTextRange(upToCursorRange) + commandFragment = Helpers.getLastCommandFragment(lineUpToCursor) + commandName = Helpers.getCommandNameFromFragment(commandFragment) + beyondCursorRange = new Range(pos.row, pos.column, pos.row, 99999) + lineBeyondCursor = editor.getSession().getTextRange(beyondCursorRange) + needsClosingBrace = !lineBeyondCursor.match(/^[^{]*}/) + return { + lineUpToCursor, + commandFragment, + commandName, + lineBeyondCursor, + needsClosingBrace + } + + return Helpers From 8a612df0098a2c669b2f6b83e16b449c8c0a9a9e Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Thu, 31 Aug 2017 08:59:38 +0100 Subject: [PATCH 002/206] fix use of context helper in onChange --- .../aceEditor/auto-complete/AutoCompleteManager.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee index 65d5113088..e760264191 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee @@ -142,8 +142,8 @@ define [ onChange: (change) -> cursorPosition = @editor.getCursorPosition() end = change.end - context = Helpers.getContext(editor, end) - {lineUpToCursor, commandFragment, lineBeyondCursor, needsClosingBrace} = context + context = Helpers.getContext(@editor, end) + {lineUpToCursor, commandFragment, commandName, lineBeyondCursor, needsClosingBrace} = context if lineUpToCursor.match(/.*%.*/) return lastCharIsBackslash = lineUpToCursor.slice(-1) == "\\" From 54af7fbdcbfd9db5dd8d74adf14695f7d86c5400 Mon Sep 17 00:00:00 2001 From: Nate Stemen Date: Thu, 31 Aug 2017 10:39:46 +0100 Subject: [PATCH 003/206] adding in a few new commands that appear in SL-CLSI analysis --- .../auto-complete/CommandManager.coffee | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/CommandManager.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/CommandManager.coffee index 0a202a7793..c5ab9179ec 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/CommandManager.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/CommandManager.coffee @@ -1,10 +1,15 @@ define [], () -> noArgumentCommands = [ 'item', 'hline', 'lipsum', 'centering', 'noindent', 'textwidth', 'draw', - 'maketitle', 'newpage', 'verb', 'bibliography', 'fi', 'hfill', 'par', - 'in', 'sum', 'cdot', 'alpha', 'ldots', 'else', 'linewidth', 'left', - 'right', 'today', 'clearpage', 'newline', 'endinput', 'mu', - 'tableofcontents', 'vfill', 'bigskip', 'fill', 'cleardoublepage' + 'maketitle', 'newpage', 'verb', 'bibliography', 'hfill', 'par', + 'in', 'sum', 'cdot', 'ldots', 'linewidth', 'left', 'right', 'today', + 'clearpage', 'newline', 'endinput', 'tableofcontents', 'vfill', + 'bigskip', 'fill', 'cleardoublepage', 'infty', 'leq', 'geq', 'times', + 'alpha', 'beta', 'gamma', 'delta', 'epsilon', 'varepsilon', 'zeta', + 'eta', 'theta', 'vartheta', 'iota', 'kappa', 'lambda', 'mu', 'nu', 'xi', + 'pi', 'varpi', 'rho', 'varrho', 'sigma', 'varsigma', 'tau', 'upsilon', + 'phi', 'varphi', 'chi', 'psi', 'omega', 'Gamma', 'Delta', 'Theta', + 'Lambda', 'Xi', 'Pi', 'Sigma', 'Upsilon', 'Phi', 'Psi', 'Omega' ] singleArgumentCommands = [ 'chapter', 'usepackage', 'section', 'label', 'textbf', 'subsection', @@ -13,11 +18,12 @@ define [], () -> 'hspace', 'bibitem', 'url', 'large', 'subsubsection', 'textsc', 'date', 'footnote', 'small', 'thanks', 'underline', 'graphicspath', 'pageref', 'section*', 'subsection*', 'subsubsection*', 'sqrt', 'text', - 'normalsize', 'Large', 'paragraph', 'pagestyle', 'thispagestyle', - 'bibliographystyle' + 'normalsize', 'footnotesize', 'Large', 'paragraph', 'pagestyle', + 'thispagestyle', 'bibliographystyle', 'hat' ] doubleArgumentCommands = [ - 'newcommand', 'frac', 'renewcommand', 'setlength', 'href', 'newtheorem' + 'newcommand', 'frac', 'dfrac', 'renewcommand', 'setlength', 'href', + 'newtheorem' ] tripleArgumentCommands = [ 'addcontentsline', 'newacronym', 'multicolumn' @@ -25,12 +31,12 @@ define [], () -> special = ['LaTeX', 'TeX'] rawCommands = [].concat( - noArgumentCommands, - singleArgumentCommands, - doubleArgumentCommands, - tripleArgumentCommands, - special - ) + noArgumentCommands, + singleArgumentCommands, + doubleArgumentCommands, + tripleArgumentCommands, + special + ) noArgumentCommands = for cmd in noArgumentCommands { From 65b2c42fadb36c89818c1727d5c3c189a83159f0 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Fri, 1 Sep 2017 10:32:26 +0100 Subject: [PATCH 004/206] Emit event when op is acknowledged --- services/web/public/coffee/ide/editor/Document.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/web/public/coffee/ide/editor/Document.coffee b/services/web/public/coffee/ide/editor/Document.coffee index d1aa883734..229e791c67 100644 --- a/services/web/public/coffee/ide/editor/Document.coffee +++ b/services/web/public/coffee/ide/editor/Document.coffee @@ -309,6 +309,9 @@ define [ @ide.pushEvent "op:acknowledged", doc_id: @doc_id op: op + @ide.$scope.$emit "ide:opAcknowledged", + doc_id: @doc_id + op: op @trigger "op:acknowledged" @doc.on "op:timeout", (op) => @ide.pushEvent "op:timeout", From c42b08fcffa26774ecb313baef158db425dba6bb Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Fri, 1 Sep 2017 17:54:35 +0100 Subject: [PATCH 005/206] Dispatch linting error event when changed --- .../coffee/ide/editor/directives/aceEditor.coffee | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee index 1cb527fc40..5107c89a7e 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee @@ -35,7 +35,7 @@ define [ url = ace.config._moduleUrl(args...) + "?fingerprint=#{window.aceFingerprint}" return url - App.directive "aceEditor", ($timeout, $compile, $rootScope, event_tracking, localStorage, $cacheFactory, labels, graphics, preamble) -> + App.directive "aceEditor", ($timeout, $compile, $rootScope, event_tracking, localStorage, $cacheFactory, labels, graphics, preamble, ide) -> monkeyPatchSearch($rootScope, $compile) return { @@ -374,6 +374,18 @@ define [ if scope.eventsBridge? session.on "changeScrollTop", onScroll + hasLintingError = null + session.on('changeAnnotation', () -> + hasErrors = session + .getAnnotations() + .filter((annotation) -> annotation.type == 'error') + .length > 0 + + if (hasLintingError != hasErrors) + ide.$scope.$broadcast('ide:lintingError', hasErrors) + hasLintingError = hasErrors + ) + setTimeout () -> # Let any listeners init themselves onScroll(editor.renderer.getScrollTop()) From dab799a81b9a8beb2eed1f89ffc60379763410fd Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Fri, 1 Sep 2017 15:31:08 +0100 Subject: [PATCH 006/206] First pass at auto compile ctrl --- services/web/app/views/project/editor/pdf.pug | 1 + .../web/public/coffee/ide/pdf/PdfManager.coffee | 3 ++- .../pdf/controllers/AutoCompileController.coffee | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 services/web/public/coffee/ide/pdf/controllers/AutoCompileController.coffee diff --git a/services/web/app/views/project/editor/pdf.pug b/services/web/app/views/project/editor/pdf.pug index 960d4bf2c9..5cc360afe1 100644 --- a/services/web/app/views/project/editor/pdf.pug +++ b/services/web/app/views/project/editor/pdf.pug @@ -1,4 +1,5 @@ div.full-size.pdf(ng-controller="PdfController") + .FIXME(ng-controller="AutoCompileController") .toolbar.toolbar-tall .btn-group( dropdown, diff --git a/services/web/public/coffee/ide/pdf/PdfManager.coffee b/services/web/public/coffee/ide/pdf/PdfManager.coffee index 56fe7b1e68..871257031a 100644 --- a/services/web/public/coffee/ide/pdf/PdfManager.coffee +++ b/services/web/public/coffee/ide/pdf/PdfManager.coffee @@ -1,7 +1,8 @@ define [ "ide/pdf/controllers/PdfController" "ide/pdf/controllers/PdfViewToggleController" - "ide/pdfng/directives/pdfJs" + "ide/pdfng/directives/pdfJs", + "ide/pdf/controllers/AutoCompileController" ], () -> class PdfManager constructor: (@ide, @$scope) -> diff --git a/services/web/public/coffee/ide/pdf/controllers/AutoCompileController.coffee b/services/web/public/coffee/ide/pdf/controllers/AutoCompileController.coffee new file mode 100644 index 0000000000..6d59aa7703 --- /dev/null +++ b/services/web/public/coffee/ide/pdf/controllers/AutoCompileController.coffee @@ -0,0 +1,14 @@ +define [ + "base" +], (App) -> + App.controller "AutoCompileController", ["$scope", "ide", ($scope, ide) -> + hasLintingError = false + + ide.$scope.$on "ide:opAcknowledged", _.debounce(() -> + if (!hasLintingError) + $scope.recompile() + , 3000) + + ide.$scope.$on "ide:lintingError", (e, hasError) -> + hasLintingError = hasError + ] \ No newline at end of file From 53bb4fdde081107131a239922addb7544be8fa10 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Mon, 4 Sep 2017 11:38:28 +0100 Subject: [PATCH 007/206] Use var in root scope instead of event to track linting error --- .../public/coffee/ide/editor/directives/aceEditor.coffee | 9 ++++----- .../ide/pdf/controllers/AutoCompileController.coffee | 7 +------ 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee index 5107c89a7e..7660178182 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee @@ -35,7 +35,7 @@ define [ url = ace.config._moduleUrl(args...) + "?fingerprint=#{window.aceFingerprint}" return url - App.directive "aceEditor", ($timeout, $compile, $rootScope, event_tracking, localStorage, $cacheFactory, labels, graphics, preamble, ide) -> + App.directive "aceEditor", ($timeout, $compile, $rootScope, event_tracking, localStorage, $cacheFactory, labels, graphics, preamble) -> monkeyPatchSearch($rootScope, $compile) return { @@ -374,16 +374,15 @@ define [ if scope.eventsBridge? session.on "changeScrollTop", onScroll - hasLintingError = null + $rootScope.hasLintingError = false session.on('changeAnnotation', () -> hasErrors = session .getAnnotations() .filter((annotation) -> annotation.type == 'error') .length > 0 - if (hasLintingError != hasErrors) - ide.$scope.$broadcast('ide:lintingError', hasErrors) - hasLintingError = hasErrors + if ($rootScope.hasLintingError != hasErrors) + $rootScope.hasLintingError = hasErrors ) setTimeout () -> diff --git a/services/web/public/coffee/ide/pdf/controllers/AutoCompileController.coffee b/services/web/public/coffee/ide/pdf/controllers/AutoCompileController.coffee index 6d59aa7703..7e28153262 100644 --- a/services/web/public/coffee/ide/pdf/controllers/AutoCompileController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/AutoCompileController.coffee @@ -2,13 +2,8 @@ define [ "base" ], (App) -> App.controller "AutoCompileController", ["$scope", "ide", ($scope, ide) -> - hasLintingError = false - ide.$scope.$on "ide:opAcknowledged", _.debounce(() -> - if (!hasLintingError) + if (!ide.$scope.hasLintingError) $scope.recompile() , 3000) - - ide.$scope.$on "ide:lintingError", (e, hasError) -> - hasLintingError = hasError ] \ No newline at end of file From 9f1adfd0c0b5bbe8157f6f70ec4be474780454af Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Mon, 4 Sep 2017 11:41:52 +0100 Subject: [PATCH 008/206] Replace separate autocompile ctrl with listener in PdfController --- services/web/app/views/project/editor/pdf.pug | 1 - services/web/public/coffee/ide/pdf/PdfManager.coffee | 3 +-- .../ide/pdf/controllers/AutoCompileController.coffee | 9 --------- .../coffee/ide/pdf/controllers/PdfController.coffee | 5 +++++ 4 files changed, 6 insertions(+), 12 deletions(-) delete mode 100644 services/web/public/coffee/ide/pdf/controllers/AutoCompileController.coffee diff --git a/services/web/app/views/project/editor/pdf.pug b/services/web/app/views/project/editor/pdf.pug index 5cc360afe1..960d4bf2c9 100644 --- a/services/web/app/views/project/editor/pdf.pug +++ b/services/web/app/views/project/editor/pdf.pug @@ -1,5 +1,4 @@ div.full-size.pdf(ng-controller="PdfController") - .FIXME(ng-controller="AutoCompileController") .toolbar.toolbar-tall .btn-group( dropdown, diff --git a/services/web/public/coffee/ide/pdf/PdfManager.coffee b/services/web/public/coffee/ide/pdf/PdfManager.coffee index 871257031a..56fe7b1e68 100644 --- a/services/web/public/coffee/ide/pdf/PdfManager.coffee +++ b/services/web/public/coffee/ide/pdf/PdfManager.coffee @@ -1,8 +1,7 @@ define [ "ide/pdf/controllers/PdfController" "ide/pdf/controllers/PdfViewToggleController" - "ide/pdfng/directives/pdfJs", - "ide/pdf/controllers/AutoCompileController" + "ide/pdfng/directives/pdfJs" ], () -> class PdfManager constructor: (@ide, @$scope) -> diff --git a/services/web/public/coffee/ide/pdf/controllers/AutoCompileController.coffee b/services/web/public/coffee/ide/pdf/controllers/AutoCompileController.coffee deleted file mode 100644 index 7e28153262..0000000000 --- a/services/web/public/coffee/ide/pdf/controllers/AutoCompileController.coffee +++ /dev/null @@ -1,9 +0,0 @@ -define [ - "base" -], (App) -> - App.controller "AutoCompileController", ["$scope", "ide", ($scope, ide) -> - ide.$scope.$on "ide:opAcknowledged", _.debounce(() -> - if (!ide.$scope.hasLintingError) - $scope.recompile() - , 3000) - ] \ No newline at end of file diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index 42c1cd1bc8..23770faef7 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -73,6 +73,11 @@ define [ $scope.pdf.view = 'errors' $scope.pdf.renderingError = true + ide.$scope.$on "ide:opAcknowledged", _.debounce(() -> + if (!ide.$scope.hasLintingError) + $scope.recompile() + , 3000) + # abort compile if syntax checks fail $scope.stop_on_validation_error = localStorage("stop_on_validation_error:#{$scope.project_id}") $scope.stop_on_validation_error ?= true # turn on for all users by default From fc4a260d95d47b03105ddcaff20e82e4da5fb925 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Mon, 4 Sep 2017 13:22:49 +0100 Subject: [PATCH 009/206] Ensure error is from linting not compile logs --- .../web/public/coffee/ide/editor/directives/aceEditor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee index 7660178182..b6455d8e93 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee @@ -378,7 +378,7 @@ define [ session.on('changeAnnotation', () -> hasErrors = session .getAnnotations() - .filter((annotation) -> annotation.type == 'error') + .filter((annotation) -> annotation.fromLinting && annotation.type == 'error') .length > 0 if ($rootScope.hasLintingError != hasErrors) From 64f70942d0b133a256fcc3106de90dbd0e8c99b8 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Tue, 5 Sep 2017 11:41:11 +0100 Subject: [PATCH 010/206] Ensure user is in beta program --- .../web/public/coffee/ide/pdf/controllers/PdfController.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index 23770faef7..abbf6b401c 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -6,7 +6,6 @@ define [ "services/log-hints-feedback" ], (App, Ace, HumanReadableLogs, BibLogParser) -> App.controller "PdfController", ($scope, $http, ide, $modal, synctex, event_tracking, logHintsFeedback, localStorage) -> - # enable per-user containers by default perUserCompile = true autoCompile = true @@ -76,7 +75,7 @@ define [ ide.$scope.$on "ide:opAcknowledged", _.debounce(() -> if (!ide.$scope.hasLintingError) $scope.recompile() - , 3000) + , 3000) if window.user?.betaProgram # abort compile if syntax checks fail $scope.stop_on_validation_error = localStorage("stop_on_validation_error:#{$scope.project_id}") From cca7f553221562aebe9bd2388d78a2bc2a7915bd Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Tue, 5 Sep 2017 12:21:23 +0100 Subject: [PATCH 011/206] Add autocompile setting --- services/web/app/views/project/editor/pdf.pug | 9 +++++++++ .../coffee/ide/pdf/controllers/PdfController.coffee | 8 +++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/services/web/app/views/project/editor/pdf.pug b/services/web/app/views/project/editor/pdf.pug index 960d4bf2c9..6e040f304e 100644 --- a/services/web/app/views/project/editor/pdf.pug +++ b/services/web/app/views/project/editor/pdf.pug @@ -26,6 +26,15 @@ div.full-size.pdf(ng-controller="PdfController") ) span.caret ul.dropdown-menu.dropdown-menu-left + // Only show on beta program? + li.dropdown-header Auto-compile + li + a(href, ng-click="autocompile_enabled = true") + i.fa.fa-fw(ng-class="{'fa-check': autocompile_enabled}") + |  On + a(href, ng-click="autocompile_enabled = false") + i.fa.fa-fw(ng-class="{'fa-check': !autocompile_enabled}") + |  Off li.dropdown-header #{translate("compile_mode")} li a(href, ng-click="draft = false") diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index abbf6b401c..3406f36f51 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -72,10 +72,16 @@ define [ $scope.pdf.view = 'errors' $scope.pdf.renderingError = true + $scope.autocompile_enabled = localStorage("autocompile_enabled:#{$scope.project_id}") or false + $scope.$watch "autocompile_enabled", (newValue, oldValue) -> + if newValue? and oldValue != newValue + localStorage("autocompile_enabled:#{$scope.project_id}", newValue) + +# TODO: toggle listener when setting changed? ide.$scope.$on "ide:opAcknowledged", _.debounce(() -> if (!ide.$scope.hasLintingError) $scope.recompile() - , 3000) if window.user?.betaProgram + , 3000) if window.user?.betaProgram and $scope.autocompile_enabled # abort compile if syntax checks fail $scope.stop_on_validation_error = localStorage("stop_on_validation_error:#{$scope.project_id}") From a9e94f6a6d02040640a8e2c5ebc3d771e295b04a Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Tue, 5 Sep 2017 13:57:11 +0100 Subject: [PATCH 012/206] Unsubscribe autocompile listener if disabled --- .../ide/pdf/controllers/PdfController.coffee | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index 3406f36f51..3c9915cdfa 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -72,16 +72,26 @@ define [ $scope.pdf.view = 'errors' $scope.pdf.renderingError = true + triggerAutoCompile = () -> + if (!ide.$scope.hasLintingError) + $scope.recompile() + + autoCompileListener = null + toggleAutoCompile = (enabling) -> + if enabling + autoCompileListener = ide.$scope.$on "ide:opAcknowledged", _.debounce(triggerAutoCompile, 3000) + else + autoCompileListener() if autoCompileListener + autoCompileListener = null + $scope.autocompile_enabled = localStorage("autocompile_enabled:#{$scope.project_id}") or false $scope.$watch "autocompile_enabled", (newValue, oldValue) -> if newValue? and oldValue != newValue localStorage("autocompile_enabled:#{$scope.project_id}", newValue) + toggleAutoCompile(newValue) -# TODO: toggle listener when setting changed? - ide.$scope.$on "ide:opAcknowledged", _.debounce(() -> - if (!ide.$scope.hasLintingError) - $scope.recompile() - , 3000) if window.user?.betaProgram and $scope.autocompile_enabled + if window.user?.betaProgram and $scope.autocompile_enabled + toggleAutoCompile(true) # abort compile if syntax checks fail $scope.stop_on_validation_error = localStorage("stop_on_validation_error:#{$scope.project_id}") From ce630e4971172d71fe9d410995082078302eb333 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Tue, 5 Sep 2017 14:00:14 +0100 Subject: [PATCH 013/206] Extract timeout constant --- .../public/coffee/ide/pdf/controllers/PdfController.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index 3c9915cdfa..2d2eac5a51 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -5,6 +5,8 @@ define [ "libs/bib-log-parser" "services/log-hints-feedback" ], (App, Ace, HumanReadableLogs, BibLogParser) -> + AUTO_COMPILE_TIMEOUT = 3000 + App.controller "PdfController", ($scope, $http, ide, $modal, synctex, event_tracking, logHintsFeedback, localStorage) -> # enable per-user containers by default perUserCompile = true @@ -79,7 +81,7 @@ define [ autoCompileListener = null toggleAutoCompile = (enabling) -> if enabling - autoCompileListener = ide.$scope.$on "ide:opAcknowledged", _.debounce(triggerAutoCompile, 3000) + autoCompileListener = ide.$scope.$on "ide:opAcknowledged", _.debounce(triggerAutoCompile, AUTO_COMPILE_TIMEOUT) else autoCompileListener() if autoCompileListener autoCompileListener = null From 42ae7a6479b4ebe337097592665eb6c878410158 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Tue, 5 Sep 2017 14:56:28 +0100 Subject: [PATCH 014/206] Only show auto compile option for beta program users --- services/web/app/views/project/editor/pdf.pug | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/services/web/app/views/project/editor/pdf.pug b/services/web/app/views/project/editor/pdf.pug index 6e040f304e..4b70fe566d 100644 --- a/services/web/app/views/project/editor/pdf.pug +++ b/services/web/app/views/project/editor/pdf.pug @@ -27,14 +27,16 @@ div.full-size.pdf(ng-controller="PdfController") span.caret ul.dropdown-menu.dropdown-menu-left // Only show on beta program? - li.dropdown-header Auto-compile - li - a(href, ng-click="autocompile_enabled = true") - i.fa.fa-fw(ng-class="{'fa-check': autocompile_enabled}") - |  On - a(href, ng-click="autocompile_enabled = false") - i.fa.fa-fw(ng-class="{'fa-check': !autocompile_enabled}") - |  Off + if user.betaProgram + li.dropdown-header Auto-compile + li + a(href, ng-click="autocompile_enabled = true") + i.fa.fa-fw(ng-class="{'fa-check': autocompile_enabled}") + |  On + li + a(href, ng-click="autocompile_enabled = false") + i.fa.fa-fw(ng-class="{'fa-check': !autocompile_enabled}") + |  Off li.dropdown-header #{translate("compile_mode")} li a(href, ng-click="draft = false") From 32b849faf7f8ccfb107e0817a1dcf11ed3fcbecc Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Tue, 5 Sep 2017 14:56:48 +0100 Subject: [PATCH 015/206] Use translations --- services/web/app/views/project/editor/pdf.pug | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/web/app/views/project/editor/pdf.pug b/services/web/app/views/project/editor/pdf.pug index 4b70fe566d..db4a0e9dd3 100644 --- a/services/web/app/views/project/editor/pdf.pug +++ b/services/web/app/views/project/editor/pdf.pug @@ -28,15 +28,15 @@ div.full-size.pdf(ng-controller="PdfController") ul.dropdown-menu.dropdown-menu-left // Only show on beta program? if user.betaProgram - li.dropdown-header Auto-compile + li.dropdown-header #{translate("auto_compile")} li a(href, ng-click="autocompile_enabled = true") i.fa.fa-fw(ng-class="{'fa-check': autocompile_enabled}") - |  On + |  #{translate('on')} li a(href, ng-click="autocompile_enabled = false") i.fa.fa-fw(ng-class="{'fa-check': !autocompile_enabled}") - |  Off + |  #{translate('off')} li.dropdown-header #{translate("compile_mode")} li a(href, ng-click="draft = false") From b2257db2c2ab67eeca9aba15b3397d412b503187 Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 5 Sep 2017 19:26:13 +0200 Subject: [PATCH 016/206] Don't get confused by commands in arguments in autocomplete --- .../auto-complete/AutoCompleteManager.coffee | 8 ++++---- .../aceEditor/auto-complete/Helpers.coffee | 16 ++++++++++++++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee index e760264191..24a524b4b6 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee @@ -213,10 +213,10 @@ define [ range.start.column, ) ) - # Delete back to last backslash, as appropriate - lastBackslashIndex = lineUpToCursor.lastIndexOf('\\') - if lastBackslashIndex != -1 - leftRange.start.column = lastBackslashIndex + # Delete back to command start, as appropriate + commandStartIndex = Helpers.getLastCommandFragmentIndex(lineUpToCursor) + if commandStartIndex != -1 + leftRange.start.column = commandStartIndex else leftRange.start.column -= completions.filterText.length editor.session.remove(leftRange) diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/Helpers.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/Helpers.coffee index 2744d1fa29..7beb41debd 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/Helpers.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/Helpers.coffee @@ -6,11 +6,23 @@ define [ Helpers = getLastCommandFragment: (lineUpToCursor) -> - if m = lineUpToCursor.match(/(\\[^\\]+)$/) - return m[1] + if (index = Helpers.getLastCommandFragmentIndex(lineUpToCursor)) > -1 + return lineUpToCursor.slice(index) else return null + getLastCommandFragmentIndex: (lineUpToCursor) -> + # This is hack to let us skip over commands in arguments, and + # go to the command on the same 'level' as us. E.g. + # \includegraphics[width=\textwidth]{.. + # should not match the \textwidth. + blankArguments = lineUpToCursor.replace /\[([^\]]*)\]/g, (args) -> + Array(args.length+1).join('.') + if m = blankArguments.match(/(\\[^\\]+)$/) + return m.index + else + return -1 + getCommandNameFromFragment: (commandFragment) -> commandFragment?.match(/\\(\w+)\{/)?[1] From 0c76b06a97a57bc0505f11e08b297f3a3acdef56 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 7 Sep 2017 10:02:48 +0100 Subject: [PATCH 017/206] Increase timeout to 5 seconds --- .../web/public/coffee/ide/pdf/controllers/PdfController.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index 2d2eac5a51..68ded26551 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -5,7 +5,7 @@ define [ "libs/bib-log-parser" "services/log-hints-feedback" ], (App, Ace, HumanReadableLogs, BibLogParser) -> - AUTO_COMPILE_TIMEOUT = 3000 + AUTO_COMPILE_TIMEOUT = 5000 App.controller "PdfController", ($scope, $http, ide, $modal, synctex, event_tracking, logHintsFeedback, localStorage) -> # enable per-user containers by default From 8a8d98935b6ff15dff25748f51f3e95feaadc88c Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 7 Sep 2017 10:04:10 +0100 Subject: [PATCH 018/206] Remove unnecessary linting check in annotations listener When user starts typing, compile log annotations are replaced with linter ones, so log errrors can be ignored for this purpose. Because we don't try to autocompile unless the user types something --- .../web/public/coffee/ide/editor/directives/aceEditor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee index b6455d8e93..7660178182 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee @@ -378,7 +378,7 @@ define [ session.on('changeAnnotation', () -> hasErrors = session .getAnnotations() - .filter((annotation) -> annotation.fromLinting && annotation.type == 'error') + .filter((annotation) -> annotation.type == 'error') .length > 0 if ($rootScope.hasLintingError != hasErrors) From 11386eb817f61bd5ec4e5d0416c4b62455f3129a Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 7 Sep 2017 10:20:21 +0100 Subject: [PATCH 019/206] Add option to autocompile, so can be identified in event tracking --- .../web/public/coffee/ide/pdf/controllers/PdfController.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index 68ded26551..cc569a9465 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -76,7 +76,7 @@ define [ triggerAutoCompile = () -> if (!ide.$scope.hasLintingError) - $scope.recompile() + $scope.recompile(isBackgroundAutoCompile: true) autoCompileListener = null toggleAutoCompile = (enabling) -> From a8cdfb7ecc86625b462993f8abc40e0df1a54dfe Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 7 Sep 2017 11:13:38 +0100 Subject: [PATCH 020/206] Track autocompile setting change --- .../web/public/coffee/ide/pdf/controllers/PdfController.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index cc569a9465..65b0263163 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -91,6 +91,7 @@ define [ if newValue? and oldValue != newValue localStorage("autocompile_enabled:#{$scope.project_id}", newValue) toggleAutoCompile(newValue) + event_tracking.sendMB "autocompile-setting-changed", newValue if window.user?.betaProgram and $scope.autocompile_enabled toggleAutoCompile(true) From 9cde236bc8da2d7f7bc9fe6ead7ff475deeb5b03 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 7 Sep 2017 11:28:36 +0100 Subject: [PATCH 021/206] Add comment explaining lint errors vs compile log errors --- .../web/public/coffee/ide/editor/directives/aceEditor.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee index 7660178182..0b22e75221 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee @@ -376,6 +376,10 @@ define [ $rootScope.hasLintingError = false session.on('changeAnnotation', () -> + # Both linter errors and compile logs are set as error annotations, + # however when the user types something, the compile logs are + # replaced with linter errors. When we check for lint errors before + # autocompile we are guaranteed to get linter errors hasErrors = session .getAnnotations() .filter((annotation) -> annotation.type == 'error') From e2523c569e3eaf88153322b21877ef1d08bfa88b Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 7 Sep 2017 17:45:16 +0100 Subject: [PATCH 022/206] Filter linting warnings --- .../web/public/coffee/ide/editor/directives/aceEditor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee index 0b22e75221..40ae0bb06f 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee @@ -382,7 +382,7 @@ define [ # autocompile we are guaranteed to get linter errors hasErrors = session .getAnnotations() - .filter((annotation) -> annotation.type == 'error') + .filter((annotation) -> annotation.type != 'info') .length > 0 if ($rootScope.hasLintingError != hasErrors) From 4a490aafbf9fb5d6268115730340aabaf5528baf Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Fri, 8 Sep 2017 10:42:54 +0100 Subject: [PATCH 023/206] Improve UX of triggering autocompile. If a user is making infrequent edits (i.e. if reading and making small changes), then waiting 5 seconds for a recompile is bad. Therefore we track the time since the last recompile and use this to decide whether a recompile should be run. This reduces the time to recompile, unless the user is typing for a significant amount of time. --- .../ide/pdf/controllers/PdfController.coffee | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index 65b0263163..d55203c267 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -6,6 +6,7 @@ define [ "services/log-hints-feedback" ], (App, Ace, HumanReadableLogs, BibLogParser) -> AUTO_COMPILE_TIMEOUT = 5000 + OP_ACKNOWLEDGEMENT_TIMEOUT = 1100 App.controller "PdfController", ($scope, $http, ide, $modal, synctex, event_tracking, logHintsFeedback, localStorage) -> # enable per-user containers by default @@ -74,14 +75,26 @@ define [ $scope.pdf.view = 'errors' $scope.pdf.renderingError = true + autoCompileTimeout = null triggerAutoCompile = () -> - if (!ide.$scope.hasLintingError) - $scope.recompile(isBackgroundAutoCompile: true) + return if autoCompileTimeout + + timeSinceLastCompile = Date.now() - $scope.recompiledAt + + if timeSinceLastCompile >= AUTO_COMPILE_TIMEOUT + if (!ide.$scope.hasLintingError) + $scope.recompile(isBackgroundAutoCompile: true) + else + # Extend remainder of timeout + autoCompileTimeout = setTimeout () -> + autoCompileTimeout = null + triggerAutoCompile() + , AUTO_COMPILE_TIMEOUT - timeSinceLastCompile autoCompileListener = null toggleAutoCompile = (enabling) -> if enabling - autoCompileListener = ide.$scope.$on "ide:opAcknowledged", _.debounce(triggerAutoCompile, AUTO_COMPILE_TIMEOUT) + autoCompileListener = ide.$scope.$on "ide:opAcknowledged", _.debounce(triggerAutoCompile, OP_ACKNOWLEDGEMENT_TIMEOUT) else autoCompileListener() if autoCompileListener autoCompileListener = null @@ -422,6 +435,8 @@ define [ $scope.pdf.renderingError = false $scope.pdf.error = true $scope.pdf.view = 'errors' + .finally () -> + $scope.recompiledAt = Date.now() # This needs to be public. ide.$scope.recompile = $scope.recompile From 38eaf82c65b979c0b328883e1c4d47fd475e582c Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Fri, 8 Sep 2017 11:00:46 +0100 Subject: [PATCH 024/206] Ensure that time is monotonic when running autocompile --- .../public/coffee/ide/pdf/controllers/PdfController.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index d55203c267..897983f47a 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -80,8 +80,11 @@ define [ return if autoCompileTimeout timeSinceLastCompile = Date.now() - $scope.recompiledAt + # If time is non-monotonic, assume that the user's system clock has been + # changed and continue with recompile + isTimeNonMonotonic = timeSinceLastPoll < 0; - if timeSinceLastCompile >= AUTO_COMPILE_TIMEOUT + if isTimeNonMonotonic || timeSinceLastCompile >= AUTO_COMPILE_TIMEOUT if (!ide.$scope.hasLintingError) $scope.recompile(isBackgroundAutoCompile: true) else From 80c00b13f75462617942014535b55427133d4d7f Mon Sep 17 00:00:00 2001 From: Nate Stemen Date: Mon, 11 Sep 2017 11:27:03 +0100 Subject: [PATCH 025/206] Raising the limit on safari regex count --- .../directives/aceEditor/auto-complete/CommandManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/CommandManager.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/CommandManager.coffee index 0a202a7793..2d928f9a82 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/CommandManager.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/CommandManager.coffee @@ -79,7 +79,7 @@ define [], () -> # hacky solution: limit iterations limit = null if window?._ide?.browserIsSafari - limit = 100 + limit = 5000 # fully formed commands realCommands = [] From a0e25386fcc5a0668b92dd6bc9bf46d7bbd8843f Mon Sep 17 00:00:00 2001 From: James Allen Date: Thu, 14 Sep 2017 17:00:44 +0200 Subject: [PATCH 026/206] Delete cleanup.js --- services/web/cleanup.js | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 services/web/cleanup.js diff --git a/services/web/cleanup.js b/services/web/cleanup.js deleted file mode 100644 index 9d6d88285c..0000000000 --- a/services/web/cleanup.js +++ /dev/null @@ -1,11 +0,0 @@ -var keys = require('./app/js/infrastructure/Keys'); -var settings = require('settings-sharelatex'); -var queueName = process.argv[2]; -var projectQueueName = process.argv[3]; -var queue = require('fairy').connect(settings.redis.web).queue(queueName); -console.log("cleaning up queue "+ queueName + " " + projectQueueName); -queue._requeue_group(projectQueueName); - -//fairy should kill the process but just in case -thirtySeconds = 30 * 1000 -setTimeout(process.exit, thirtySeconds) From f0092bc85a3b3b09ddb63270978c775bb6563c30 Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 15 Sep 2017 15:20:03 +0200 Subject: [PATCH 027/206] Push master branch to public repo after successful build Note that this has to happen outside of the docker container to work around issues with git and user ids, so we've had to modify the pipeline to explicitly run the steps inside docker as needed. --- services/web/Jenkinsfile | 58 +++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/services/web/Jenkinsfile b/services/web/Jenkinsfile index 8a844ca0a0..82dd2c049c 100644 --- a/services/web/Jenkinsfile +++ b/services/web/Jenkinsfile @@ -1,11 +1,6 @@ pipeline { - agent { - docker { - image 'node:6.9.5' - args "-v /var/lib/jenkins/.npm:/tmp/.npm" - } - } + agent any environment { HOME = "/tmp" @@ -18,6 +13,12 @@ pipeline { stages { stage('Set up') { + agent { + docker { + image 'node:6.9.5' + reuseNode true + } + } steps { // we need to disable logallrefupdates, else git clones during the npm install will require git to lookup the user id // which does not exist in the container's /etc/passwd file, causing the clone to fail. @@ -44,7 +45,15 @@ pipeline { } stage('Install') { + agent { + docker { + image 'node:6.9.5' + args "-v /var/lib/jenkins/.npm:/tmp/.npm" + reuseNode true + } + } steps { + sh 'git config --global core.logallrefupdates false' sh 'mv app/views/external/robots.txt public/robots.txt' sh 'mv app/views/external/googlebdb0f8f7f4a17241.html public/googlebdb0f8f7f4a17241.html' sh 'npm install' @@ -56,24 +65,48 @@ pipeline { } stage('Compile') { + agent { + docker { + image 'node:6.9.5' + reuseNode true + } + } steps { sh 'node_modules/.bin/grunt compile --verbose' } } stage('Smoke Test') { + agent { + docker { + image 'node:6.9.5' + reuseNode true + } + } steps { sh 'node_modules/.bin/grunt compile:smoke_tests' } } stage('Minify') { + agent { + docker { + image 'node:6.9.5' + reuseNode true + } + } steps { sh 'node_modules/.bin/grunt compile:minify' } } stage('Unit Test') { + agent { + docker { + image 'node:6.9.5' + reuseNode true + } + } steps { sh 'env NODE_ENV=development ./node_modules/.bin/grunt test:unit --reporter=tap' } @@ -87,6 +120,7 @@ pipeline { sh 'tar -czf build.tar.gz --exclude=build.tar.gz --exclude-vcs .' } } + stage('Publish') { steps { withAWS(credentials:'S3_CI_BUILDS_AWS_KEYS', region:"${S3_REGION_BUILD_ARTEFACTS}") { @@ -96,6 +130,18 @@ pipeline { } } } + + + stage('Sync OSS') { + when { + branch 'master' + } + steps { + sshagent (credentials: ['GIT_DEPLOY_KEY']) { + sh 'git push git@github.com:sharelatex/web-sharelatex.git HEAD:ja-oss-test' + } + } + } } post { From a0ac11eb0dc6dce84812fb961255a67da28bbbf8 Mon Sep 17 00:00:00 2001 From: James Allen Date: Mon, 18 Sep 2017 11:11:33 +0200 Subject: [PATCH 028/206] Actually push to master branch --- services/web/Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/Jenkinsfile b/services/web/Jenkinsfile index 82dd2c049c..8e3f2af972 100644 --- a/services/web/Jenkinsfile +++ b/services/web/Jenkinsfile @@ -138,7 +138,7 @@ pipeline { } steps { sshagent (credentials: ['GIT_DEPLOY_KEY']) { - sh 'git push git@github.com:sharelatex/web-sharelatex.git HEAD:ja-oss-test' + sh 'git push git@github.com:sharelatex/web-sharelatex.git HEAD:master' } } } From d0ce22b0f85ec1931c4454a866dd6dd7e0116dd0 Mon Sep 17 00:00:00 2001 From: James Allen Date: Mon, 18 Sep 2017 11:30:43 +0200 Subject: [PATCH 029/206] Add in overleaf-integration module to build --- services/web/Jenkinsfile | 1 + 1 file changed, 1 insertion(+) diff --git a/services/web/Jenkinsfile b/services/web/Jenkinsfile index 8e3f2af972..c43f82ac4e 100644 --- a/services/web/Jenkinsfile +++ b/services/web/Jenkinsfile @@ -41,6 +41,7 @@ pipeline { checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'modules/learn-wiki'], [$class: 'CloneOption', shallow: true]], userRemoteConfigs: [[credentialsId: 'GIT_DEPLOY_KEY', url: 'git@bitbucket.org:sharelatex/learn-wiki-web-module.git']]]) checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'modules/templates'], [$class: 'CloneOption', shallow: true]], userRemoteConfigs: [[credentialsId: 'GIT_DEPLOY_KEY', url: 'git@github.com:sharelatex/templates-webmodule.git']]]) checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'modules/track-changes'], [$class: 'CloneOption', shallow: true]], userRemoteConfigs: [[credentialsId: 'GIT_DEPLOY_KEY', url: 'git@github.com:sharelatex/track-changes-web-module.git']]]) + checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'modules/overleaf-integration'], [$class: 'CloneOption', shallow: true]], userRemoteConfigs: [[credentialsId: 'GIT_DEPLOY_KEY', url: 'git@github.com:sharelatex/overleaf-integration-web-module.git']]]) } } From 963a14b33cf1311a47c5e2aaa055053707415ef9 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Tue, 19 Sep 2017 14:25:30 +0100 Subject: [PATCH 030/206] Fix incorrectly named var and remove unneccessary semicolon --- .../web/public/coffee/ide/pdf/controllers/PdfController.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index 897983f47a..067a85f0d2 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -82,7 +82,7 @@ define [ timeSinceLastCompile = Date.now() - $scope.recompiledAt # If time is non-monotonic, assume that the user's system clock has been # changed and continue with recompile - isTimeNonMonotonic = timeSinceLastPoll < 0; + isTimeNonMonotonic = timeSinceLastCompile < 0 if isTimeNonMonotonic || timeSinceLastCompile >= AUTO_COMPILE_TIMEOUT if (!ide.$scope.hasLintingError) From 4007fc682b21ad9b3e11f29519e8ea70f46d5599 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Tue, 19 Sep 2017 15:55:00 +0100 Subject: [PATCH 031/206] Send JSON in autocompile setting event payload --- .../web/public/coffee/ide/pdf/controllers/PdfController.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index 68a6649ef7..ce3c8cbce1 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -107,7 +107,7 @@ define [ if newValue? and oldValue != newValue localStorage("autocompile_enabled:#{$scope.project_id}", newValue) toggleAutoCompile(newValue) - event_tracking.sendMB "autocompile-setting-changed", newValue + event_tracking.sendMB "autocompile-setting-changed", { value: newValue } if window.user?.betaProgram and $scope.autocompile_enabled toggleAutoCompile(true) From 24f44889a79b650d2ff683279098542f077cbd63 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 21 Sep 2017 13:25:19 +0100 Subject: [PATCH 032/206] First pass at decoding changes & comments in ranges --- .../public/coffee/ide/editor/Document.coffee | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/services/web/public/coffee/ide/editor/Document.coffee b/services/web/public/coffee/ide/editor/Document.coffee index 229e791c67..c22c6cd6b3 100644 --- a/services/web/public/coffee/ide/editor/Document.coffee +++ b/services/web/public/coffee/ide/editor/Document.coffee @@ -256,14 +256,36 @@ define [ return callback(error) if error? @joined = true @doc.catchUp( updates ) - @_catchUpRanges( ranges?.changes, ranges?.comments ) + + if ranges?.changes + changes = for change in ranges.changes + change.op.i = decodeURIComponent(escape(change.op.i)) if change.op.i + change.op.d = decodeURIComponent(escape(change.op.d)) if change.op.d + change + if ranges?.comments + comments = for comment in ranges.comments + comment.op.c = decodeURIComponent(escape(comment.op.c)) + comment + + @_catchUpRanges( changes, comments ) callback() else @ide.socket.emit 'joinDoc', @doc_id, (error, docLines, version, updates, ranges) => return callback(error) if error? @joined = true @doc = new ShareJsDoc @doc_id, docLines, version, @ide.socket - @ranges = new RangesTracker(ranges?.changes, ranges?.comments) + + if ranges?.changes + changes = for change in ranges.changes + change.op.i = decodeURIComponent(escape(change.op.i)) if change.op.i + change.op.d = decodeURIComponent(escape(change.op.d)) if change.op.d + change + if ranges?.comments + comments = for comment in ranges.comments + comment.op.c = decodeURIComponent(escape(comment.op.c)) + comment + + @ranges = new RangesTracker(changes, comments) @_bindToShareJsDocEvents() callback() From b33929fa8321129c00692f2a8fdecc31ad7b1d4c Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 21 Sep 2017 13:36:31 +0100 Subject: [PATCH 033/206] DRY up --- .../public/coffee/ide/editor/Document.coffee | 36 ++++++++----------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/services/web/public/coffee/ide/editor/Document.coffee b/services/web/public/coffee/ide/editor/Document.coffee index c22c6cd6b3..43e11a29db 100644 --- a/services/web/public/coffee/ide/editor/Document.coffee +++ b/services/web/public/coffee/ide/editor/Document.coffee @@ -256,17 +256,7 @@ define [ return callback(error) if error? @joined = true @doc.catchUp( updates ) - - if ranges?.changes - changes = for change in ranges.changes - change.op.i = decodeURIComponent(escape(change.op.i)) if change.op.i - change.op.d = decodeURIComponent(escape(change.op.d)) if change.op.d - change - if ranges?.comments - comments = for comment in ranges.comments - comment.op.c = decodeURIComponent(escape(comment.op.c)) - comment - + { changes, comments } = @_decodeRanges(ranges) @_catchUpRanges( changes, comments ) callback() else @@ -274,21 +264,23 @@ define [ return callback(error) if error? @joined = true @doc = new ShareJsDoc @doc_id, docLines, version, @ide.socket - - if ranges?.changes - changes = for change in ranges.changes - change.op.i = decodeURIComponent(escape(change.op.i)) if change.op.i - change.op.d = decodeURIComponent(escape(change.op.d)) if change.op.d - change - if ranges?.comments - comments = for comment in ranges.comments - comment.op.c = decodeURIComponent(escape(comment.op.c)) - comment - + { changes, comments } = @_decodeRanges(ranges) @ranges = new RangesTracker(changes, comments) @_bindToShareJsDocEvents() callback() + _decodeRanges: (ranges) -> + if ranges?.changes + changes = for change in ranges.changes + change.op.i = decodeURIComponent(escape(change.op.i)) if change.op.i + change.op.d = decodeURIComponent(escape(change.op.d)) if change.op.d + change + if ranges?.comments + comments = for comment in ranges.comments + comment.op.c = decodeURIComponent(escape(comment.op.c)) + comment + { changes, comments } + _leaveDoc: (callback = (error) ->) -> sl_console.log '[_leaveDoc] Sending leaveDoc request' @ide.socket.emit 'leaveDoc', @doc_id, (error) => From e2bb6dcecb1d06ff9aa6ce0f8a575f5f9a976a7b Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 21 Sep 2017 14:23:39 +0100 Subject: [PATCH 034/206] Pass option to encode range To ensure backwards compat with clients not refreshing, pass a flag to enable encoding. This way, old client won't receive encoded ranges, but also won't have decoding logic. The flag can then be removed once all clients are up to date --- services/web/public/coffee/ide/editor/Document.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/public/coffee/ide/editor/Document.coffee b/services/web/public/coffee/ide/editor/Document.coffee index 43e11a29db..082c977bfe 100644 --- a/services/web/public/coffee/ide/editor/Document.coffee +++ b/services/web/public/coffee/ide/editor/Document.coffee @@ -252,7 +252,7 @@ define [ _joinDoc: (callback = (error) ->) -> if @doc? - @ide.socket.emit 'joinDoc', @doc_id, @doc.getVersion(), (error, docLines, version, updates, ranges) => + @ide.socket.emit 'joinDoc', @doc_id, { encodeRanges: true }, @doc.getVersion(), (error, docLines, version, updates, ranges) => return callback(error) if error? @joined = true @doc.catchUp( updates ) @@ -260,7 +260,7 @@ define [ @_catchUpRanges( changes, comments ) callback() else - @ide.socket.emit 'joinDoc', @doc_id, (error, docLines, version, updates, ranges) => + @ide.socket.emit 'joinDoc', @doc_id, { encodeRanges: true }, (error, docLines, version, updates, ranges) => return callback(error) if error? @joined = true @doc = new ShareJsDoc @doc_id, docLines, version, @ide.socket From a022f83cce18187fc04f4d8b7052c56eb8f7aee3 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 21 Sep 2017 15:22:56 +0100 Subject: [PATCH 035/206] Fix order of args --- services/web/public/coffee/ide/editor/Document.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/public/coffee/ide/editor/Document.coffee b/services/web/public/coffee/ide/editor/Document.coffee index 082c977bfe..335e2e75ae 100644 --- a/services/web/public/coffee/ide/editor/Document.coffee +++ b/services/web/public/coffee/ide/editor/Document.coffee @@ -252,7 +252,7 @@ define [ _joinDoc: (callback = (error) ->) -> if @doc? - @ide.socket.emit 'joinDoc', @doc_id, { encodeRanges: true }, @doc.getVersion(), (error, docLines, version, updates, ranges) => + @ide.socket.emit 'joinDoc', @doc_id, @doc.getVersion(), { encodeRanges: true }, (error, docLines, version, updates, ranges) => return callback(error) if error? @joined = true @doc.catchUp( updates ) From 42b604dcda8ea0374e2bb74674b801e6a60b9505 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 21 Sep 2017 15:37:27 +0100 Subject: [PATCH 036/206] Clean up decoding from websockets --- .../public/coffee/ide/editor/Document.coffee | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/services/web/public/coffee/ide/editor/Document.coffee b/services/web/public/coffee/ide/editor/Document.coffee index 335e2e75ae..1773844638 100644 --- a/services/web/public/coffee/ide/editor/Document.coffee +++ b/services/web/public/coffee/ide/editor/Document.coffee @@ -256,30 +256,26 @@ define [ return callback(error) if error? @joined = true @doc.catchUp( updates ) - { changes, comments } = @_decodeRanges(ranges) - @_catchUpRanges( changes, comments ) + @_decodeRanges(ranges) + @_catchUpRanges(ranges?.changes, ranges?.comments) callback() else @ide.socket.emit 'joinDoc', @doc_id, { encodeRanges: true }, (error, docLines, version, updates, ranges) => return callback(error) if error? @joined = true @doc = new ShareJsDoc @doc_id, docLines, version, @ide.socket - { changes, comments } = @_decodeRanges(ranges) - @ranges = new RangesTracker(changes, comments) + @_decodeRanges(ranges) + @ranges = new RangesTracker(ranges?.changes, ranges?.comments) @_bindToShareJsDocEvents() callback() _decodeRanges: (ranges) -> - if ranges?.changes - changes = for change in ranges.changes - change.op.i = decodeURIComponent(escape(change.op.i)) if change.op.i - change.op.d = decodeURIComponent(escape(change.op.d)) if change.op.d - change - if ranges?.comments - comments = for comment in ranges.comments - comment.op.c = decodeURIComponent(escape(comment.op.c)) - comment - { changes, comments } + decodeFromWebsockets = (text) -> decodeURIComponent(escape(text)) + for change in ranges.changes or [] + change.op.i = decodeFromWebsockets(change.op.i) if change.op.i + change.op.d = decodeFromWebsockets(change.op.d) if change.op.d + for comment in ranges.comments or [] + comment.op.c = decodeFromWebsockets(comment.op.c) _leaveDoc: (callback = (error) ->) -> sl_console.log '[_leaveDoc] Sending leaveDoc request' From f8d49235475c5fad207eb2132d5c3f71dbcf62a4 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 21 Sep 2017 15:41:07 +0100 Subject: [PATCH 037/206] Wrap in try/catch --- .../web/public/coffee/ide/editor/Document.coffee | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/services/web/public/coffee/ide/editor/Document.coffee b/services/web/public/coffee/ide/editor/Document.coffee index 1773844638..887cd53a9a 100644 --- a/services/web/public/coffee/ide/editor/Document.coffee +++ b/services/web/public/coffee/ide/editor/Document.coffee @@ -271,11 +271,14 @@ define [ _decodeRanges: (ranges) -> decodeFromWebsockets = (text) -> decodeURIComponent(escape(text)) - for change in ranges.changes or [] - change.op.i = decodeFromWebsockets(change.op.i) if change.op.i - change.op.d = decodeFromWebsockets(change.op.d) if change.op.d - for comment in ranges.comments or [] - comment.op.c = decodeFromWebsockets(comment.op.c) + try + for change in ranges.changes or [] + change.op.i = decodeFromWebsockets(change.op.i) if change.op.i + change.op.d = decodeFromWebsockets(change.op.d) if change.op.d + for comment in ranges.comments or [] + comment.op.c = decodeFromWebsockets(comment.op.c) + catch err + console.log(err) _leaveDoc: (callback = (error) ->) -> sl_console.log '[_leaveDoc] Sending leaveDoc request' From 96d13215e390a37d9de6e36b5b9bd727d4b05075 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Fri, 22 Sep 2017 09:36:50 +0100 Subject: [PATCH 038/206] Be more defensive when checking encoded text --- services/web/public/coffee/ide/editor/Document.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/web/public/coffee/ide/editor/Document.coffee b/services/web/public/coffee/ide/editor/Document.coffee index 887cd53a9a..459f033ac6 100644 --- a/services/web/public/coffee/ide/editor/Document.coffee +++ b/services/web/public/coffee/ide/editor/Document.coffee @@ -273,10 +273,10 @@ define [ decodeFromWebsockets = (text) -> decodeURIComponent(escape(text)) try for change in ranges.changes or [] - change.op.i = decodeFromWebsockets(change.op.i) if change.op.i - change.op.d = decodeFromWebsockets(change.op.d) if change.op.d + change.op.i = decodeFromWebsockets(change.op.i) if change.op.i? + change.op.d = decodeFromWebsockets(change.op.d) if change.op.d? for comment in ranges.comments or [] - comment.op.c = decodeFromWebsockets(comment.op.c) + comment.op.c = decodeFromWebsockets(comment.op.c) if comment.op.c? catch err console.log(err) From e68b367f3f26bc03a38e94a389dc7e70e446ed73 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Mon, 25 Sep 2017 11:48:42 +0100 Subject: [PATCH 039/206] Common variables; refactor some Bootstrap hard-coded stuff into new variables. --- .../public/stylesheets/components/card.less | 12 ++--- .../stylesheets/core/_common-variables.less | 2 + .../public/stylesheets/core/ol-variables.less | 47 +++++++++++++++---- .../public/stylesheets/core/scaffolding.less | 2 +- 4 files changed, 48 insertions(+), 15 deletions(-) diff --git a/services/web/public/stylesheets/components/card.less b/services/web/public/stylesheets/components/card.less index 2619686b0a..f906d68daf 100644 --- a/services/web/public/stylesheets/components/card.less +++ b/services/web/public/stylesheets/components/card.less @@ -1,6 +1,6 @@ .card { background-color: white; - border-radius: @border-radius-base; + border-radius: @card-border-radius-base; -webkit-box-shadow: 0 2px 4px rgba(0,0,0,0.1); box-shadow: 0 2px 4px rgba(0,0,0,0.15); padding: @line-height-computed; @@ -39,16 +39,16 @@ z-index: 1; // Make it taller and card like margin-top: 0; - border-radius: @border-radius-base; + border-radius: @card-border-radius-base; } &.card-first { - border-top-left-radius: @border-radius-base; - border-bottom-left-radius: @border-radius-base; + border-top-left-radius: @card-border-radius-base; + border-bottom-left-radius: @card-border-radius-base; } &.card-last { - border-top-right-radius: @border-radius-base; - border-bottom-right-radius: @border-radius-base; + border-top-right-radius: @card-border-radius-base; + border-bottom-right-radius: @card-border-radius-base; } } } \ No newline at end of file diff --git a/services/web/public/stylesheets/core/_common-variables.less b/services/web/public/stylesheets/core/_common-variables.less index 6274b265f9..0481b4ae5e 100644 --- a/services/web/public/stylesheets/core/_common-variables.less +++ b/services/web/public/stylesheets/core/_common-variables.less @@ -86,6 +86,7 @@ @border-radius-large: 5px; @border-radius-small: 2px; +@card-border-radius-base: @border-radius-base; //** Global color for active items (e.g., navs or dropdowns). @component-active-color: #fff; //** Global background color for active items (e.g., navs or dropdowns). @@ -793,6 +794,7 @@ @content-margin-top: @line-height-computed; // Custom +@content-alt-bg-color: lighten(@gray-lightest, 2.5%); @left-menu-width: 260px; @left-menu-animation-duration: 0.35s; diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index 23151bd9c4..45c18b5d65 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -1,11 +1,45 @@ -@ol-green: #4A9F48; -@ol-dark-green: #1C5B26; +@import "./_common-variables.less"; + +// Styleguide colors +@ol-blue-gray-1 : #E4E8EE; +@ol-blue-gray-2 : #9DA7B7; +@ol-blue-gray-3 : #5D6879; +@ol-blue-gray-4 : #485973; +@ol-blue-gray-5 : #2C3645; +@ol-blue-gray-6 : #1E2530; + +@ol-green : #4F9C45; +@ol-dark-green : #1C5B26; +@ol-blue : #4B7FD1; +@ol-dark-blue : #2857A1; +@ol-red : #C9453E; +@ol-dark-red : #A6312B; + +@ol-type-color : @ol-blue-gray-3; + +// Navbar customization +@navbar-default-color : #FFF; +@navbar-default-bg : @ol-blue-gray-6; +@navbar-default-border : transparent; +@navbar-brand-image-url : url(/img/ol-brand/logo-horizontal.png); + +// Backgrounds +@body-bg : @ol-blue-gray-1; +@content-alt-bg-color : @ol-blue-gray-1; + +// Typography +@text-color : @ol-type-color; + +// Border radius (used for components, e.g. buttons) +@border-radius-base : 999px; // Large enough value to be fully rounded everywhere +@border-radius-large : 999px; // As above +@border-radius-small : 999px; // As above + +@card-border-radius-base: 3px; //== Colors // //## Gray and brand colors for use across Bootstrap. - - @gray-darker: #252525; @gray-dark: #505050; @gray: #7a7a7a; @@ -28,10 +62,7 @@ @brand-warning: @orange; @brand-danger: #E03A06; -@navbar-brand-image-url: url(/img/ol-brand/logo-horizontal.png); @editor-loading-logo-padding-top: 115.44%; @editor-loading-logo-background-url: url(/img/ol-brand/overleaf-o-grey.svg); -@editor-loading-logo-foreground-url: url(/img/ol-brand/overleaf-o.svg); - -@import "./_common-variables.less"; +@editor-loading-logo-foreground-url: url(/img/ol-brand/overleaf-o.svg); \ No newline at end of file diff --git a/services/web/public/stylesheets/core/scaffolding.less b/services/web/public/stylesheets/core/scaffolding.less index b6dc68363d..0d433739db 100755 --- a/services/web/public/stylesheets/core/scaffolding.less +++ b/services/web/public/stylesheets/core/scaffolding.less @@ -142,7 +142,7 @@ hr { } .content-alt { - background-color: lighten(@gray-lightest, 2.5%); + background-color: @content-alt-bg-color; } .row-spaced { From 66654e4a1471d31d39a951ddbcc256f8628d56a8 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Mon, 25 Sep 2017 15:40:44 +0100 Subject: [PATCH 040/206] Customize buttons; make button borders configurable. --- services/web/app/views/project/list.pug | 101 +++++++++--------- .../stylesheets/components/buttons.less | 4 +- .../stylesheets/core/_common-variables.less | 3 + .../public/stylesheets/core/ol-variables.less | 43 ++++++++ 4 files changed, 98 insertions(+), 53 deletions(-) diff --git a/services/web/app/views/project/list.pug b/services/web/app/views/project/list.pug index ddf80a0b5f..06efb1b11d 100644 --- a/services/web/app/views/project/list.pug +++ b/services/web/app/views/project/list.pug @@ -15,59 +15,58 @@ block content } }; - .content.content-alt.project-list-page(ng-controller="ProjectPageController") - .container - .announcements( - ng-controller="AnnouncementsController" - ng-class="{ 'announcements-open': ui.isOpen }" - ng-cloak + .container-fluid.content.content-alt.project-list-page(ng-controller="ProjectPageController") + .announcements( + ng-controller="AnnouncementsController" + ng-class="{ 'announcements-open': ui.isOpen }" + ng-cloak + ) + .announcements-backdrop( + ng-if="ui.isOpen" + ng-click="toggleAnnouncementsUI();" ) - .announcements-backdrop( - ng-if="ui.isOpen" - ng-click="toggleAnnouncementsUI();" - ) - a.announcements-btn( - href - ng-if="announcements.length" - ng-click="toggleAnnouncementsUI();" - ng-class="{ 'announcements-btn-open': ui.isOpen, 'announcements-btn-has-new': ui.newItems }" - ) - span.announcements-badge(ng-if="ui.newItems") {{ ui.newItems }} - .announcements-body( - ng-if="ui.isOpen" - ) - .announcements-scroller - .announcement( - ng-repeat="announcement in announcements | filter:(ui.newItems ? { read: false } : '') track by announcement.id" - ) - h2.announcement-header {{ announcement.title }} - p.announcement-description(ng-bind-html="announcement.excerpt") - .announcement-meta - p.announcement-date {{ announcement.date | date:"longDate" }} - a.announcement-link( - ng-href="{{ announcement.url }}" - ng-click="logAnnouncementClick()", - target="_blank" - ) Read more - div.text-center( - ng-if="ui.newItems > 0 && ui.newItems < announcements.length" - ) - a.btn.btn-default.btn-sm( - href - ng-click="showAll();" - ) Show all + a.announcements-btn( + href + ng-if="announcements.length" + ng-click="toggleAnnouncementsUI();" + ng-class="{ 'announcements-btn-open': ui.isOpen, 'announcements-btn-has-new': ui.newItems }" + ) + span.announcements-badge(ng-if="ui.newItems") {{ ui.newItems }} + .announcements-body( + ng-if="ui.isOpen" + ) + .announcements-scroller + .announcement( + ng-repeat="announcement in announcements | filter:(ui.newItems ? { read: false } : '') track by announcement.id" + ) + h2.announcement-header {{ announcement.title }} + p.announcement-description(ng-bind-html="announcement.excerpt") + .announcement-meta + p.announcement-date {{ announcement.date | date:"longDate" }} + a.announcement-link( + ng-href="{{ announcement.url }}" + ng-click="logAnnouncementClick()", + target="_blank" + ) Read more + div.text-center( + ng-if="ui.newItems > 0 && ui.newItems < announcements.length" + ) + a.btn.btn-default.btn-sm( + href + ng-click="showAll();" + ) Show all - .row(ng-cloak) - span(ng-if="projects.length > 0") - aside.col-md-2.col-xs-3 - include ./list/side-bar + .row(ng-cloak) + span(ng-if="projects.length > 0") + aside.col-md-2.col-xs-3 + include ./list/side-bar - .col-md-10.col-xs-9 - include ./list/notifications - include ./list/project-list - - span(ng-if="projects.length === 0") - .col-md-offset-2.col-md-8.col-md-offset-2.col-xs-8 - include ./list/empty-project-list + .col-md-10.col-xs-9 + include ./list/notifications + include ./list/project-list + + span(ng-if="projects.length === 0") + .col-md-offset-2.col-md-8.col-md-offset-2.col-xs-8 + include ./list/empty-project-list include ./list/modals diff --git a/services/web/public/stylesheets/components/buttons.less b/services/web/public/stylesheets/components/buttons.less index 6e77fd2b7f..277bd2240b 100755 --- a/services/web/public/stylesheets/components/buttons.less +++ b/services/web/public/stylesheets/components/buttons.less @@ -14,8 +14,8 @@ vertical-align: middle; cursor: pointer; background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 - border: 1px solid transparent; - border-bottom: 2px solid transparent; + border: @btn-border-width solid transparent; + border-bottom: @btn-border-bottom-width solid transparent; white-space: nowrap; .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @border-radius-base); .user-select(none); diff --git a/services/web/public/stylesheets/core/_common-variables.less b/services/web/public/stylesheets/core/_common-variables.less index 0481b4ae5e..4bf27cf58d 100644 --- a/services/web/public/stylesheets/core/_common-variables.less +++ b/services/web/public/stylesheets/core/_common-variables.less @@ -796,6 +796,9 @@ // Custom @content-alt-bg-color: lighten(@gray-lightest, 2.5%); +@btn-border-width: 1px; +@btn-border-bottom-width: 2px; + @left-menu-width: 260px; @left-menu-animation-duration: 0.35s; diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index 45c18b5d65..0ff08f73a8 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -37,6 +37,49 @@ @card-border-radius-base: 3px; +// Button colors and sizing +@btn-border-width : 0; +@btn-border-bottom-width : 0; + +@btn-default-color : #FFF; +@btn-default-bg : @ol-blue-gray-4; +@btn-default-border : transparent; + +@btn-primary-color : #FFF; +@btn-primary-bg : @ol-green; +@btn-primary-border : transparent; + +@btn-success-color : #FFF; +@btn-success-bg : @ol-green; +@btn-success-border : transparent; + +@btn-info-color : #FFF; +@btn-info-bg : @ol-blue; +@btn-info-border : transparent; + +// TODO Warning color-orange? +@btn-warning-color : #FFF; +@btn-warning-bg : @ol-red; +@btn-warning-border : transparent; + +@btn-danger-color : #FFF; +@btn-danger-bg : @ol-red; +@btn-danger-border : transparent; + + + +.btn-info { + .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border); +} +// Warning appears as orange +.btn-warning { + .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border); +} +// Danger and error appear as red +.btn-danger { + .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border); +} + //== Colors // //## Gray and brand colors for use across Bootstrap. From e31464b47d19f78f26694b2aa908238dfedfcd79 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Mon, 25 Sep 2017 15:46:29 +0100 Subject: [PATCH 041/206] Keep track of pdf layout hidden state --- services/web/public/coffee/ide.coffee | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/services/web/public/coffee/ide.coffee b/services/web/public/coffee/ide.coffee index d37334649c..e317b355ef 100644 --- a/services/web/public/coffee/ide.coffee +++ b/services/web/public/coffee/ide.coffee @@ -71,11 +71,12 @@ define [ view: "editor" chatOpen: false pdfLayout: 'sideBySide' + pdfHidden: false, reviewPanelOpen: localStorage("ui.reviewPanelOpen.#{window.project_id}") miniReviewPanelVisible: false } $scope.user = window.user - + $scope.$watch "project.features.trackChangesVisible", (visible) -> return if !visible? $scope.ui.showCollabFeaturesOnboarding = window.showTrackChangesOnboarding and visible @@ -99,6 +100,9 @@ define [ if value? localStorage "ui.reviewPanelOpen.#{window.project_id}", value + $scope.$on "layout:pdf:resize", (_, layoutState) -> + $scope.ui.pdfHidden = layoutState.east.initClosed + # Tracking code. $scope.$watch "ui.view", (newView, oldView) -> if newView? and newView != "editor" and newView != "pdf" From 5b3c9f4033cbde25a708a30e59578c1e41bf050f Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Mon, 25 Sep 2017 15:46:43 +0100 Subject: [PATCH 042/206] Prevent autocompile if pdf preview is hidden --- .../web/public/coffee/ide/pdf/controllers/PdfController.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index ce3c8cbce1..93166d4388 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -77,7 +77,7 @@ define [ autoCompileTimeout = null triggerAutoCompile = () -> - return if autoCompileTimeout + return if autoCompileTimeout or $scope.ui.pdfHidden timeSinceLastCompile = Date.now() - $scope.recompiledAt # If time is non-monotonic, assume that the user's system clock has been From 08e0865870a1f5deb498be591f88b3ec81d3762c Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Mon, 25 Sep 2017 16:41:09 +0100 Subject: [PATCH 043/206] Customize dashboard sidebar. --- services/web/app/views/project/list.pug | 4 +-- .../web/app/views/project/list/side-bar.pug | 2 +- .../public/stylesheets/app/project-list.less | 29 +++++++++++++++---- .../stylesheets/core/_common-variables.less | 7 +++++ .../public/stylesheets/core/ol-variables.less | 21 +++++--------- 5 files changed, 40 insertions(+), 23 deletions(-) diff --git a/services/web/app/views/project/list.pug b/services/web/app/views/project/list.pug index 06efb1b11d..dead200279 100644 --- a/services/web/app/views/project/list.pug +++ b/services/web/app/views/project/list.pug @@ -58,10 +58,10 @@ block content .row(ng-cloak) span(ng-if="projects.length > 0") - aside.col-md-2.col-xs-3 + aside.project-list-sidebar.col-md-2.col-xs-3 include ./list/side-bar - .col-md-10.col-xs-9 + .project-list-main.col-md-10.col-xs-9 include ./list/notifications include ./list/project-list diff --git a/services/web/app/views/project/list/side-bar.pug b/services/web/app/views/project/list/side-bar.pug index 5429045afb..93eeb46125 100644 --- a/services/web/app/views/project/list/side-bar.pug +++ b/services/web/app/views/project/list/side-bar.pug @@ -1,5 +1,5 @@ .dropdown(dropdown) - a.btn.btn-primary.dropdown-toggle( + a.btn.btn-primary.btn-block.dropdown-toggle( href="#", data-toggle="dropdown", dropdown-toggle diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less index 13ca60e181..aabf35b899 100644 --- a/services/web/public/stylesheets/app/project-list.less +++ b/services/web/public/stylesheets/app/project-list.less @@ -19,8 +19,21 @@ .project-list-page { position: relative; + padding-top: 0; + padding-bottom: 0; } + .project-list-sidebar { + background-color: @sidebar-bg; + padding-top: @content-margin-top; + padding-bottom: @content-margin-top; + } + + .project-list-main { + padding-top: @content-margin-top; + padding-bottom: @content-margin-top; + } + .project-header { .btn-group > .btn { padding-left: @line-height-base / 2; @@ -76,7 +89,7 @@ } ul.folders-menu { - margin: 0; + margin: 0 -(@grid-gutter-width / 2); .subdued { color: @gray-light; } @@ -85,18 +98,22 @@ ul.folders-menu { line-height: 1.8; position: relative; > a { + display: block; font-size: 0.9rem; - color: #333; - padding: (@line-height-computed / 4); - display: inline-block; + color: @sidebar-link-color; + padding: (@line-height-computed / 4) (@grid-gutter-width / 2); line-height: 1.2; + &:hover { + background-color: @sidebar-hover-bg; + text-decoration: @sidebar-hover-text-decoration; + } } } > li.active { //border-right: 4px solid @red; - background-color: @link-color; - border-radius: @border-radius-small; + border-radius: @sidebar-active-border-radius; > a { + background-color: @sidebar-active-bg; font-weight: 700; color: white; .subdued { diff --git a/services/web/public/stylesheets/core/_common-variables.less b/services/web/public/stylesheets/core/_common-variables.less index 4bf27cf58d..80ce79e76e 100644 --- a/services/web/public/stylesheets/core/_common-variables.less +++ b/services/web/public/stylesheets/core/_common-variables.less @@ -796,6 +796,13 @@ // Custom @content-alt-bg-color: lighten(@gray-lightest, 2.5%); +@sidebar-bg: transparent; +@sidebar-link-color: #333; +@sidebar-active-border-radius: @border-radius-small; +@sidebar-active-bg: @link-color; +@sidebar-hover-bg: transparent; +@sidebar-hover-text-decoration: underline; + @btn-border-width: 1px; @btn-border-bottom-width: 2px; diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index 0ff08f73a8..0090713b09 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -66,20 +66,13 @@ @btn-danger-bg : @ol-red; @btn-danger-border : transparent; - - -.btn-info { - .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border); -} -// Warning appears as orange -.btn-warning { - .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border); -} -// Danger and error appear as red -.btn-danger { - .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border); -} - +// Sidebar +@sidebar-bg : @ol-blue-gray-5; +@sidebar-link-color : #FFF; +@sidebar-active-border-radius : 0; +@sidebar-active-bg : @ol-blue-gray-6; +@sidebar-hover-bg : @ol-blue-gray-4; +@sidebar-hover-text-decoration : none; //== Colors // //## Gray and brand colors for use across Bootstrap. From 465de8701b28d09913a8b5795373c9121cf84583 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Mon, 25 Sep 2017 16:56:22 +0100 Subject: [PATCH 044/206] Use a different strategy to achieve rounded buttons (less impact on other elements). --- .../web/public/stylesheets/components/buttons.less | 8 ++++---- services/web/public/stylesheets/components/card.less | 12 ++++++------ .../public/stylesheets/core/_common-variables.less | 7 +++++-- .../web/public/stylesheets/core/ol-variables.less | 11 ++++------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/services/web/public/stylesheets/components/buttons.less b/services/web/public/stylesheets/components/buttons.less index 277bd2240b..57a29c5353 100755 --- a/services/web/public/stylesheets/components/buttons.less +++ b/services/web/public/stylesheets/components/buttons.less @@ -17,7 +17,7 @@ border: @btn-border-width solid transparent; border-bottom: @btn-border-bottom-width solid transparent; white-space: nowrap; - .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @border-radius-base); + .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @btn-border-radius-base); .user-select(none); &, @@ -124,14 +124,14 @@ .btn-lg { // line-height: ensure even-numbered height of button next to large input - .button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large); + .button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @btn-border-radius-large); } .btn-sm { // line-height: ensure proper height of button next to small input - .button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small); + .button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @btn-border-radius-small); } .btn-xs { - .button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @border-radius-small); + .button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @btn-border-radius-small); } diff --git a/services/web/public/stylesheets/components/card.less b/services/web/public/stylesheets/components/card.less index f906d68daf..2619686b0a 100644 --- a/services/web/public/stylesheets/components/card.less +++ b/services/web/public/stylesheets/components/card.less @@ -1,6 +1,6 @@ .card { background-color: white; - border-radius: @card-border-radius-base; + border-radius: @border-radius-base; -webkit-box-shadow: 0 2px 4px rgba(0,0,0,0.1); box-shadow: 0 2px 4px rgba(0,0,0,0.15); padding: @line-height-computed; @@ -39,16 +39,16 @@ z-index: 1; // Make it taller and card like margin-top: 0; - border-radius: @card-border-radius-base; + border-radius: @border-radius-base; } &.card-first { - border-top-left-radius: @card-border-radius-base; - border-bottom-left-radius: @card-border-radius-base; + border-top-left-radius: @border-radius-base; + border-bottom-left-radius: @border-radius-base; } &.card-last { - border-top-right-radius: @card-border-radius-base; - border-bottom-right-radius: @card-border-radius-base; + border-top-right-radius: @border-radius-base; + border-bottom-right-radius: @border-radius-base; } } } \ No newline at end of file diff --git a/services/web/public/stylesheets/core/_common-variables.less b/services/web/public/stylesheets/core/_common-variables.less index 80ce79e76e..0a2337706e 100644 --- a/services/web/public/stylesheets/core/_common-variables.less +++ b/services/web/public/stylesheets/core/_common-variables.less @@ -85,8 +85,6 @@ @border-radius-base: 3px; @border-radius-large: 5px; @border-radius-small: 2px; - -@card-border-radius-base: @border-radius-base; //** Global color for active items (e.g., navs or dropdowns). @component-active-color: #fff; //** Global background color for active items (e.g., navs or dropdowns). @@ -796,6 +794,11 @@ // Custom @content-alt-bg-color: lighten(@gray-lightest, 2.5%); +@btn-border-radius-large: @border-radius-large; +@btn-border-radius-base: @border-radius-base; +@btn-border-radius-small: @border-radius-small; + + @sidebar-bg: transparent; @sidebar-link-color: #333; @sidebar-active-border-radius: @border-radius-small; diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index 0090713b09..7de6fde466 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -30,17 +30,14 @@ // Typography @text-color : @ol-type-color; -// Border radius (used for components, e.g. buttons) -@border-radius-base : 999px; // Large enough value to be fully rounded everywhere -@border-radius-large : 999px; // As above -@border-radius-small : 999px; // As above - -@card-border-radius-base: 3px; - // Button colors and sizing @btn-border-width : 0; @btn-border-bottom-width : 0; +@btn-border-radius-large : 9999px; +@btn-border-radius-base : 9999px; +@btn-border-radius-small : 9999px; + @btn-default-color : #FFF; @btn-default-bg : @ol-blue-gray-4; @btn-default-border : transparent; From f6e236afa67b7a3e5fd2f8c37c4fac56221021e7 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Mon, 25 Sep 2017 17:12:31 +0100 Subject: [PATCH 045/206] Link typography. --- .../web/public/stylesheets/app/project-list.less | 2 +- .../web/public/stylesheets/components/card.less | 4 ++-- .../stylesheets/core/_common-variables.less | 3 +++ .../public/stylesheets/core/ol-variables.less | 16 +++++++++++++--- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less index aabf35b899..39d7cf9069 100644 --- a/services/web/public/stylesheets/app/project-list.less +++ b/services/web/public/stylesheets/app/project-list.less @@ -239,7 +239,7 @@ ul.structured-list { background-color: white; } a { - color: darken(@blue, 10%); + color: @structured-list-link-color; } .header { text-transform: uppercase; diff --git a/services/web/public/stylesheets/components/card.less b/services/web/public/stylesheets/components/card.less index 2619686b0a..1e06fbe3b4 100644 --- a/services/web/public/stylesheets/components/card.less +++ b/services/web/public/stylesheets/components/card.less @@ -1,8 +1,8 @@ .card { background-color: white; border-radius: @border-radius-base; - -webkit-box-shadow: 0 2px 4px rgba(0,0,0,0.1); - box-shadow: 0 2px 4px rgba(0,0,0,0.15); + -webkit-box-shadow: @card-box-shadow; + box-shadow: @card-box-shadow; padding: @line-height-computed; .page-header { margin: 0 0 1.5625rem; diff --git a/services/web/public/stylesheets/core/_common-variables.less b/services/web/public/stylesheets/core/_common-variables.less index 0a2337706e..49ad90b399 100644 --- a/services/web/public/stylesheets/core/_common-variables.less +++ b/services/web/public/stylesheets/core/_common-variables.less @@ -798,6 +798,9 @@ @btn-border-radius-base: @border-radius-base; @btn-border-radius-small: @border-radius-small; +@card-box-shadow: 0 2px 4px rgba(0,0,0,0.15); + +@structured-list-link-color: darken(@blue, 10%); @sidebar-bg: transparent; @sidebar-link-color: #333; diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index 7de6fde466..3befa66283 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -29,6 +29,8 @@ // Typography @text-color : @ol-type-color; +@link-color : @ol-blue; +@link-hover-color : @ol-dark-blue; // Button colors and sizing @btn-border-width : 0; @@ -59,9 +61,12 @@ @btn-warning-bg : @ol-red; @btn-warning-border : transparent; -@btn-danger-color : #FFF; -@btn-danger-bg : @ol-red; -@btn-danger-border : transparent; +@btn-danger-color : #FFF; +@btn-danger-bg : @ol-red; +@btn-danger-border : transparent; + +// Cards +@card-box-shadow : none; // Sidebar @sidebar-bg : @ol-blue-gray-5; @@ -70,6 +75,11 @@ @sidebar-active-bg : @ol-blue-gray-6; @sidebar-hover-bg : @ol-blue-gray-4; @sidebar-hover-text-decoration : none; + + +// Project table +@structured-list-link-color : @ol-blue; + //== Colors // //## Gray and brand colors for use across Bootstrap. From 0f855689a7c6c4b7d5f1a8a0aeefecfb41ba2c1c Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 26 Sep 2017 08:07:35 +0100 Subject: [PATCH 046/206] show error for compile in progress --- services/web/app/coffee/Features/Compile/ClsiManager.coffee | 2 ++ services/web/app/views/project/editor/pdf.pug | 4 ++++ .../public/coffee/ide/pdf/controllers/PdfController.coffee | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/services/web/app/coffee/Features/Compile/ClsiManager.coffee b/services/web/app/coffee/Features/Compile/ClsiManager.coffee index 68b13a8f68..46cb4e35e3 100755 --- a/services/web/app/coffee/Features/Compile/ClsiManager.coffee +++ b/services/web/app/coffee/Features/Compile/ClsiManager.coffee @@ -107,6 +107,8 @@ module.exports = ClsiManager = callback null, compile:status:"project-too-large" else if response.statusCode == 409 callback null, compile:status:"conflict" + else if response.statusCode == 423 + callback null, compile:status:"compile-in-progress" else error = new Error("CLSI returned non-success code: #{response.statusCode}") logger.error err: error, project_id: project_id, "CLSI returned failure code" diff --git a/services/web/app/views/project/editor/pdf.pug b/services/web/app/views/project/editor/pdf.pug index db4a0e9dd3..065dd0cec0 100644 --- a/services/web/app/views/project/editor/pdf.pug +++ b/services/web/app/views/project/editor/pdf.pug @@ -344,6 +344,10 @@ div.full-size.pdf(ng-controller="PdfController") strong #{translate("pdf_compile_rate_limit_hit")} span #{translate("project_flagged_too_many_compiles")} + .alert.alert-danger(ng-show="pdf.compileInProgress") + strong #{translate("pdf_compile_in_progress_error")} + span #{translate("pdf_compile_try_again")} + .alert.alert-danger(ng-show="pdf.timedout") p strong #{translate("timedout")}. diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index ce3c8cbce1..ed69ee63f7 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -167,6 +167,7 @@ define [ $scope.pdf.compileTerminated = false $scope.pdf.compileExited = false $scope.pdf.failedCheck = false + $scope.pdf.compileInProgress = false # make a cache to look up files by name fileByPath = {} @@ -223,6 +224,9 @@ define [ else if response.status == "validation-problems" $scope.pdf.view = "validation-problems" $scope.pdf.validation = response.validationProblems + else if response.status == "compile-in-progress" + $scope.pdf.view = 'errors' + $scope.pdf.compileInProgress = true else if response.status == "success" $scope.pdf.view = 'pdf' $scope.shouldShowLogs = false From 2c0e9bb89af261f0ae0e49027079a86ce0aefcb9 Mon Sep 17 00:00:00 2001 From: Hayden Faulds Date: Tue, 26 Sep 2017 10:19:30 +0100 Subject: [PATCH 047/206] return overleaf details from ProejctsDetailsHandler.getdetails --- .../Project/ProjectDetailsHandler.coffee | 10 +++++++--- .../Project/ProjectDetailsHandlerTests.coffee | 17 ++++++++++++----- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/services/web/app/coffee/Features/Project/ProjectDetailsHandler.coffee b/services/web/app/coffee/Features/Project/ProjectDetailsHandler.coffee index 8234907ff4..5819e03472 100644 --- a/services/web/app/coffee/Features/Project/ProjectDetailsHandler.coffee +++ b/services/web/app/coffee/Features/Project/ProjectDetailsHandler.coffee @@ -9,7 +9,7 @@ Errors = require("../Errors/Errors") module.exports = ProjectDetailsHandler = getDetails: (project_id, callback)-> - ProjectGetter.getProject project_id, {name:true, description:true, compiler:true, features:true, owner_ref:true}, (err, project)-> + ProjectGetter.getProject project_id, {name:true, description:true, compiler:true, features:true, owner_ref:true, overleaf:true}, (err, project)-> if err? logger.err err:err, project_id:project_id, "error getting project" return callback(err) @@ -21,7 +21,11 @@ module.exports = ProjectDetailsHandler = description: project.description compiler: project.compiler features: user.features - logger.log project_id:project_id, details:details, "getting project details" + + if project.overleaf? + details.overleaf = project.overleaf + + logger.log project_id:project_id, details: details, "getting project details" callback(err, details) getProjectDescription: (project_id, callback)-> @@ -66,4 +70,4 @@ module.exports = ProjectDetailsHandler = logger.log project_id: project_id, level: newAccessLevel, "set public access level" if project_id? && newAccessLevel? and _.include [PublicAccessLevels.READ_ONLY, PublicAccessLevels.READ_AND_WRITE, PublicAccessLevels.PRIVATE], newAccessLevel Project.update {_id:project_id},{publicAccesLevel:newAccessLevel}, (err)-> - callback() \ No newline at end of file + callback() diff --git a/services/web/test/UnitTests/coffee/Project/ProjectDetailsHandlerTests.coffee b/services/web/test/UnitTests/coffee/Project/ProjectDetailsHandlerTests.coffee index c4c3b3ef07..abb0b505d0 100644 --- a/services/web/test/UnitTests/coffee/Project/ProjectDetailsHandlerTests.coffee +++ b/services/web/test/UnitTests/coffee/Project/ProjectDetailsHandlerTests.coffee @@ -12,7 +12,7 @@ describe 'ProjectDetailsHandler', -> beforeEach -> @project_id = "321l3j1kjkjl" @user_id = "user-id-123" - @project = + @project = name: "project" description: "this is a great project" something:"should not exist" @@ -20,7 +20,7 @@ describe 'ProjectDetailsHandler', -> owner_ref: @user_id @user = features: "mock-features" - @ProjectGetter = + @ProjectGetter = getProjectWithoutDocLines: sinon.stub().callsArgWith(1, null, @project) getProject: sinon.stub().callsArgWith(2, null, @project) @ProjectModel = @@ -42,7 +42,7 @@ describe 'ProjectDetailsHandler', -> describe "getDetails", -> it "should find the project and owner", (done)-> - @handler.getDetails @project_id, (err, details)=> + @handler.getDetails @project_id, (err, details)=> details.name.should.equal @project.name details.description.should.equal @project.description details.compiler.should.equal @project.compiler @@ -50,6 +50,13 @@ describe 'ProjectDetailsHandler', -> assert.equal(details.something, undefined) done() + it "should find overleaf metadata if it exists", (done)-> + @project.overleaf = { id: 'id' } + @handler.getDetails @project_id, (err, details)=> + details.overleaf.should.equal @project.overleaf + assert.equal(details.something, undefined) + done() + it "should return an error for a non-existent project", (done)-> @ProjectGetter.getProject.callsArg(2, null, null) err = new Errors.NotFoundError("project not found") @@ -79,7 +86,7 @@ describe 'ProjectDetailsHandler', -> @handler.getProjectDescription @project_id, (returnedErr, returnedDescription)=> err.should.equal returnedErr description.should.equal returnedDescription - done() + done() describe "setProjectDescription", -> @@ -110,7 +117,7 @@ describe 'ProjectDetailsHandler', -> @handler.renameProject @project_id, @newName, => @tpdsUpdateSender.moveEntity.calledWith({project_id:@project_id, project_name:@project.name, newProjectName:@newName}).should.equal true done() - + it "should not do anything with an invalid name", (done) -> @handler.validateProjectName = sinon.stub().yields(new Error("invalid name")) @handler.renameProject @project_id, @newName, => From 4adf88ca01eb7988c676814ab5c6494a49677e34 Mon Sep 17 00:00:00 2001 From: Hayden Faulds Date: Tue, 26 Sep 2017 10:19:49 +0100 Subject: [PATCH 048/206] handle OL or SL ids in UserInfoController.getPersonalInfo --- .../Features/User/UserInfoController.coffee | 14 ++++- .../User/UserInfoControllerTests.coffee | 59 +++++++++++++++---- 2 files changed, 59 insertions(+), 14 deletions(-) diff --git a/services/web/app/coffee/Features/User/UserInfoController.coffee b/services/web/app/coffee/Features/User/UserInfoController.coffee index 8054f48afe..0de5501e3c 100644 --- a/services/web/app/coffee/Features/User/UserInfoController.coffee +++ b/services/web/app/coffee/Features/User/UserInfoController.coffee @@ -4,6 +4,7 @@ UserDeleter = require("./UserDeleter") UserUpdater = require("./UserUpdater") sanitize = require('sanitizer') AuthenticationController = require('../Authentication/AuthenticationController') +ObjectId = require("mongojs").ObjectId module.exports = UserController = getLoggedInUsersPersonalInfo: (req, res, next = (error) ->) -> @@ -19,8 +20,17 @@ module.exports = UserController = UserController.sendFormattedPersonalInfo(user, res, next) getPersonalInfo: (req, res, next = (error) ->) -> - UserGetter.getUser req.params.user_id, { _id: true, first_name: true, last_name: true, email: true}, (error, user) -> - logger.log user_id: req.params.user_id, "reciving request for getting users personal info" + {user_id} = req.params + + if user_id.match(/^\d+$/) + query = { "overleaf.id": parseInt(user_id, 10) } + else if user_id.match(/^[a-f0-9]{24}$/) + query = { _id: ObjectId(user_id) } + else + return res.send(400) + + UserGetter.getUser query, { _id: true, first_name: true, last_name: true, email: true}, (error, user) -> + logger.log user_id: req.params.user_id, "receiving request for getting users personal info" return next(error) if error? return res.send(404) if !user? UserController.sendFormattedPersonalInfo(user, res, next) diff --git a/services/web/test/UnitTests/coffee/User/UserInfoControllerTests.coffee b/services/web/test/UnitTests/coffee/User/UserInfoControllerTests.coffee index 37a1c034f0..7f1c0917d6 100644 --- a/services/web/test/UnitTests/coffee/User/UserInfoControllerTests.coffee +++ b/services/web/test/UnitTests/coffee/User/UserInfoControllerTests.coffee @@ -12,9 +12,9 @@ ObjectId = require("mongojs").ObjectId describe "UserInfoController", -> beforeEach -> - @UserDeleter = + @UserDeleter = deleteUser: sinon.stub().callsArgWith(1) - @UserUpdater = + @UserUpdater = updatePersonalInfo: sinon.stub() @sanitizer = escape:(v)->v sinon.spy @sanitizer, "escape" @@ -50,23 +50,47 @@ describe "UserInfoController", -> .should.equal true describe "getPersonalInfo", -> - beforeEach -> - @user_id = ObjectId().toString() - @user = - _id: ObjectId(@user_id) - @req.params = user_id: @user_id - - describe "when the user exists", -> + describe "when the user exists with sharelatex id", -> beforeEach -> + @user_id = ObjectId().toString() + @user = + _id: ObjectId(@user_id) + @req.params = user_id: @user_id @UserGetter.getUser = sinon.stub().callsArgWith(2, null, @user) @UserInfoController.sendFormattedPersonalInfo = sinon.stub() @UserInfoController.getPersonalInfo(@req, @res, @next) it "should look up the user in the database", -> @UserGetter.getUser - .calledWith(@user_id, { _id: true, first_name: true, last_name: true, email: true }) + .calledWith( + { _id: ObjectId(@user_id) }, + { _id: true, first_name: true, last_name: true, email: true } + ).should.equal true + + it "should send the formatted details back to the client", -> + @UserInfoController.sendFormattedPersonalInfo + .calledWith(@user, @res, @next) .should.equal true - + + describe "when the user exists with overleaf id", -> + beforeEach -> + @user_id = 12345 + @user = + _id: ObjectId() + overleaf: + id: @user_id + @req.params = user_id: @user_id.toString() + @UserGetter.getUser = sinon.stub().callsArgWith(2, null, @user) + @UserInfoController.sendFormattedPersonalInfo = sinon.stub() + @UserInfoController.getPersonalInfo(@req, @res, @next) + + it "should look up the user in the database", -> + @UserGetter.getUser + .calledWith( + { "overleaf.id": @user_id }, + { _id: true, first_name: true, last_name: true, email: true } + ).should.equal true + it "should send the formatted details back to the client", -> @UserInfoController.sendFormattedPersonalInfo .calledWith(@user, @res, @next) @@ -74,13 +98,24 @@ describe "UserInfoController", -> describe "when the user does not exist", -> beforeEach -> + @user_id = ObjectId().toString() + @req.params = user_id: @user_id @UserGetter.getUser = sinon.stub().callsArgWith(2, null, null) - @UserInfoController.sendFormattedPersonalInfo = sinon.stub() @UserInfoController.getPersonalInfo(@req, @res, @next) it "should return 404 to the client", -> @res.statusCode.should.equal 404 + describe "when the user id is invalid", -> + beforeEach -> + @user_id = "invalid" + @req.params = user_id: @user_id + @UserGetter.getUser = sinon.stub().callsArgWith(2, null, null) + @UserInfoController.getPersonalInfo(@req, @res, @next) + + it "should return 400 to the client", -> + @res.statusCode.should.equal 400 + describe "sendFormattedPersonalInfo", -> beforeEach -> @user = From 1b6829eed68d7c4d80d4e4fafa00e22ae1f20aad Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Tue, 26 Sep 2017 13:49:46 +0100 Subject: [PATCH 049/206] Style the project list table. --- .../public/stylesheets/app/project-list.less | 58 +++++++++++-------- .../stylesheets/core/_common-variables.less | 3 + .../public/stylesheets/core/ol-variables.less | 3 + 3 files changed, 40 insertions(+), 24 deletions(-) diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less index 39d7cf9069..b210b93477 100644 --- a/services/web/public/stylesheets/app/project-list.less +++ b/services/web/public/stylesheets/app/project-list.less @@ -222,38 +222,42 @@ ul.structured-list { overflow: hidden; overflow-y: auto; li { - border-bottom: 1px solid @gray-lightest; + border-bottom: 1px solid @structured-list-border-color; padding: (@line-height-computed / 4) 0; - &:first-child { - .header { - font-size: 1rem; - } - } + // &:first-child { + // .header { + // font-size: 1rem; + // } + // } &:last-child { border-bottom: 0 none; } &:hover { - background-color: @gray-lightest; + background-color: @structured-list-hover-color; } - &:first-child:hover { - background-color: white; + &:first-child { + border-bottom-color: @structured-header-border-color; + &:hover { + background-color: transparent; + } } a { color: @structured-list-link-color; } .header { - text-transform: uppercase; + font-weight: 600; } .select-item, .select-all { display: inline-block; + margin: 0 (@line-height-computed / 4); } - .select-item, .select-all { - position: absolute; - left: @line-height-computed; - } + // .select-item, .select-all { + // position: absolute; + // left: @line-height-computed; + // } .select-item + span, .select-all + span { display: inline-block; - padding-left: @line-height-computed * 1.5; + padding-left: (@line-height-computed / 4); max-width: 100%; overflow: hidden; text-overflow: ellipsis; @@ -262,17 +266,23 @@ ul.structured-list { } } +.project-list-card { + padding: 0 (@line-height-computed / 4); +} + ul.project-list { li { - .last-modified, .owner { - font-size: .8rem; - } - .owner { - margin-right: 0; - } - .projectName { - margin-right: @line-height-computed / 4; - } + line-height: 3; + padding: 0; + // .last-modified, .owner { + // font-size: .8rem; + // } + // .owner { + // margin-right: 0; + // } + // .projectName { + // margin-right: @line-height-computed / 4; + // } .tag-label { margin-left: @line-height-computed / 4; position: relative; diff --git a/services/web/public/stylesheets/core/_common-variables.less b/services/web/public/stylesheets/core/_common-variables.less index 49ad90b399..80454ce594 100644 --- a/services/web/public/stylesheets/core/_common-variables.less +++ b/services/web/public/stylesheets/core/_common-variables.less @@ -801,6 +801,9 @@ @card-box-shadow: 0 2px 4px rgba(0,0,0,0.15); @structured-list-link-color: darken(@blue, 10%); +@structured-header-border-color: @gray-lightest; +@structured-list-border-color: @gray-lightest; +@structured-list-hover-color: @gray-lightest; @sidebar-bg: transparent; @sidebar-link-color: #333; diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index 3befa66283..b6fcf39eed 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -79,6 +79,9 @@ // Project table @structured-list-link-color : @ol-blue; +@structured-header-border-color : shade(@ol-blue-gray-1, 5%); +@structured-list-border-color : @ol-blue-gray-1; +@structured-list-hover-color : lighten(@ol-blue-gray-1, 5%); //== Colors // From 1502da85cf0fd15daa1adbaa05bad35c1e479709 Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 27 Sep 2017 10:37:20 +0200 Subject: [PATCH 050/206] Set up acceptance tests to run in docker container --- services/web/Gruntfile.coffee | 25 ++++++++++++++-- services/web/Jenkinsfile | 6 ++++ services/web/package.json | 4 ++- .../coffee/AuthorizationTests.coffee | 3 ++ .../coffee/helpers/MockDocUpdaterApi.coffee | 12 ++++++++ .../coffee/helpers/MockDocstoreApi.coffee | 29 +++++++++++++++++++ .../web/test/acceptance/scripts/full-test.sh | 26 +++++++++++++++++ 7 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 services/web/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee create mode 100644 services/web/test/acceptance/coffee/helpers/MockDocstoreApi.coffee create mode 100755 services/web/test/acceptance/scripts/full-test.sh diff --git a/services/web/Gruntfile.coffee b/services/web/Gruntfile.coffee index 0845b435c2..82ec9987e3 100644 --- a/services/web/Gruntfile.coffee +++ b/services/web/Gruntfile.coffee @@ -20,6 +20,8 @@ module.exports = (grunt) -> grunt.loadNpmTasks 'grunt-parallel' grunt.loadNpmTasks 'grunt-exec' grunt.loadNpmTasks 'grunt-postcss' + grunt.loadNpmTasks 'grunt-forever' + grunt.loadNpmTasks 'grunt-shell' # grunt.loadNpmTasks 'grunt-contrib-imagemin' # grunt.loadNpmTasks 'grunt-sprity' @@ -31,6 +33,10 @@ module.exports = (grunt) -> cssmin: command:"node_modules/clean-css/bin/cleancss --s0 -o public/stylesheets/style.css public/stylesheets/style.css" + forever: + app: + options: + index: "app.js" watch: coffee: @@ -244,8 +250,11 @@ module.exports = (grunt) -> pattern: "@@RELEASE@@" replacement: process.env.BUILD_NUMBER || "(unknown build)" - - + shell: + fullAcceptanceTests: + command: "bash ./test/acceptance/scripts/full-test.sh" + dockerTests: + command: 'docker run -v "$(pwd):/app" --env SHARELATEX_ALLOW_PUBLIC_ACCESS=true --rm sharelatex/acceptance-test-runner' availabletasks: tasks: @@ -396,6 +405,18 @@ module.exports = (grunt) -> grunt.registerTask 'test:acceptance', 'Run the acceptance tests (use --grep= or --feature= for individual tests)', ['compile:acceptance_tests', 'mochaTest:acceptance'] grunt.registerTask 'test:smoke', 'Run the smoke tests', ['compile:smoke_tests', 'mochaTest:smoke'] + grunt.registerTask( + 'test:acceptance:full', + "Start server and run acceptance tests", + ['shell:fullAcceptanceTests'] + ) + + grunt.registerTask( + 'test:acceptance:docker', + "Run acceptance tests inside docker container", + ['compile:acceptance_tests', 'shell:dockerTests'] + ) + grunt.registerTask 'test:modules:unit', 'Run the unit tests for the modules', ['compile:modules:server', 'compile:modules:unit_tests'].concat(moduleUnitTestTasks) grunt.registerTask 'run:watch', "Compile and run the web-sharelatex server", ['compile', 'env:run', 'parallel'] diff --git a/services/web/Jenkinsfile b/services/web/Jenkinsfile index c43f82ac4e..dbc86d99fd 100644 --- a/services/web/Jenkinsfile +++ b/services/web/Jenkinsfile @@ -113,6 +113,12 @@ pipeline { } } + stage('Acceptance Tests') { + steps { + sh './node_modules/.bin/grunt test:acceptance:docker' + } + } + stage('Package') { steps { sh 'rm -rf ./node_modules/grunt*' diff --git a/services/web/package.json b/services/web/package.json index 335e99b85d..25122f635d 100644 --- a/services/web/package.json +++ b/services/web/package.json @@ -75,9 +75,9 @@ "bunyan": "0.22.1", "chai": "3.5.0", "chai-spies": "", - "grunt": "0.4.5", "clean-css": "^3.4.18", "es6-promise": "^4.0.5", + "grunt": "0.4.5", "grunt-available-tasks": "0.4.1", "grunt-bunyan": "0.5.0", "grunt-contrib-clean": "0.5.0", @@ -89,12 +89,14 @@ "grunt-exec": "^0.4.7", "grunt-execute": "^0.2.2", "grunt-file-append": "0.0.6", + "grunt-forever": "^0.4.7", "grunt-git-rev-parse": "^0.1.4", "grunt-mocha-test": "0.9.0", "grunt-newer": "^1.2.0", "grunt-parallel": "^0.5.1", "grunt-postcss": "^0.8.0", "grunt-sed": "^0.1.1", + "grunt-shell": "^2.1.0", "sandboxed-module": "0.2.0", "sinon": "^1.17.0", "timekeeper": "", diff --git a/services/web/test/acceptance/coffee/AuthorizationTests.coffee b/services/web/test/acceptance/coffee/AuthorizationTests.coffee index 05c1615a3e..37cc48226f 100644 --- a/services/web/test/acceptance/coffee/AuthorizationTests.coffee +++ b/services/web/test/acceptance/coffee/AuthorizationTests.coffee @@ -4,6 +4,9 @@ User = require "./helpers/User" request = require "./helpers/request" settings = require "settings-sharelatex" +MockDocstoreApi = require './helpers/MockDocstoreApi' +MockDocUpdaterApi = require './helpers/MockDocUpdaterApi' + try_read_access = (user, project_id, test, callback) -> async.series [ (cb) -> diff --git a/services/web/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee b/services/web/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee new file mode 100644 index 0000000000..07ecc33b9a --- /dev/null +++ b/services/web/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee @@ -0,0 +1,12 @@ +express = require("express") +app = express() + +module.exports = MockDocUpdaterApi = + run: () -> + app.post "/project/:project_id/flush", (req, res, next) => + res.sendStatus 200 + + app.listen 3003, (error) -> + throw error if error? + +MockDocUpdaterApi.run() diff --git a/services/web/test/acceptance/coffee/helpers/MockDocstoreApi.coffee b/services/web/test/acceptance/coffee/helpers/MockDocstoreApi.coffee new file mode 100644 index 0000000000..e7b0e4142c --- /dev/null +++ b/services/web/test/acceptance/coffee/helpers/MockDocstoreApi.coffee @@ -0,0 +1,29 @@ +express = require("express") +bodyParser = require "body-parser" +app = express() + +module.exports = MockDocUpdaterApi = + docs: {} + + run: () -> + app.post "/project/:project_id/doc/:doc_id", bodyParser.json(), (req, res, next) => + {project_id, doc_id} = req.params + {lines, version, ranges} = req.body + @docs[project_id] ?= {} + @docs[project_id][doc_id] = {lines, version, ranges} + @docs[project_id][doc_id].rev ?= 0 + @docs[project_id][doc_id].rev += 1 + res.json { + modified: true + rev: @docs[project_id][doc_id].rev + } + + app.get "/project/:project_id/doc", (req, res, next) => + docs = (doc for doc_id, doc of @docs[req.params.project_id]) + res.send JSON.stringify docs + + app.listen 3016, (error) -> + throw error if error? + +MockDocUpdaterApi.run() + diff --git a/services/web/test/acceptance/scripts/full-test.sh b/services/web/test/acceptance/scripts/full-test.sh new file mode 100755 index 0000000000..c8ce75dd4b --- /dev/null +++ b/services/web/test/acceptance/scripts/full-test.sh @@ -0,0 +1,26 @@ +#! /usr/bin/env bash + +# If you're running on OS X, you probably need to manually +# 'rm -r node_modules/bcrypt; npm install bcrypt' inside +# the docker container, before it will start. +# npm rebuild bcrypt + +echo ">> Starting server..." + +grunt --no-color forever:app:start + +echo ">> Server started" + +sleep 5 + +echo ">> Running acceptance tests..." +grunt --no-color mochaTest:acceptance +_test_exit_code=$? + +echo ">> Killing server" + +grunt --no-color forever:app:stop + +echo ">> Done" + +exit $_test_exit_code \ No newline at end of file From ffc35d9d65b5efab278d9c55ee8183cb9e71ac87 Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 27 Sep 2017 10:50:34 +0200 Subject: [PATCH 051/206] Include docker command directly because node isn't available in Jenkins --- services/web/Jenkinsfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/web/Jenkinsfile b/services/web/Jenkinsfile index dbc86d99fd..93f0a31008 100644 --- a/services/web/Jenkinsfile +++ b/services/web/Jenkinsfile @@ -115,7 +115,8 @@ pipeline { stage('Acceptance Tests') { steps { - sh './node_modules/.bin/grunt test:acceptance:docker' + sh 'docker pull sharelatex/acceptance-test-runner' + sh 'docker run --rm -v $(pwd):/app --env SHARELATEX_ALLOW_PUBLIC_ACCESS=true sharelatex/acceptance-test-runner' } } From 23bd8407961b0dd12262c273da9245e35e4e08bd Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 27 Sep 2017 10:53:00 +0200 Subject: [PATCH 052/206] Fix MockDocStoreApi name --- .../web/test/acceptance/coffee/helpers/MockDocstoreApi.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/test/acceptance/coffee/helpers/MockDocstoreApi.coffee b/services/web/test/acceptance/coffee/helpers/MockDocstoreApi.coffee index e7b0e4142c..5caed105b9 100644 --- a/services/web/test/acceptance/coffee/helpers/MockDocstoreApi.coffee +++ b/services/web/test/acceptance/coffee/helpers/MockDocstoreApi.coffee @@ -2,7 +2,7 @@ express = require("express") bodyParser = require "body-parser" app = express() -module.exports = MockDocUpdaterApi = +module.exports = MockDocStoreApi = docs: {} run: () -> From 96a129a8600a52be5b8ef8ef3df6ae17bf84446f Mon Sep 17 00:00:00 2001 From: Hayden Faulds Date: Wed, 27 Sep 2017 10:54:06 +0100 Subject: [PATCH 053/206] show user.name in history if available --- .../ide/history/controllers/HistoryListController.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/services/web/public/coffee/ide/history/controllers/HistoryListController.coffee b/services/web/public/coffee/ide/history/controllers/HistoryListController.coffee index d3ca0c50f2..f6e7c724c3 100644 --- a/services/web/public/coffee/ide/history/controllers/HistoryListController.coffee +++ b/services/web/public/coffee/ide/history/controllers/HistoryListController.coffee @@ -115,7 +115,10 @@ define [ $scope.resetHoverState() $scope.displayName = (user) -> - full_name = "#{user.first_name} #{user.last_name}" + if user.name? + full_name = user.name + else + full_name = "#{user.first_name} #{user.last_name}" fallback_name = "Unknown" if !user? fallback_name From afff8ddf29c524e80e9f4c6ceddd5824b6a42f6b Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Tue, 26 Sep 2017 14:50:45 +0100 Subject: [PATCH 054/206] Pass (dummy) autocompile onboarding --- .../app/coffee/Features/Project/ProjectController.coffee | 7 +++++-- services/web/app/views/project/editor.pug | 1 + services/web/public/coffee/ide.coffee | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/services/web/app/coffee/Features/Project/ProjectController.coffee b/services/web/app/coffee/Features/Project/ProjectController.coffee index 84fa89ea37..ef5df22f28 100644 --- a/services/web/app/coffee/Features/Project/ProjectController.coffee +++ b/services/web/app/coffee/Features/Project/ProjectController.coffee @@ -248,6 +248,8 @@ module.exports = ProjectController = return cb(null, false) else return cb(null, true) + showAutoCompileOnboarding: (cb) -> + return cb(null, true) }, (err, results)-> if err? logger.err err:err, "error getting details for project page" @@ -255,9 +257,9 @@ module.exports = ProjectController = project = results.project user = results.user subscription = results.subscription - { showTrackChangesOnboarding, showPerUserTCNotice } = results + { showTrackChangesOnboarding, showPerUserTCNotice, showAutoCompileOnboarding } = results - daysSinceLastUpdated = (new Date() - project.lastUpdated) /86400000 + daysSinceLastUpdated = (new Date() - project.lastUpdated) / 86400000 logger.log project_id:project_id, daysSinceLastUpdated:daysSinceLastUpdated, "got db results for loading editor" AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, (error, privilegeLevel)-> @@ -300,6 +302,7 @@ module.exports = ProjectController = trackChangesState: project.track_changes showTrackChangesOnboarding: !!showTrackChangesOnboarding showPerUserTCNotice: !!showPerUserTCNotice + showAutoCompileOnboarding: !!showAutoCompileOnboarding privilegeLevel: privilegeLevel chatUrl: Settings.apis.chat.url anonymous: anonymous diff --git a/services/web/app/views/project/editor.pug b/services/web/app/views/project/editor.pug index fb92510917..1065941815 100644 --- a/services/web/app/views/project/editor.pug +++ b/services/web/app/views/project/editor.pug @@ -122,6 +122,7 @@ block requirejs window.trackChangesState = data.trackChangesState; window.showTrackChangesOnboarding = #{!!showTrackChangesOnboarding}; window.showPerUserTCNotice = #{!!showPerUserTCNotice}; + window.showAutoCompileOnboarding = #{!!showAutoCompileOnboarding} window.wikiEnabled = #{!!(settings.apis.wiki && settings.apis.wiki.url)}; window.requirejs = { "paths" : { diff --git a/services/web/public/coffee/ide.coffee b/services/web/public/coffee/ide.coffee index e317b355ef..af7e710561 100644 --- a/services/web/public/coffee/ide.coffee +++ b/services/web/public/coffee/ide.coffee @@ -73,7 +73,8 @@ define [ pdfLayout: 'sideBySide' pdfHidden: false, reviewPanelOpen: localStorage("ui.reviewPanelOpen.#{window.project_id}") - miniReviewPanelVisible: false + miniReviewPanelVisible: false, + showAutoCompileOnboarding: window.user.betaProgram and window.showAutoCompileOnboarding } $scope.user = window.user From 8d2a451b44529b9bb38ba6e989f5e4970f88e509 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Wed, 27 Sep 2017 12:12:20 +0100 Subject: [PATCH 055/206] First pass at autocompile onboarding popover --- .../web/app/views/project/editor/editor.pug | 11 ++++++++++- .../autocompile/setting-dropdown.png | Bin 0 -> 22577 bytes .../app/editor/feature-onboarding.less | 17 ++++++++++++++++- 3 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 services/web/public/img/onboarding/autocompile/setting-dropdown.png diff --git a/services/web/app/views/project/editor/editor.pug b/services/web/app/views/project/editor/editor.pug index 6007f2e0be..3c3efd63a5 100644 --- a/services/web/app/views/project/editor/editor.pug +++ b/services/web/app/views/project/editor/editor.pug @@ -94,4 +94,13 @@ div.full-size( ng-show="ui.view == 'pdf'" ) include ./pdf - + +.onboarding__autocompile.popover.right + .arrow + .popover-inner + h3.popover-title Auto Compile + .popover-content + p Try out the new auto compile setting! + img(src="/img/onboarding/autocompile/setting-dropdown.png" width="100%") + p When enabled, your project will be compiled after you finish your sentence. + button.btn.btn-default.btn-block Got it diff --git a/services/web/public/img/onboarding/autocompile/setting-dropdown.png b/services/web/public/img/onboarding/autocompile/setting-dropdown.png new file mode 100644 index 0000000000000000000000000000000000000000..cc0da8f7a5820bf06f1ea94351152c26fdd3d11b GIT binary patch literal 22577 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 literal 0 HcmV?d00001 diff --git a/services/web/public/stylesheets/app/editor/feature-onboarding.less b/services/web/public/stylesheets/app/editor/feature-onboarding.less index 7289ed4fb7..f1098875aa 100644 --- a/services/web/public/stylesheets/app/editor/feature-onboarding.less +++ b/services/web/public/stylesheets/app/editor/feature-onboarding.less @@ -98,4 +98,19 @@ a.feat-onboard-dismiss { color: #FFF; opacity: 1; } -} \ No newline at end of file +} + +.onboarding__autocompile { + display: block; + top: 10px; + left: 694px; + + .arrow { + top: 17px !important; + left: -10px !important; + + &:after { + border-right-color: #f7f7f7 !important; + } + } +} From 316cf0722511f09a8f8a29b8f9b3c9e251ef59f4 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Wed, 27 Sep 2017 16:35:28 +0100 Subject: [PATCH 056/206] Add AutoCompileOnboardingController --- services/web/app/views/project/editor/editor.pug | 2 +- services/web/public/coffee/ide.coffee | 3 ++- .../coffee/ide/AutoCompileOnboardingController.coffee | 7 +++++++ 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 services/web/public/coffee/ide/AutoCompileOnboardingController.coffee diff --git a/services/web/app/views/project/editor/editor.pug b/services/web/app/views/project/editor/editor.pug index 3c3efd63a5..c68b889d6d 100644 --- a/services/web/app/views/project/editor/editor.pug +++ b/services/web/app/views/project/editor/editor.pug @@ -95,7 +95,7 @@ div.full-size( ) include ./pdf -.onboarding__autocompile.popover.right +.onboarding__autocompile.popover.right(ng-controller="AutoCompileOnboardingController") .arrow .popover-inner h3.popover-title Auto Compile diff --git a/services/web/public/coffee/ide.coffee b/services/web/public/coffee/ide.coffee index af7e710561..5fc208ce87 100644 --- a/services/web/public/coffee/ide.coffee +++ b/services/web/public/coffee/ide.coffee @@ -12,7 +12,8 @@ define [ "ide/labels/LabelsManager" "ide/review-panel/ReviewPanelManager" "ide/SafariScrollPatcher" - "ide/FeatureOnboardingController" + "ide/FeatureOnboardingController", + "ide/AutoCompileOnboardingController", "ide/settings/index" "ide/share/index" "ide/chat/index" diff --git a/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee b/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee new file mode 100644 index 0000000000..67110d59c9 --- /dev/null +++ b/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee @@ -0,0 +1,7 @@ +define [ + "base" +], (App) -> + App.controller "AutoCompileOnboardingController", ($scope) -> + unsub = $scope.$on "pdf.recompile", () -> + console.log('recompiling') + unsub() \ No newline at end of file From 9386ddf4a9a5f14f9c3147813f54ad70a77aa1f7 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Wed, 27 Sep 2017 17:08:58 +0100 Subject: [PATCH 057/206] Only show autocompile onboarding when first manual compile is run --- services/web/app/views/project/editor/editor.pug | 6 +++++- services/web/public/coffee/ide.coffee | 3 +++ .../public/coffee/ide/pdf/controllers/PdfController.coffee | 3 +++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/services/web/app/views/project/editor/editor.pug b/services/web/app/views/project/editor/editor.pug index c68b889d6d..daac701f31 100644 --- a/services/web/app/views/project/editor/editor.pug +++ b/services/web/app/views/project/editor/editor.pug @@ -95,7 +95,11 @@ div.full-size( ) include ./pdf -.onboarding__autocompile.popover.right(ng-controller="AutoCompileOnboardingController") + +.popover.right.onboarding__autocompile( + ng-controller="AutoCompileOnboardingController" + ng-if="onboarding.autoCompile == 'show'" +) .arrow .popover-inner h3.popover-title Auto Compile diff --git a/services/web/public/coffee/ide.coffee b/services/web/public/coffee/ide.coffee index 5fc208ce87..6e4061081c 100644 --- a/services/web/public/coffee/ide.coffee +++ b/services/web/public/coffee/ide.coffee @@ -77,6 +77,9 @@ define [ miniReviewPanelVisible: false, showAutoCompileOnboarding: window.user.betaProgram and window.showAutoCompileOnboarding } + $scope.onboarding = { + autoCompile: 'unseen' + } $scope.user = window.user $scope.$watch "project.features.trackChangesVisible", (visible) -> diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index 73ae9a4380..1e0403bb76 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -412,6 +412,9 @@ define [ $scope.recompile = (options = {}) -> return if $scope.pdf.compiling + if !options.isAutoCompile and $scope.onboarding.autoCompile == 'unseen' + $scope.onboarding.autoCompile = 'show' + event_tracking.sendMBSampled "editor-recompile-sampled", options $scope.pdf.compiling = true From 3259b871c0063cc6c489897ad537fef4ea36c1f8 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Wed, 27 Sep 2017 17:20:06 +0100 Subject: [PATCH 058/206] Dismiss autocompile onboarding --- services/web/app/views/project/editor/editor.pug | 3 ++- .../public/coffee/ide/AutoCompileOnboardingController.coffee | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/services/web/app/views/project/editor/editor.pug b/services/web/app/views/project/editor/editor.pug index daac701f31..68230e018e 100644 --- a/services/web/app/views/project/editor/editor.pug +++ b/services/web/app/views/project/editor/editor.pug @@ -107,4 +107,5 @@ div.full-size( p Try out the new auto compile setting! img(src="/img/onboarding/autocompile/setting-dropdown.png" width="100%") p When enabled, your project will be compiled after you finish your sentence. - button.btn.btn-default.btn-block Got it + button.btn.btn-default.btn-block(ng-click="dismiss()") + | Got it diff --git a/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee b/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee index 67110d59c9..815825e750 100644 --- a/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee +++ b/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee @@ -2,6 +2,5 @@ define [ "base" ], (App) -> App.controller "AutoCompileOnboardingController", ($scope) -> - unsub = $scope.$on "pdf.recompile", () -> - console.log('recompiling') - unsub() \ No newline at end of file + $scope.dismiss = () -> + $scope.onboarding.autoCompile = 'dismissed' \ No newline at end of file From 7e407621e9ac094d0c7e9fa779597db792ae0df3 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Wed, 27 Sep 2017 17:45:28 +0100 Subject: [PATCH 059/206] Position onboarding popover after measuring position --- services/web/app/views/project/editor/pdf.pug | 2 +- .../coffee/ide/AutoCompileOnboardingController.coffee | 7 +++++++ .../public/stylesheets/app/editor/feature-onboarding.less | 1 - 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/services/web/app/views/project/editor/pdf.pug b/services/web/app/views/project/editor/pdf.pug index 065dd0cec0..536eefeb5b 100644 --- a/services/web/app/views/project/editor/pdf.pug +++ b/services/web/app/views/project/editor/pdf.pug @@ -1,6 +1,6 @@ div.full-size.pdf(ng-controller="PdfController") .toolbar.toolbar-tall - .btn-group( + .btn-group#recompile( dropdown, tooltip-html="'"+translate('recompile_pdf')+" ({{modifierKey}} + Enter)'" tooltip-class="keyboard-tooltip" diff --git a/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee b/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee index 815825e750..08d7674dcd 100644 --- a/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee +++ b/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee @@ -2,5 +2,12 @@ define [ "base" ], (App) -> App.controller "AutoCompileOnboardingController", ($scope) -> + recompileBtn = angular.element('#recompile') + { top, left } = recompileBtn.offset() + angular.element('.onboarding__autocompile').offset({ + top: top, + left: left + 170 + }) + $scope.dismiss = () -> $scope.onboarding.autoCompile = 'dismissed' \ No newline at end of file diff --git a/services/web/public/stylesheets/app/editor/feature-onboarding.less b/services/web/public/stylesheets/app/editor/feature-onboarding.less index f1098875aa..cd4b6f3b3a 100644 --- a/services/web/public/stylesheets/app/editor/feature-onboarding.less +++ b/services/web/public/stylesheets/app/editor/feature-onboarding.less @@ -103,7 +103,6 @@ a.feat-onboard-dismiss { .onboarding__autocompile { display: block; top: 10px; - left: 694px; .arrow { top: 17px !important; From fcc2db9ea7c3dc6723632e3839fbfed46231f3f0 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 28 Sep 2017 11:03:54 +0100 Subject: [PATCH 060/206] Use width of button instead of hardcoded --- services/web/app/views/project/editor/editor.pug | 1 - .../public/coffee/ide/AutoCompileOnboardingController.coffee | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/services/web/app/views/project/editor/editor.pug b/services/web/app/views/project/editor/editor.pug index 68230e018e..26602de1b1 100644 --- a/services/web/app/views/project/editor/editor.pug +++ b/services/web/app/views/project/editor/editor.pug @@ -95,7 +95,6 @@ div.full-size( ) include ./pdf - .popover.right.onboarding__autocompile( ng-controller="AutoCompileOnboardingController" ng-if="onboarding.autoCompile == 'show'" diff --git a/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee b/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee index 08d7674dcd..184ac05e51 100644 --- a/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee +++ b/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee @@ -6,7 +6,7 @@ define [ { top, left } = recompileBtn.offset() angular.element('.onboarding__autocompile').offset({ top: top, - left: left + 170 + left: left + recompileBtn.width() + 11 # Width of arrow }) $scope.dismiss = () -> From 464c7c79d8bfa0903c1b17fb99894673e2eca0f7 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 28 Sep 2017 11:31:55 +0100 Subject: [PATCH 061/206] Keep track of pdf panel width --- services/web/public/coffee/ide.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/web/public/coffee/ide.coffee b/services/web/public/coffee/ide.coffee index 6e4061081c..b082ed0413 100644 --- a/services/web/public/coffee/ide.coffee +++ b/services/web/public/coffee/ide.coffee @@ -73,6 +73,7 @@ define [ chatOpen: false pdfLayout: 'sideBySide' pdfHidden: false, + pdfWidth: 0, reviewPanelOpen: localStorage("ui.reviewPanelOpen.#{window.project_id}") miniReviewPanelVisible: false, showAutoCompileOnboarding: window.user.betaProgram and window.showAutoCompileOnboarding @@ -107,6 +108,7 @@ define [ $scope.$on "layout:pdf:resize", (_, layoutState) -> $scope.ui.pdfHidden = layoutState.east.initClosed + $scope.ui.pdfWidth = layoutState.east.size # Tracking code. $scope.$watch "ui.view", (newView, oldView) -> From 2d5a61f5b6d7facc5c1ce422e567376c5a850d7c Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 28 Sep 2017 11:32:38 +0100 Subject: [PATCH 062/206] Determine placement of popover based on width of pdf panel --- .../web/app/views/project/editor/editor.pug | 21 ++++++++++--------- .../AutoCompileOnboardingController.coffee | 19 +++++++++++++---- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/services/web/app/views/project/editor/editor.pug b/services/web/app/views/project/editor/editor.pug index 26602de1b1..ccda61229d 100644 --- a/services/web/app/views/project/editor/editor.pug +++ b/services/web/app/views/project/editor/editor.pug @@ -95,16 +95,17 @@ div.full-size( ) include ./pdf -.popover.right.onboarding__autocompile( +div( ng-controller="AutoCompileOnboardingController" ng-if="onboarding.autoCompile == 'show'" ) - .arrow - .popover-inner - h3.popover-title Auto Compile - .popover-content - p Try out the new auto compile setting! - img(src="/img/onboarding/autocompile/setting-dropdown.png" width="100%") - p When enabled, your project will be compiled after you finish your sentence. - button.btn.btn-default.btn-block(ng-click="dismiss()") - | Got it + .onboarding__autocompile.popover(ng-class="placement") + .arrow + .popover-inner + h3.popover-title Auto Compile + .popover-content + p Try out the new auto compile setting! + img(src="/img/onboarding/autocompile/setting-dropdown.png" width="100%") + p When enabled, your project will be compiled after you finish your sentence. + button.btn.btn-default.btn-block(ng-click="dismiss()") + | Got it diff --git a/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee b/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee index 184ac05e51..3131661315 100644 --- a/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee +++ b/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee @@ -3,11 +3,22 @@ define [ ], (App) -> App.controller "AutoCompileOnboardingController", ($scope) -> recompileBtn = angular.element('#recompile') + popover = angular.element('.onboarding__autocompile') { top, left } = recompileBtn.offset() - angular.element('.onboarding__autocompile').offset({ - top: top, - left: left + recompileBtn.width() + 11 # Width of arrow - }) + + if $scope.ui.pdfWidth < 475 + $scope.placement = 'left' + popover.offset({ + top: top, + left: left - popover.width() - 11 # Width of arrow + }) + else + $scope.placement = 'right' + angular.element('.onboarding__autocompile').offset({ + top: top, + left: left + recompileBtn.width() + 11 # Width of arrow + }) + $scope.dismiss = () -> $scope.onboarding.autoCompile = 'dismissed' \ No newline at end of file From e78ee69c04cb37535fc21bf25bf175b7fa29c3f4 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 28 Sep 2017 11:55:20 +0100 Subject: [PATCH 063/206] Fix positioning & styling of arrows --- .../coffee/ide/AutoCompileOnboardingController.coffee | 5 ++--- .../stylesheets/app/editor/feature-onboarding.less | 11 +++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee b/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee index 3131661315..50852d8093 100644 --- a/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee +++ b/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee @@ -10,15 +10,14 @@ define [ $scope.placement = 'left' popover.offset({ top: top, - left: left - popover.width() - 11 # Width of arrow + left: left - popover.width() }) else $scope.placement = 'right' angular.element('.onboarding__autocompile').offset({ top: top, - left: left + recompileBtn.width() + 11 # Width of arrow + left: left + recompileBtn.width() }) - $scope.dismiss = () -> $scope.onboarding.autoCompile = 'dismissed' \ No newline at end of file diff --git a/services/web/public/stylesheets/app/editor/feature-onboarding.less b/services/web/public/stylesheets/app/editor/feature-onboarding.less index cd4b6f3b3a..134001b212 100644 --- a/services/web/public/stylesheets/app/editor/feature-onboarding.less +++ b/services/web/public/stylesheets/app/editor/feature-onboarding.less @@ -106,10 +106,21 @@ a.feat-onboard-dismiss { .arrow { top: 17px !important; + } + + &.right > .arrow { left: -10px !important; &:after { border-right-color: #f7f7f7 !important; } } + + &.left > .arrow { + right: -10px !important; + + &:after { + border-left-color: #f7f7f7 !important; + } + } } From af6b722dfb3bb07475405184bf92a4c749a48c15 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Thu, 28 Sep 2017 12:12:04 +0100 Subject: [PATCH 064/206] Add Overleaf SVG assets. --- services/web/public/img/ol-brand/overleaf-white.svg | 1 + services/web/public/img/ol-brand/overleaf.svg | 1 + 2 files changed, 2 insertions(+) create mode 100644 services/web/public/img/ol-brand/overleaf-white.svg create mode 100644 services/web/public/img/ol-brand/overleaf.svg diff --git a/services/web/public/img/ol-brand/overleaf-white.svg b/services/web/public/img/ol-brand/overleaf-white.svg new file mode 100644 index 0000000000..2ced81aa46 --- /dev/null +++ b/services/web/public/img/ol-brand/overleaf-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/services/web/public/img/ol-brand/overleaf.svg b/services/web/public/img/ol-brand/overleaf.svg new file mode 100644 index 0000000000..73d2a9b4e0 --- /dev/null +++ b/services/web/public/img/ol-brand/overleaf.svg @@ -0,0 +1 @@ + \ No newline at end of file From 589d4521a5a9f558b83e3002f9c7ae1604d13479 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Thu, 28 Sep 2017 12:12:22 +0100 Subject: [PATCH 065/206] Add source map to less files. --- services/web/Gruntfile.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/web/Gruntfile.coffee b/services/web/Gruntfile.coffee index 0845b435c2..6e986edb61 100644 --- a/services/web/Gruntfile.coffee +++ b/services/web/Gruntfile.coffee @@ -137,9 +137,13 @@ module.exports = (grunt) -> less: app: + options: + sourceMap: true files: "public/stylesheets/style.css": "public/stylesheets/style.less" ol: + options: + sourceMap: true files: "public/stylesheets/ol-style.css": "public/stylesheets/ol-style.less" From e68b166309edb8d29880972f153a18ffbdda5877 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Thu, 28 Sep 2017 12:12:33 +0100 Subject: [PATCH 066/206] Style the navbar. --- .../public/stylesheets/components/navbar.less | 26 +++++++++---------- .../stylesheets/core/_common-variables.less | 13 ++++++++++ .../public/stylesheets/core/ol-variables.less | 21 ++++++++++++--- 3 files changed, 44 insertions(+), 16 deletions(-) diff --git a/services/web/public/stylesheets/components/navbar.less b/services/web/public/stylesheets/components/navbar.less index 52a4a10029..12c8f552f7 100755 --- a/services/web/public/stylesheets/components/navbar.less +++ b/services/web/public/stylesheets/components/navbar.less @@ -378,7 +378,7 @@ .navbar-default { background-color: @navbar-default-bg; border-color: @navbar-default-border; - padding: 1rem 2rem; + padding: @navbar-default-padding; .navbar-brand { position: absolute; @@ -400,11 +400,11 @@ > li > a { color: @navbar-default-link-color; border: 2px solid transparent; - border-radius: @border-radius-base; - font-size: @font-size-base * .8; - font-weight: 700; - line-height: 1; - padding: 10px 10px 11px; + border-radius: @navbar-btn-border-radius; + font-size: @navbar-btn-font-size; + font-weight: @navbar-btn-font-weight; + line-height: @navbar-btn-line-height; + padding: @navbar-btn-padding; &:hover, &:focus { @@ -432,15 +432,15 @@ > li.subdued > a { border: 0; - color: @gray; - padding: 12px 12px 13px; + color: @navbar-subdued-color; + padding: @navbar-subdued-padding; margin-left: 0; &:hover { - color: @gray-dark; - background-color: @gray-lightest; + color: @navbar-subdued-hover-color; + background-color: @navbar-subdued-hover-bg; } &:focus { - color: @gray; + color: @navbar-subdued-color; background-color: transparent; } } @@ -485,8 +485,8 @@ &, &:hover, &:focus { - color: @gray-dark; - background-color: @gray-lightest; + color: @navbar-subdued-hover-color; + background-color: @navbar-subdued-hover-bg; } } diff --git a/services/web/public/stylesheets/core/_common-variables.less b/services/web/public/stylesheets/core/_common-variables.less index 80454ce594..83fa3e3e55 100644 --- a/services/web/public/stylesheets/core/_common-variables.less +++ b/services/web/public/stylesheets/core/_common-variables.less @@ -794,6 +794,19 @@ // Custom @content-alt-bg-color: lighten(@gray-lightest, 2.5%); +@navbar-default-padding: 1rem 2rem; + +@navbar-btn-font-size: @font-size-base * 0.8; +@navbar-btn-border-radius: @border-radius-base; +@navbar-btn-font-weight: 700; +@navbar-btn-padding: 10px 10px 11px; +@navbar-btn-line-height: 1; + +@navbar-subdued-padding: 12px 12px 13px; +@navbar-subdued-color: @gray; +@navbar-subdued-hover-bg: @gray-lightest; +@navbar-subdued-hover-color: @gray-dark; + @btn-border-radius-large: @border-radius-large; @btn-border-radius-base: @border-radius-base; @btn-border-radius-small: @border-radius-small; diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index b6fcf39eed..484e851179 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -21,7 +21,7 @@ @navbar-default-color : #FFF; @navbar-default-bg : @ol-blue-gray-6; @navbar-default-border : transparent; -@navbar-brand-image-url : url(/img/ol-brand/logo-horizontal.png); +@navbar-brand-image-url : url(/img/ol-brand/overleaf-white.svg); // Backgrounds @body-bg : @ol-blue-gray-1; @@ -56,6 +56,23 @@ @btn-info-bg : @ol-blue; @btn-info-border : transparent; +// Navbar +@navbar-default-padding : (@grid-gutter-width / 2) 0; +@navbar-default-link-color : #FFF; +@navbar-default-link-hover-bg : @ol-green; +@navbar-default-link-active-bg : @ol-green; +@navbar-default-link-hover-color : @ol-green; +@navbar-btn-font-size : @font-size-base; +@navbar-btn-border-radius : @btn-border-radius-base; +@navbar-btn-font-weight : 400; +@navbar-btn-padding : (@padding-base-vertical - 1) @padding-base-horizontal @padding-base-vertical; +@navbar-btn-line-height : @line-height-base; + +@navbar-subdued-color : #FFF; +@navbar-subdued-padding : (@padding-base-vertical + 1) (@padding-base-horizontal + 1) (@padding-base-vertical + 2); +@navbar-subdued-hover-bg : #FFF; +@navbar-subdued-hover-color : @ol-green; + // TODO Warning color-orange? @btn-warning-color : #FFF; @btn-warning-bg : @ol-red; @@ -76,7 +93,6 @@ @sidebar-hover-bg : @ol-blue-gray-4; @sidebar-hover-text-decoration : none; - // Project table @structured-list-link-color : @ol-blue; @structured-header-border-color : shade(@ol-blue-gray-1, 5%); @@ -108,7 +124,6 @@ @brand-warning: @orange; @brand-danger: #E03A06; - @editor-loading-logo-padding-top: 115.44%; @editor-loading-logo-background-url: url(/img/ol-brand/overleaf-o-grey.svg); @editor-loading-logo-foreground-url: url(/img/ol-brand/overleaf-o.svg); \ No newline at end of file From d9af9e723b83b1c7709842167749095026a9ac55 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 28 Sep 2017 12:12:48 +0100 Subject: [PATCH 067/206] Clean up popover wrapper --- .../web/app/views/project/editor/editor.pug | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/services/web/app/views/project/editor/editor.pug b/services/web/app/views/project/editor/editor.pug index ccda61229d..f9621987b8 100644 --- a/services/web/app/views/project/editor/editor.pug +++ b/services/web/app/views/project/editor/editor.pug @@ -95,17 +95,17 @@ div.full-size( ) include ./pdf -div( +.onboarding__autocompile.popover( ng-controller="AutoCompileOnboardingController" ng-if="onboarding.autoCompile == 'show'" + ng-class="placement" ) - .onboarding__autocompile.popover(ng-class="placement") - .arrow - .popover-inner - h3.popover-title Auto Compile - .popover-content - p Try out the new auto compile setting! - img(src="/img/onboarding/autocompile/setting-dropdown.png" width="100%") - p When enabled, your project will be compiled after you finish your sentence. - button.btn.btn-default.btn-block(ng-click="dismiss()") - | Got it + .arrow + .popover-inner + h3.popover-title Auto Compile + .popover-content + p Try out the new auto compile setting! + img(src="/img/onboarding/autocompile/setting-dropdown.png" width="100%") + p When enabled, your project will be compiled after you finish your sentence. + button.btn.btn-default.btn-block(ng-click="dismiss()") + | Got it From 199e85e4a98ff181588b0c17e9def1678bedb879 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 28 Sep 2017 12:16:02 +0100 Subject: [PATCH 068/206] Comment --- .../public/coffee/ide/AutoCompileOnboardingController.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee b/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee index 50852d8093..37b52c754f 100644 --- a/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee +++ b/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee @@ -6,6 +6,8 @@ define [ popover = angular.element('.onboarding__autocompile') { top, left } = recompileBtn.offset() + # If pdf panel smaller than recompile button + popover, show to left. + # Otherwise show to right if $scope.ui.pdfWidth < 475 $scope.placement = 'left' popover.offset({ From 39c8a6a1939d2b2da75a1fb61c35df382331a552 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 28 Sep 2017 12:27:29 +0100 Subject: [PATCH 069/206] Send analytics data when onboarding dismissed --- .../public/coffee/ide/AutoCompileOnboardingController.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee b/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee index 37b52c754f..5dd11d2230 100644 --- a/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee +++ b/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee @@ -22,4 +22,5 @@ define [ }) $scope.dismiss = () -> - $scope.onboarding.autoCompile = 'dismissed' \ No newline at end of file + $scope.onboarding.autoCompile = 'dismissed' + event_tracking.sendMB "shown-autocompile-onboarding" From e95778c703f656ee8d0183004809be229d7017e7 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 28 Sep 2017 13:02:16 +0100 Subject: [PATCH 070/206] Fix event tracking not being injecting --- .../public/coffee/ide/AutoCompileOnboardingController.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee b/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee index 5dd11d2230..aecfcb395a 100644 --- a/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee +++ b/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee @@ -1,7 +1,7 @@ define [ "base" ], (App) -> - App.controller "AutoCompileOnboardingController", ($scope) -> + App.controller "AutoCompileOnboardingController", ($scope, event_tracking) -> recompileBtn = angular.element('#recompile') popover = angular.element('.onboarding__autocompile') { top, left } = recompileBtn.offset() From f2c0bf55156c02a0444b277e661a6352306bc658 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 28 Sep 2017 13:04:18 +0100 Subject: [PATCH 071/206] First pass at getting onboarding shown event from analytics --- .../coffee/Features/Project/ProjectController.coffee | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/services/web/app/coffee/Features/Project/ProjectController.coffee b/services/web/app/coffee/Features/Project/ProjectController.coffee index ef5df22f28..479a57a00c 100644 --- a/services/web/app/coffee/Features/Project/ProjectController.coffee +++ b/services/web/app/coffee/Features/Project/ProjectController.coffee @@ -249,7 +249,16 @@ module.exports = ProjectController = else return cb(null, true) showAutoCompileOnboarding: (cb) -> - return cb(null, true) + cb = underscore.once(cb) + if (!user_id?) + return cb() + AnalyticsManager.getLastOccurance user_id, "shown-autocompile-onboarding", (error, event) -> + if error? + return cb(null, false) + else if event? + return cb(null, false) + else + return cb(null, true) }, (err, results)-> if err? logger.err err:err, "error getting details for project page" From 3e2388a7dee5b975fc528b7f03448d98c5687fe7 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 28 Sep 2017 13:57:15 +0100 Subject: [PATCH 072/206] Don't show autocompile for users signed up after release date --- .../app/coffee/Features/Project/ProjectController.coffee | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/services/web/app/coffee/Features/Project/ProjectController.coffee b/services/web/app/coffee/Features/Project/ProjectController.coffee index 479a57a00c..7c0328fe17 100644 --- a/services/web/app/coffee/Features/Project/ProjectController.coffee +++ b/services/web/app/coffee/Features/Project/ProjectController.coffee @@ -250,9 +250,16 @@ module.exports = ProjectController = return cb(null, true) showAutoCompileOnboarding: (cb) -> cb = underscore.once(cb) - if (!user_id?) + if !user_id? return cb() + timestamp = user_id.toString().substring(0,8) + userSignupDate = new Date( parseInt( timestamp, 16 ) * 1000 ) + if userSignupDate > new Date("2017-10-05") + # Don't show for users who registered after it was released + return cb(null, false) + timeout = setTimeout cb, 500 AnalyticsManager.getLastOccurance user_id, "shown-autocompile-onboarding", (error, event) -> + clearTimeout timeout if error? return cb(null, false) else if event? From e41a48de7253ee277b73f43a6918dba6575bfa3e Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Thu, 28 Sep 2017 14:01:03 +0100 Subject: [PATCH 073/206] Conditionally disable text-shadow on chrome v61 and v62. --- services/web/public/coffee/ide.coffee | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/services/web/public/coffee/ide.coffee b/services/web/public/coffee/ide.coffee index e317b355ef..357b45224e 100644 --- a/services/web/public/coffee/ide.coffee +++ b/services/web/public/coffee/ide.coffee @@ -185,6 +185,21 @@ define [ if ide.browserIsSafari ide.safariScrollPatcher = new SafariScrollPatcher($scope) + # Fix Chrome 61 and 62 text-shadow rendering + browserIsChrome61or62 = false + try + chromeVersion = parseFloat(navigator.userAgent.split(" Chrome/")[1]) || null; + browserIsChrome61or62 = ( + chromeVersion? && + (chromeVersion == 61 || chromeVersion == 62) + ) + console.log chromeVersion, browserIsChrome61or62 + if browserIsChrome61or62 + document.styleSheets[0].insertRule(".ace_editor.ace_autocomplete .ace_completion-highlight { text-shadow: none !important; }", 1) + catch err + console.error err + + # User can append ?ft=somefeature to url to activate a feature toggle ide.featureToggle = location?.search?.match(/^\?ft=(\w+)$/)?[1] From 65d38aa7f7b2df09529613abc74b49d7add4cd00 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Thu, 28 Sep 2017 14:09:37 +0100 Subject: [PATCH 074/206] Remove debug line. --- services/web/public/coffee/ide.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/services/web/public/coffee/ide.coffee b/services/web/public/coffee/ide.coffee index 357b45224e..06201bd0b1 100644 --- a/services/web/public/coffee/ide.coffee +++ b/services/web/public/coffee/ide.coffee @@ -193,7 +193,6 @@ define [ chromeVersion? && (chromeVersion == 61 || chromeVersion == 62) ) - console.log chromeVersion, browserIsChrome61or62 if browserIsChrome61or62 document.styleSheets[0].insertRule(".ace_editor.ace_autocomplete .ace_completion-highlight { text-shadow: none !important; }", 1) catch err From 8cd1f8c7ac595421e02d935c91c29382bc4da57e Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 28 Sep 2017 14:13:51 +0100 Subject: [PATCH 075/206] Simplify logic for showing autocompile onboarding --- services/web/public/coffee/ide.coffee | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/services/web/public/coffee/ide.coffee b/services/web/public/coffee/ide.coffee index b082ed0413..b3d83e7ee5 100644 --- a/services/web/public/coffee/ide.coffee +++ b/services/web/public/coffee/ide.coffee @@ -74,12 +74,11 @@ define [ pdfLayout: 'sideBySide' pdfHidden: false, pdfWidth: 0, - reviewPanelOpen: localStorage("ui.reviewPanelOpen.#{window.project_id}") + reviewPanelOpen: localStorage("ui.reviewPanelOpen.#{window.project_id}"), miniReviewPanelVisible: false, - showAutoCompileOnboarding: window.user.betaProgram and window.showAutoCompileOnboarding } $scope.onboarding = { - autoCompile: 'unseen' + autoCompile: if window.user.betaProgram and window.showAutoCompileOnboarding then 'unseen' else 'dismissed' } $scope.user = window.user From c50f309b0998f8a17bdd1f92a9105cd65e38dd85 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 28 Sep 2017 15:12:37 +0100 Subject: [PATCH 076/206] add missing whitespace in pdf.pug --- services/web/app/views/project/editor/pdf.pug | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/app/views/project/editor/pdf.pug b/services/web/app/views/project/editor/pdf.pug index 065dd0cec0..377f09f71a 100644 --- a/services/web/app/views/project/editor/pdf.pug +++ b/services/web/app/views/project/editor/pdf.pug @@ -345,7 +345,7 @@ div.full-size.pdf(ng-controller="PdfController") span #{translate("project_flagged_too_many_compiles")} .alert.alert-danger(ng-show="pdf.compileInProgress") - strong #{translate("pdf_compile_in_progress_error")} + strong #{translate("pdf_compile_in_progress_error")}. span #{translate("pdf_compile_try_again")} .alert.alert-danger(ng-show="pdf.timedout") From 3c9c94f3c20c7376110eab43f8a3f171db150df5 Mon Sep 17 00:00:00 2001 From: Joe Green Date: Fri, 29 Sep 2017 10:37:39 +0100 Subject: [PATCH 077/206] release number (#32) Replace release number in pug template for sentry --- services/web/Jenkinsfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/web/Jenkinsfile b/services/web/Jenkinsfile index 93f0a31008..6421174e68 100644 --- a/services/web/Jenkinsfile +++ b/services/web/Jenkinsfile @@ -74,6 +74,8 @@ pipeline { } steps { sh 'node_modules/.bin/grunt compile --verbose' + // replace the build number placeholder for sentry + sh 'node_modules/.bin/grunt version' } } From dd7e6f0612a8de51fc795918490ccf63b926d45b Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Fri, 29 Sep 2017 10:49:45 +0100 Subject: [PATCH 078/206] Make class names more consistent & switch to JS targeting through id --- services/web/app/views/project/editor/editor.pug | 2 +- .../public/coffee/ide/AutoCompileOnboardingController.coffee | 2 +- .../web/public/stylesheets/app/editor/feature-onboarding.less | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/services/web/app/views/project/editor/editor.pug b/services/web/app/views/project/editor/editor.pug index f9621987b8..40734e8632 100644 --- a/services/web/app/views/project/editor/editor.pug +++ b/services/web/app/views/project/editor/editor.pug @@ -95,7 +95,7 @@ div.full-size( ) include ./pdf -.onboarding__autocompile.popover( +#onboarding-autocompile.onboarding-autocompile.popover( ng-controller="AutoCompileOnboardingController" ng-if="onboarding.autoCompile == 'show'" ng-class="placement" diff --git a/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee b/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee index aecfcb395a..80ba7de814 100644 --- a/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee +++ b/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee @@ -3,7 +3,7 @@ define [ ], (App) -> App.controller "AutoCompileOnboardingController", ($scope, event_tracking) -> recompileBtn = angular.element('#recompile') - popover = angular.element('.onboarding__autocompile') + popover = angular.element('#onboarding-autocompile') { top, left } = recompileBtn.offset() # If pdf panel smaller than recompile button + popover, show to left. diff --git a/services/web/public/stylesheets/app/editor/feature-onboarding.less b/services/web/public/stylesheets/app/editor/feature-onboarding.less index 134001b212..80c21e26c3 100644 --- a/services/web/public/stylesheets/app/editor/feature-onboarding.less +++ b/services/web/public/stylesheets/app/editor/feature-onboarding.less @@ -100,7 +100,7 @@ a.feat-onboard-dismiss { } } -.onboarding__autocompile { +.onboarding-autocompile { display: block; top: 10px; From ba6a0b44a996e74afbde7f5b989c95c75dcd3db2 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Fri, 29 Sep 2017 10:53:06 +0100 Subject: [PATCH 079/206] Fix incorrect selector --- .../public/coffee/ide/AutoCompileOnboardingController.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee b/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee index 80ba7de814..a57575fb94 100644 --- a/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee +++ b/services/web/public/coffee/ide/AutoCompileOnboardingController.coffee @@ -16,7 +16,7 @@ define [ }) else $scope.placement = 'right' - angular.element('.onboarding__autocompile').offset({ + popover.offset({ top: top, left: left + recompileBtn.width() }) From ab7d83b1c5616d6c7196e5b5e772efb1d69957da Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Fri, 29 Sep 2017 10:59:01 +0100 Subject: [PATCH 080/206] Switch to translations --- services/web/app/views/project/editor/editor.pug | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services/web/app/views/project/editor/editor.pug b/services/web/app/views/project/editor/editor.pug index 40734e8632..37a103c6ce 100644 --- a/services/web/app/views/project/editor/editor.pug +++ b/services/web/app/views/project/editor/editor.pug @@ -102,10 +102,10 @@ div.full-size( ) .arrow .popover-inner - h3.popover-title Auto Compile + h3.popover-title #{translate("auto_compile")} .popover-content - p Try out the new auto compile setting! + p #{translate("try_out_auto_compile_setting")} img(src="/img/onboarding/autocompile/setting-dropdown.png" width="100%") - p When enabled, your project will be compiled after you finish your sentence. + p #{tranlate("auto_compile_onboarding_description")} button.btn.btn-default.btn-block(ng-click="dismiss()") - | Got it + | #{translate("got_it")} From a4fa713f38dd9347e6ddfc60df46fa87c68fec7f Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Fri, 29 Sep 2017 11:18:44 +0100 Subject: [PATCH 081/206] Fix typo --- services/web/app/views/project/editor/editor.pug | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/app/views/project/editor/editor.pug b/services/web/app/views/project/editor/editor.pug index 37a103c6ce..66bff761a1 100644 --- a/services/web/app/views/project/editor/editor.pug +++ b/services/web/app/views/project/editor/editor.pug @@ -106,6 +106,6 @@ div.full-size( .popover-content p #{translate("try_out_auto_compile_setting")} img(src="/img/onboarding/autocompile/setting-dropdown.png" width="100%") - p #{tranlate("auto_compile_onboarding_description")} + p #{translate("auto_compile_onboarding_description")} button.btn.btn-default.btn-block(ng-click="dismiss()") | #{translate("got_it")} From 738acefac745339bf2d907f77dd3a53177366c26 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Fri, 29 Sep 2017 11:38:56 +0100 Subject: [PATCH 082/206] Style onboarding image with more space & light border --- .../public/stylesheets/app/editor/feature-onboarding.less | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/services/web/public/stylesheets/app/editor/feature-onboarding.less b/services/web/public/stylesheets/app/editor/feature-onboarding.less index 80c21e26c3..ef89a93df4 100644 --- a/services/web/public/stylesheets/app/editor/feature-onboarding.less +++ b/services/web/public/stylesheets/app/editor/feature-onboarding.less @@ -108,6 +108,11 @@ a.feat-onboard-dismiss { top: 17px !important; } + img { + margin-bottom: 10px; + border: 1px solid @gray-lighter; + } + &.right > .arrow { left: -10px !important; From bf1c24f6f9c308a93c3c03721864bace2fced0b5 Mon Sep 17 00:00:00 2001 From: Hayden Faulds Date: Thu, 28 Sep 2017 15:01:45 +0100 Subject: [PATCH 083/206] return pathname from DocumentController.getDoc --- .../Documents/DocumentController.coffee | 6 +- .../Project/ProjectEntityHandler.coffee | 25 +++-- .../Documents/DocumentControllerTests.coffee | 8 +- .../Project/ProjectEntityHandlerTests.coffee | 105 +++++++++++------- 4 files changed, 88 insertions(+), 56 deletions(-) diff --git a/services/web/app/coffee/Features/Documents/DocumentController.coffee b/services/web/app/coffee/Features/Documents/DocumentController.coffee index 2042f6a218..5a68a4777c 100644 --- a/services/web/app/coffee/Features/Documents/DocumentController.coffee +++ b/services/web/app/coffee/Features/Documents/DocumentController.coffee @@ -7,7 +7,7 @@ module.exports = doc_id = req.params.doc_id plain = req?.query?.plain == 'true' logger.log doc_id:doc_id, project_id:project_id, "receiving get document request from api (docupdater)" - ProjectEntityHandler.getDoc project_id, doc_id, (error, lines, rev, version, ranges) -> + ProjectEntityHandler.getDoc project_id, doc_id, {pathname: true}, (error, lines, rev, version, ranges, pathname) -> if error? logger.err err:error, doc_id:doc_id, project_id:project_id, "error finding element for getDocument" return next(error) @@ -20,6 +20,7 @@ module.exports = lines: lines version: version ranges: ranges + pathname: pathname } setDocument: (req, res, next = (error) ->) -> @@ -33,6 +34,3 @@ module.exports = return next(error) logger.log doc_id:doc_id, project_id:project_id, "finished receiving set document request from api (docupdater)" res.sendStatus 200 - - - diff --git a/services/web/app/coffee/Features/Project/ProjectEntityHandler.coffee b/services/web/app/coffee/Features/Project/ProjectEntityHandler.coffee index 870e8a05b2..6489b2bbea 100644 --- a/services/web/app/coffee/Features/Project/ProjectEntityHandler.coffee +++ b/services/web/app/coffee/Features/Project/ProjectEntityHandler.coffee @@ -131,7 +131,7 @@ module.exports = ProjectEntityHandler = setRootDoc: (project_id, newRootDocID, callback = (error) ->)-> logger.log project_id: project_id, rootDocId: newRootDocID, "setting root doc" Project.update {_id:project_id}, {rootDoc_id:newRootDocID}, {}, callback - + unsetRootDoc: (project_id, callback = (error) ->) -> logger.log project_id: project_id, "removing root doc" Project.update {_id:project_id}, {$unset: {rootDoc_id: true}}, {}, callback @@ -140,8 +140,15 @@ module.exports = ProjectEntityHandler = if typeof(options) == "function" callback = options options = {} - DocstoreManager.getDoc project_id, doc_id, options, callback + if options["pathname"] + delete options["pathname"] + projectLocator.findElement {project_id: project_id, element_id: doc_id, type: 'doc'}, (error, doc, path) => + return callback(error) if error? + DocstoreManager.getDoc project_id, doc_id, options, (error, lines, rev, version, ranges) => + callback(error, lines, rev, version, ranges, path.fileSystem) + else + DocstoreManager.getDoc project_id, doc_id, options, callback addDoc: (project_id, folder_id, docName, docLines, callback = (error, doc, folder_id) ->)=> ProjectGetter.getProjectWithOnlyFolders project_id, (err, project) -> @@ -158,7 +165,7 @@ module.exports = ProjectEntityHandler = # Put doc in docstore first, so that if it errors, we don't have a doc_id in the project # which hasn't been created in docstore. DocstoreManager.updateDoc project_id.toString(), doc._id.toString(), docLines, 0, {}, (err, modified, rev) -> - return callback(err) if err? + return callback(err) if err? ProjectEntityHandler._putElement project, folder_id, doc, "doc", (err, result)=> return callback(err) if err? @@ -207,7 +214,7 @@ module.exports = ProjectEntityHandler = replaceFile: (project_id, file_id, fsPath, callback)-> ProjectGetter.getProject project_id, {name:true}, (err, project) -> return callback(err) if err? - findOpts = + findOpts = project_id:project._id element_id:file_id type:"file" @@ -280,7 +287,7 @@ module.exports = ProjectEntityHandler = procesFolder = (previousFolders, folderName, callback)=> previousFolders = previousFolders || [] parentFolder = previousFolders[previousFolders.length-1] - if parentFolder? + if parentFolder? parentFolder_id = parentFolder._id builtUpPath = "#{builtUpPath}/#{folderName}" projectLocator.findElementByPath project, builtUpPath, (err, foundFolder)=> @@ -360,7 +367,7 @@ module.exports = ProjectEntityHandler = return callback(err) if err? projectLocator.findElement {project:project, element_id:entity_id, type:entityType}, (err, entity, path)-> return callback(err) if err? - + if entityType.match(/folder/) ensureFolderIsNotMovedIntoChild = (callback = (error) ->) -> projectLocator.findElement {project: project, element_id: folder_id, type:"folder"}, (err, destEntity, destPath) -> @@ -372,7 +379,7 @@ module.exports = ProjectEntityHandler = callback() else ensureFolderIsNotMovedIntoChild = (callback = () ->) -> callback() - + ensureFolderIsNotMovedIntoChild (error) -> return callback(error) if error? self._removeElementFromMongoArray Project, project_id, path.mongo, (err)-> @@ -382,7 +389,7 @@ module.exports = ProjectEntityHandler = return callback(err) if err? ProjectEntityHandler._putElement project, destinationFolder_id, entity, entityType, (err, result)-> return callback(err) if err? - opts = + opts = project_id:project_id project_name:project.name startPath:path.fileSystem @@ -506,7 +513,7 @@ module.exports = ProjectEntityHandler = _countElements : (project, callback)-> - + countFolder = (folder, cb = (err, count)->)-> jobs = _.map folder?.folders, (folder)-> diff --git a/services/web/test/UnitTests/coffee/Documents/DocumentControllerTests.coffee b/services/web/test/UnitTests/coffee/Documents/DocumentControllerTests.coffee index fedfa1c1b3..1d4cef928f 100644 --- a/services/web/test/UnitTests/coffee/Documents/DocumentControllerTests.coffee +++ b/services/web/test/UnitTests/coffee/Documents/DocumentControllerTests.coffee @@ -24,6 +24,7 @@ describe "DocumentController", -> @doc_lines = ["one", "two", "three"] @version = 42 @ranges = {"mock": "ranges"} + @pathname = '/a/b/c/file.tex' @rev = 5 describe "getDocument", -> @@ -34,12 +35,12 @@ describe "DocumentController", -> describe "when the document exists", -> beforeEach -> - @ProjectEntityHandler.getDoc = sinon.stub().callsArgWith(2, null, @doc_lines, @rev, @version, @ranges) + @ProjectEntityHandler.getDoc = sinon.stub().callsArgWith(3, null, @doc_lines, @rev, @version, @ranges, @pathname) @DocumentController.getDocument(@req, @res, @next) it "should get the document from Mongo", -> @ProjectEntityHandler.getDoc - .calledWith(@project_id, @doc_id) + .calledWith(@project_id, @doc_id, pathname: true) .should.equal true it "should return the document data to the client as JSON", -> @@ -48,10 +49,11 @@ describe "DocumentController", -> lines: @doc_lines version: @version ranges: @ranges + pathname: @pathname describe "when the document doesn't exist", -> beforeEach -> - @ProjectEntityHandler.getDoc = sinon.stub().callsArgWith(2, new Errors.NotFoundError("not found"), null) + @ProjectEntityHandler.getDoc = sinon.stub().callsArgWith(3, new Errors.NotFoundError("not found"), null) @DocumentController.getDocument(@req, @res, @next) it "should call next with the NotFoundError", -> diff --git a/services/web/test/UnitTests/coffee/Project/ProjectEntityHandlerTests.coffee b/services/web/test/UnitTests/coffee/Project/ProjectEntityHandlerTests.coffee index 0ac40cd2c5..0bf3460cbb 100644 --- a/services/web/test/UnitTests/coffee/Project/ProjectEntityHandlerTests.coffee +++ b/services/web/test/UnitTests/coffee/Project/ProjectEntityHandlerTests.coffee @@ -14,17 +14,17 @@ describe 'ProjectEntityHandler', -> doc_id = '4eecb1c1bffa66588e0000a2' folder_id = "4eecaffcbffa66588e000008" rootFolderId = "4eecaffcbffa66588e000007" - + beforeEach -> - @FileStoreHandler = + @FileStoreHandler = uploadFileFromDisk:(project_id, fileRef, localImagePath, callback)->callback() copyFile: sinon.stub().callsArgWith(4, null) @tpdsUpdateSender = addDoc:sinon.stub().callsArg(1) addFile:sinon.stub().callsArg(1) addFolder:sinon.stub().callsArg(1) - @rootFolder = - _id:rootFolderId, + @rootFolder = + _id:rootFolderId, folders:[ {name:"level1", folders:[]} ] @@ -46,7 +46,7 @@ describe 'ProjectEntityHandler', -> @FileModel = class File constructor:(options)-> {@name} = options - @._id = "file_id" + @._id = "file_id" @rev = 0 @FolderModel = class Folder constructor:(options)-> @@ -57,12 +57,12 @@ describe 'ProjectEntityHandler', -> @ProjectModel.findById = (project_id, callback)=> callback(null, @project) @ProjectModel.getProject = (project_id, fields, callback)=> callback(null, @project) - @ProjectGetter = + @ProjectGetter = getProjectWithOnlyFolders : (project_id, callback)=> callback(null, @project) getProjectWithoutDocLines : (project_id, callback)=> callback(null, @project) getProject:sinon.stub() @projectUpdater = markAsUpdated:sinon.stub() - @projectLocator = + @projectLocator = findElement : sinon.stub() @settings = maxEntitiesPerProject:200 @@ -97,8 +97,8 @@ describe 'ProjectEntityHandler', -> else cb null, @parentFolder @ProjectEntityHandler.addFolder = (project_id, parentFolder_id, folderName, callback)=> - callback null, {name:folderName}, @parentFolder_id - + callback null, {name:folderName}, @parentFolder_id + it 'should return the root folder if the path is just a slash', (done)-> path = "/" @ProjectEntityHandler.mkdirp project_id, path, (err, folders, lastFolder)=> @@ -239,7 +239,7 @@ describe 'ProjectEntityHandler', -> @ProjectEntityHandler._putElement = sinon.stub().callsArgWith(4, null, path: @pathAfterMove) @ProjectGetter.getProject.callsArgWith(2, null, @project) @tpdsUpdateSender.moveEntity = sinon.stub().callsArg(1) - + describe "moving a doc", -> beforeEach (done) -> @docId = "4eecaffcbffa66588e000009" @@ -257,10 +257,10 @@ describe 'ProjectEntityHandler', -> it 'should remove the element from its current position', -> @ProjectEntityHandler._removeElementFromMongoArray .calledWith(@ProjectModel, project_id, @path.mongo ).should.equal true - + it "should put the element back in the new folder", -> @ProjectEntityHandler._putElement.calledWith(@project, folder_id, @doc, "docs").should.equal true - + it 'should tell the third party data store', -> @tpdsUpdateSender.moveEntity .calledWith({ @@ -271,7 +271,7 @@ describe 'ProjectEntityHandler', -> rev: @doc.rev }) .should.equal true - + describe "moving a folder", -> beforeEach -> @folder_id = "folder-to-move" @@ -294,7 +294,7 @@ describe 'ProjectEntityHandler', -> else console.log "UNKNOWN ID", options sinon.spy @projectLocator, "findElement" - + describe "when the destination folder is outside the moving folder", -> beforeEach (done) -> @path.fileSystem = "/one/directory" @@ -318,7 +318,7 @@ describe 'ProjectEntityHandler', -> @path.mongo ) .should.equal true - + it "should put the element back in the new folder", -> @ProjectEntityHandler._putElement .calledWith( @@ -328,7 +328,7 @@ describe 'ProjectEntityHandler', -> "folder" ) .should.equal true - + it 'should tell the third party data store', -> @tpdsUpdateSender.moveEntity .calledWith({ @@ -339,7 +339,7 @@ describe 'ProjectEntityHandler', -> rev: @folder.rev }) .should.equal true - + describe "when the destination folder is inside the moving folder", -> beforeEach -> @path.fileSystem = "/one/two" @@ -355,7 +355,7 @@ describe 'ProjectEntityHandler', -> project: @project }) .should.equal true - + it "should return an error", -> @callback .calledWith(new Error("destination folder is a child folder of me")) @@ -385,16 +385,41 @@ describe 'ProjectEntityHandler', -> @rev = 5 @version = 42 @ranges = {"mock": "ranges"} + @DocstoreManager.getDoc = sinon.stub().callsArgWith(3, null, @lines, @rev, @version, @ranges) - @ProjectEntityHandler.getDoc project_id, doc_id, @callback - it "should call the docstore", -> - @DocstoreManager.getDoc - .calledWith(project_id, doc_id) - .should.equal true + describe 'without pathname option', -> + beforeEach -> + @ProjectEntityHandler.getDoc project_id, doc_id, @callback + + it "should call the docstore", -> + @DocstoreManager.getDoc + .calledWith(project_id, doc_id) + .should.equal true + + it "should call the callback with the lines, version and rev", -> + @callback.calledWith(null, @lines, @rev, @version, @ranges).should.equal true + + describe 'with pathname option', -> + beforeEach -> + @project = 'a project' + @path = mongo: "mongo.path", fileSystem: "/file/system/path" + @projectLocator.findElement = sinon.stub().callsArgWith(1, null, {}, @path) + @ProjectEntityHandler.getDoc project_id, doc_id, {pathname: true}, @callback + + it "should call the project locator", -> + @projectLocator.findElement + .calledWith({project_id: project_id, element_id: doc_id, type: 'doc'}) + .should.equal true + + it "should call the docstore", -> + @DocstoreManager.getDoc + .calledWith(project_id, doc_id) + .should.equal true + + it "should return the pathname if option given", -> + @callback.calledWith(null, @lines, @rev, @version, @ranges, @path.fileSystem).should.equal true - it "should call the callback with the lines, version and rev", -> - @callback.calledWith(null, @lines, @rev, @version, @ranges).should.equal true describe 'addDoc', -> beforeEach -> @@ -876,7 +901,7 @@ describe 'ProjectEntityHandler', -> path: path }) .should.equal true - + describe "setRootDoc", -> it "should call Project.update", -> @project_id = "project-id-123234adfs" @@ -907,22 +932,22 @@ describe 'ProjectEntityHandler', -> it 'should copy the file in FileStoreHandler', (done)-> @ProjectEntityHandler._putElement = sinon.stub().callsArgWith(4, null, {path:{fileSystem:"somehintg"}}) - @ProjectEntityHandler.copyFileFromExistingProject project_id, folder_id, oldProject_id, oldFileRef, (err, fileRef, parentFolder)=> + @ProjectEntityHandler.copyFileFromExistingProject project_id, folder_id, oldProject_id, oldFileRef, (err, fileRef, parentFolder)=> @FileStoreHandler.copyFile.calledWith(oldProject_id, oldFileRef._id, project_id, fileRef._id).should.equal true done() it 'should put file into folder by calling put element', (done)-> - @ProjectEntityHandler._putElement = (passedProject, passedFolder_id, passedFileRef, passedType, callback)-> + @ProjectEntityHandler._putElement = (passedProject, passedFolder_id, passedFileRef, passedType, callback)-> passedProject._id.should.equal project_id passedFolder_id.should.equal folder_id passedFileRef.name.should.equal fileName passedType.should.equal 'file' done() - @ProjectEntityHandler.copyFileFromExistingProject project_id, folder_id, oldProject_id, oldFileRef, (err, fileRef, parentFolder)-> + @ProjectEntityHandler.copyFileFromExistingProject project_id, folder_id, oldProject_id, oldFileRef, (err, fileRef, parentFolder)-> it 'should return doc and parent folder', (done)-> - @ProjectEntityHandler.copyFileFromExistingProject project_id, folder_id, oldProject_id, oldFileRef, (err, fileRef, parentFolder)-> + @ProjectEntityHandler.copyFileFromExistingProject project_id, folder_id, oldProject_id, oldFileRef, (err, fileRef, parentFolder)-> parentFolder.should.equal folder_id fileRef.name.should.equal fileName done() @@ -942,7 +967,7 @@ describe 'ProjectEntityHandler', -> options.rev.should.equal 0 done() - @ProjectEntityHandler.copyFileFromExistingProject project_id, folder_id, oldProject_id, oldFileRef, (err, fileRef, parentFolder)-> + @ProjectEntityHandler.copyFileFromExistingProject project_id, folder_id, oldProject_id, oldFileRef, (err, fileRef, parentFolder)-> describe "renameEntity", -> @@ -1054,7 +1079,7 @@ describe 'ProjectEntityHandler', -> @folder = _id: ObjectId() name: "someFolder" - @doc = + @doc = _id: ObjectId() name: "new.tex" @path = mongo: "mongo.path", fileSystem: "/file/system/old.tex" @@ -1064,7 +1089,7 @@ describe 'ProjectEntityHandler', -> describe "updating the project", -> - + it "should use the correct mongo path", (done)-> @ProjectEntityHandler._putElement @project, @folder._id, @doc, "docs", (err)=> @@ -1089,12 +1114,12 @@ describe 'ProjectEntityHandler', -> done() it "should error if the element has no _id", (done)-> - doc = + doc = name:"something" @ProjectEntityHandler._putElement @project, @folder._id, doc, "doc", (err)=> @ProjectModel.update.called.should.equal false done() - + describe "_countElements", -> @@ -1109,7 +1134,7 @@ describe 'ProjectEntityHandler', -> fileRefs:{} folders: [ { - docs:[_id:1234], + docs:[_id:1234], fileRefs:[{_id:23123}, {_id:123213}, {_id:2312}] folders:[ { @@ -1131,7 +1156,7 @@ describe 'ProjectEntityHandler', -> } ] } - ] + ] it "should return the correct number", (done)-> @ProjectEntityHandler._countElements @project, (err, count)-> @@ -1142,19 +1167,19 @@ describe 'ProjectEntityHandler', -> @project.rootFolder[0].folders[0].folders = undefined @ProjectEntityHandler._countElements @project, (err, count)-> count.should.equal 17 - done() + done() it "should deal with null docs", (done)-> @project.rootFolder[0].folders[0].docs = undefined @ProjectEntityHandler._countElements @project, (err, count)-> count.should.equal 23 - done() + done() it "should deal with null fileRefs", (done)-> @project.rootFolder[0].folders[0].folders[0].fileRefs = undefined @ProjectEntityHandler._countElements @project, (err, count)-> count.should.equal 23 - done() + done() From e9114551ba679fb722d50ad643811fc55d5ff297 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 2 Oct 2017 10:11:26 +0100 Subject: [PATCH 084/206] add unit test for precompile resources check --- .../coffee/Compile/ClsiManagerTests.coffee | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/services/web/test/UnitTests/coffee/Compile/ClsiManagerTests.coffee b/services/web/test/UnitTests/coffee/Compile/ClsiManagerTests.coffee index 32deed5522..f02c228ad6 100644 --- a/services/web/test/UnitTests/coffee/Compile/ClsiManagerTests.coffee +++ b/services/web/test/UnitTests/coffee/Compile/ClsiManagerTests.coffee @@ -33,7 +33,7 @@ describe "ClsiManager", -> getProjectDocsIfMatch: sinon.stub().callsArgWith(2,null,null) "./ClsiCookieManager": @ClsiCookieManager "./ClsiStateManager": @ClsiStateManager - "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub(), warn: sinon.stub() } + "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub(), err: sinon.stub(), warn: sinon.stub() } "request": @request = sinon.stub() "./ClsiFormatChecker": @ClsiFormatChecker "metrics-sharelatex": @Metrics = @@ -122,6 +122,21 @@ describe "ClsiManager", -> it "should call the callback with a success status", -> @callback.calledWith(null, @status, ).should.equal true + describe "when the resources fail the precompile check", -> + beforeEach -> + @ClsiFormatChecker.checkRecoursesForProblems = sinon.stub().callsArgWith(1, new Error("failed")) + @ClsiManager._postToClsi = sinon.stub().callsArgWith(4, null, { + compile: + status: @status = "failure" + }) + @ClsiManager.sendRequest @project_id, @user_id, {}, @callback + + it "should call the callback only once", -> + @callback.calledOnce.should.equal true + + it "should call the callback with an error", -> + @callback.calledWithExactly(new Error("failed")).should.equal true + describe "deleteAuxFiles", -> beforeEach -> @ClsiManager._makeRequest = sinon.stub().callsArg(2) From bd005d7bb6993c9f0fd9f16f0d2c5d523abfadc0 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 2 Oct 2017 10:14:52 +0100 Subject: [PATCH 085/206] fix double callback in precompile resources check --- .../Features/Compile/ClsiManager.coffee | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/services/web/app/coffee/Features/Compile/ClsiManager.coffee b/services/web/app/coffee/Features/Compile/ClsiManager.coffee index 46cb4e35e3..ca3cd2b6ab 100755 --- a/services/web/app/coffee/Features/Compile/ClsiManager.coffee +++ b/services/web/app/coffee/Features/Compile/ClsiManager.coffee @@ -38,17 +38,17 @@ module.exports = ClsiManager = if validationProblems? logger.log project_id:project_id, validationProblems:validationProblems, "problems with users latex before compile was attempted" return callback(null, "validation-problems", null, null, validationProblems) - ClsiManager._postToClsi project_id, user_id, req, options.compileGroup, (error, response) -> - if error? - logger.err err:error, project_id:project_id, "error sending request to clsi" - return callback(error) - logger.log project_id: project_id, outputFilesLength: response?.outputFiles?.length, status: response?.status, compile_status: response?.compile?.status, "received compile response from CLSI" - ClsiCookieManager._getServerId project_id, (err, clsiServerId)-> - if err? - logger.err err:err, project_id:project_id, "error getting server id" - return callback(err) - outputFiles = ClsiManager._parseOutputFiles(project_id, response?.compile?.outputFiles) - callback(null, response?.compile?.status, outputFiles, clsiServerId) + ClsiManager._postToClsi project_id, user_id, req, options.compileGroup, (error, response) -> + if error? + logger.err err:error, project_id:project_id, "error sending request to clsi" + return callback(error) + logger.log project_id: project_id, outputFilesLength: response?.outputFiles?.length, status: response?.status, compile_status: response?.compile?.status, "received compile response from CLSI" + ClsiCookieManager._getServerId project_id, (err, clsiServerId)-> + if err? + logger.err err:err, project_id:project_id, "error getting server id" + return callback(err) + outputFiles = ClsiManager._parseOutputFiles(project_id, response?.compile?.outputFiles) + callback(null, response?.compile?.status, outputFiles, clsiServerId) stopCompile: (project_id, user_id, options, callback = (error) ->) -> compilerUrl = @_getCompilerUrl(options?.compileGroup, project_id, user_id, "compile/stop") From a69afae03dd54ce331ec23f9bbf7592cc05992d1 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Mon, 2 Oct 2017 13:49:28 +0100 Subject: [PATCH 086/206] Remove unnecessary arrow elem --- .../web/app/views/project/editor/editor.pug | 1 - .../app/editor/feature-onboarding.less | 44 ++++++++++++------- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/services/web/app/views/project/editor/editor.pug b/services/web/app/views/project/editor/editor.pug index 66bff761a1..86cc63a5e5 100644 --- a/services/web/app/views/project/editor/editor.pug +++ b/services/web/app/views/project/editor/editor.pug @@ -100,7 +100,6 @@ div.full-size( ng-if="onboarding.autoCompile == 'show'" ng-class="placement" ) - .arrow .popover-inner h3.popover-title #{translate("auto_compile")} .popover-content diff --git a/services/web/public/stylesheets/app/editor/feature-onboarding.less b/services/web/public/stylesheets/app/editor/feature-onboarding.less index ef89a93df4..475658f885 100644 --- a/services/web/public/stylesheets/app/editor/feature-onboarding.less +++ b/services/web/public/stylesheets/app/editor/feature-onboarding.less @@ -104,28 +104,42 @@ a.feat-onboard-dismiss { display: block; top: 10px; - .arrow { - top: 17px !important; - } - img { margin-bottom: 10px; border: 1px solid @gray-lighter; } - &.right > .arrow { - left: -10px !important; - - &:after { - border-right-color: #f7f7f7 !important; - } + &::before, &::after { + content: ''; + border-width: 11px; + border-style: solid; + border-color: transparent; + top: 7px; + display: block; + position: absolute; } - &.left > .arrow { - right: -10px !important; + &.right::before { + border-left-width: 0; + border-right-color: rgba(0, 0, 0, .3); + left: -11px; + } - &:after { - border-left-color: #f7f7f7 !important; - } + &.right::after { + border-left-width: 0; + border-right-color: #f7f7f7; + left: -9.5px; + } + + &.left::before { + border-right-width: 0; + border-left-color: rgba(0, 0, 0, .3); + right: -11px + } + + &.left::after { + border-right-width: 0; + border-left-color: #f7f7f7; + right: -9.5px; } } From a3800a321be6d6a5a93d972d208f706e81b4ee55 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Mon, 2 Oct 2017 14:12:18 +0100 Subject: [PATCH 087/206] Add beta program badge on autocompile popover & setting --- services/web/app/views/project/editor/editor.pug | 4 +++- services/web/app/views/project/editor/pdf.pug | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/services/web/app/views/project/editor/editor.pug b/services/web/app/views/project/editor/editor.pug index 86cc63a5e5..89ec5c7828 100644 --- a/services/web/app/views/project/editor/editor.pug +++ b/services/web/app/views/project/editor/editor.pug @@ -101,7 +101,9 @@ div.full-size( ng-class="placement" ) .popover-inner - h3.popover-title #{translate("auto_compile")} + h3.popover-title + | #{translate("auto_compile")} + span.beta-feature-badge .popover-content p #{translate("try_out_auto_compile_setting")} img(src="/img/onboarding/autocompile/setting-dropdown.png" width="100%") diff --git a/services/web/app/views/project/editor/pdf.pug b/services/web/app/views/project/editor/pdf.pug index 536eefeb5b..99369eb4c5 100644 --- a/services/web/app/views/project/editor/pdf.pug +++ b/services/web/app/views/project/editor/pdf.pug @@ -28,7 +28,9 @@ div.full-size.pdf(ng-controller="PdfController") ul.dropdown-menu.dropdown-menu-left // Only show on beta program? if user.betaProgram - li.dropdown-header #{translate("auto_compile")} + li.dropdown-header + | #{translate("auto_compile")} + span.beta-feature-badge li a(href, ng-click="autocompile_enabled = true") i.fa.fa-fw(ng-class="{'fa-check': autocompile_enabled}") From 17ea9d9799b4eff6c40bb9e205576bf305ee3f7f Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Mon, 2 Oct 2017 17:01:51 +0100 Subject: [PATCH 088/206] Style project list tags. --- services/web/public/stylesheets/app/project-list.less | 6 ++++++ services/web/public/stylesheets/core/_common-variables.less | 4 ++++ services/web/public/stylesheets/core/ol-variables.less | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less index b210b93477..e5f4a9caff 100644 --- a/services/web/public/stylesheets/app/project-list.less +++ b/services/web/public/stylesheets/app/project-list.less @@ -294,6 +294,12 @@ ul.project-list { display: inline-block; padding-top: 0.3em; color: #FFF; + border-radius: @tag-border-radius; + background-color: @tag-bg-color; + &:hover, + &:focus { + background-color: @tag-bg-hover-color; + } } .tag-label-name { padding-right: 0.3em; diff --git a/services/web/public/stylesheets/core/_common-variables.less b/services/web/public/stylesheets/core/_common-variables.less index 83fa3e3e55..75a57c5e7f 100644 --- a/services/web/public/stylesheets/core/_common-variables.less +++ b/services/web/public/stylesheets/core/_common-variables.less @@ -828,6 +828,10 @@ @btn-border-width: 1px; @btn-border-bottom-width: 2px; +@tag-border-radius: 0.25em; +@tag-bg-color: inherit; +@tag-bg-hover-color: inherit; + @left-menu-width: 260px; @left-menu-animation-duration: 0.35s; diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index 484e851179..26c975b277 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -56,6 +56,11 @@ @btn-info-bg : @ol-blue; @btn-info-border : transparent; +// Tags +@tag-border-radius : 999px; +@tag-bg-color : @ol-green; +@tag-bg-hover-color : @ol-dark-green; + // Navbar @navbar-default-padding : (@grid-gutter-width / 2) 0; @navbar-default-link-color : #FFF; From 7d5785b98f089a1828f270de0732c89cfa3ed078 Mon Sep 17 00:00:00 2001 From: Nate Stemen Date: Mon, 2 Oct 2017 18:10:53 +0100 Subject: [PATCH 089/206] adding top 100 packages and package tracking --- .../auto-complete/AutoCompleteManager.coffee | 13 +++-- .../auto-complete/CommandManager.coffee | 22 ++++---- .../auto-complete/PackageManager.coffee | 52 +++++++++++++++++++ 3 files changed, 71 insertions(+), 16 deletions(-) create mode 100644 services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/PackageManager.coffee diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee index 24a524b4b6..60241da990 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee @@ -1,10 +1,11 @@ define [ "ide/editor/directives/aceEditor/auto-complete/CommandManager" "ide/editor/directives/aceEditor/auto-complete/EnvironmentManager" + "ide/editor/directives/aceEditor/auto-complete/PackageManager" "ide/editor/directives/aceEditor/auto-complete/Helpers" "ace/ace" "ace/ext-language_tools" -], (CommandManager, EnvironmentManager, Helpers) -> +], (CommandManager, EnvironmentManager, PackageManager, Helpers) -> Range = ace.require("ace/range").Range aceSnippetManager = ace.require('ace/snippets').snippetManager @@ -35,6 +36,7 @@ define [ }) SnippetCompleter = new EnvironmentManager() + PackageCompleter = new PackageManager() Graphics = @graphics Preamble = @preamble @@ -126,10 +128,11 @@ define [ callback null, result @editor.completers = [ - @suggestionManager, - SnippetCompleter, - ReferencesCompleter, - LabelsCompleter, + @suggestionManager + SnippetCompleter + PackageCompleter + ReferencesCompleter + LabelsCompleter GraphicsCompleter ] diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/CommandManager.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/CommandManager.coffee index fd6cdf7e64..c513f6854d 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/CommandManager.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/CommandManager.coffee @@ -63,19 +63,19 @@ define [], () -> meta: "cmd" } special = for cmd in special - { - caption: "\\#{cmd}{}" - snippet: "\\#{cmd}{}" - meta: "cmd" - } + { + caption: "\\#{cmd}{}" + snippet: "\\#{cmd}{}" + meta: "cmd" + } staticCommands = [].concat( - noArgumentCommands, - singleArgumentCommands, - doubleArgumentCommands, - tripleArgumentCommands, - special - ) + noArgumentCommands, + singleArgumentCommands, + doubleArgumentCommands, + tripleArgumentCommands, + special + ) class Parser constructor: (@doc, @prefix) -> diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/PackageManager.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/PackageManager.coffee new file mode 100644 index 0000000000..af8cac861a --- /dev/null +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/PackageManager.coffee @@ -0,0 +1,52 @@ +define () -> + packages = [ + 'inputenc', 'graphicx', 'amsmath', 'geometry', 'amssymb', 'hyperref', + 'babel', 'color', 'xcolor', 'url', 'natbib', 'fontenc', 'fancyhdr', + 'amsfonts', 'booktabs', 'amsthm', 'float', 'tikz', 'caption', + 'setspace', 'multirow', 'array', 'multicol', 'titlesec', 'enumitem', + 'ifthen', 'listings', 'blindtext', 'subcaption', 'times', 'bm', + 'subfigure', 'algorithm', 'fontspec', 'biblatex', 'tabularx', + 'microtype', 'etoolbox', 'parskip', 'calc', 'verbatim', 'mathtools', + 'epsfig', 'wrapfig', 'lipsum', 'cite', 'textcomp', 'longtable', + 'textpos', 'algpseudocode', 'enumerate', 'subfig', 'pdfpages', + 'epstopdf', 'latexsym', 'lmodern', 'pifont', 'ragged2e', 'rotating', + 'dcolumn', 'xltxtra', 'marvosym', 'indentfirst', 'xspace', 'csquotes', + 'xparse', 'changepage', 'soul', 'xunicode', 'comment', 'mathrsfs', + 'tocbibind', 'lastpage', 'algorithm2e', 'pgfplots', 'lineno', + 'graphics', 'algorithmic', 'fullpage', 'mathptmx', 'todonotes', + 'ulem', 'tweaklist', 'moderncvstyleclassic', 'collection', + 'moderncvcompatibility', 'gensymb', 'helvet', 'siunitx', 'adjustbox', + 'placeins', 'colortbl', 'appendix', 'makeidx', 'supertabular', 'ifpdf', + 'framed', 'aliascnt', 'layaureo', 'authblk' + ] + + packageSnippets = for pkg in packages + { + caption: "\\usepackage{#{pkg}}" + snippet: """\\usepackage{#{pkg}}""" + meta: "pkg" + } + + parseLoadedPackages = (text) -> + re = /^\\usepackage(?:\[(.*?)])?.*?{((?:.|\n)*?)}/gm + result = [] + iterations = 0 + while match = re.exec(text) + if match[2] + for pkg in match[2].split(",") + cleaned = pkg.replace(/%.*\n/gm,'').trim() + if cleaned not in result + result.push cleaned + iterations += 1 + if iterations >= 1000 + return result + return result + + class PackageManager + getCompletions: (editor, session, pos, prefix, callback) -> + docText = session.getValue() + loaded = parseLoadedPackages(docText) + # console.log loaded + callback null, packageSnippets + + return PackageManager From 38b8450cee2c297136abc0b6bdf06c311fd6a1af Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 3 Oct 2017 10:29:49 +0100 Subject: [PATCH 090/206] Add jsonwebtoken package --- services/web/npm-shrinkwrap.json | 1216 +++++++++++++++++++++++++++++- services/web/package.json | 1 + 2 files changed, 1211 insertions(+), 6 deletions(-) diff --git a/services/web/npm-shrinkwrap.json b/services/web/npm-shrinkwrap.json index bfb59b91cc..5c26a8431e 100644 --- a/services/web/npm-shrinkwrap.json +++ b/services/web/npm-shrinkwrap.json @@ -59,6 +59,12 @@ "from": "ansi-regex@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" }, + "ansi-styles": { + "version": "2.2.1", + "from": "ansi-styles@>=2.2.1 <3.0.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "dev": true + }, "aproba": { "version": "1.1.2", "from": "aproba@>=1.0.3 <2.0.0", @@ -150,6 +156,12 @@ "from": "asynckit@>=0.4.0 <0.5.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" }, + "autoprefixer": { + "version": "6.7.7", + "from": "autoprefixer@>=6.6.1 <7.0.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", + "dev": true + }, "aws-sdk": { "version": "2.113.0", "from": "aws-sdk@>=2.2.36 <3.0.0", @@ -224,6 +236,11 @@ } } }, + "base64url": { + "version": "2.0.0", + "from": "base64url@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz" + }, "basic-auth-connect": { "version": "1.0.0", "from": "basic-auth-connect@>=1.0.0 <2.0.0", @@ -297,6 +314,38 @@ "from": "brace-expansion@>=1.1.7 <2.0.0", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz" }, + "broadway": { + "version": "0.3.6", + "from": "broadway@>=0.3.2 <0.4.0", + "resolved": "https://registry.npmjs.org/broadway/-/broadway-0.3.6.tgz", + "dev": true, + "dependencies": { + "async": { + "version": "0.2.10", + "from": "async@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "dev": true + }, + "cliff": { + "version": "0.1.9", + "from": "cliff@0.1.9", + "resolved": "https://registry.npmjs.org/cliff/-/cliff-0.1.9.tgz", + "dev": true + }, + "winston": { + "version": "0.8.0", + "from": "winston@0.8.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-0.8.0.tgz", + "dev": true + } + } + }, + "browserslist": { + "version": "1.7.7", + "from": "browserslist@>=1.7.6 <2.0.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "dev": true + }, "bson": { "version": "1.0.4", "from": "bson@>=1.0.4 <1.1.0", @@ -319,6 +368,11 @@ "from": "buffer-crc32@>=0.2.1 <0.3.0", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz" }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "from": "buffer-equal-constant-time@1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz" + }, "buffer-shims": { "version": "1.0.0", "from": "buffer-shims@>=1.0.0 <2.0.0", @@ -373,6 +427,12 @@ "from": "camelize@1.0.0", "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz" }, + "caniuse-db": { + "version": "1.0.30000740", + "from": "caniuse-db@>=1.0.30000634 <2.0.0", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000740.tgz", + "dev": true + }, "caseless": { "version": "0.12.0", "from": "caseless@>=0.12.0 <0.13.0", @@ -383,11 +443,31 @@ "from": "center-align@>=0.1.1 <0.2.0", "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz" }, + "chai": { + "version": "3.5.0", + "from": "chai@3.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", + "dev": true + }, "chai-spies": { "version": "0.7.1", "from": "chai-spies@latest", "resolved": "https://registry.npmjs.org/chai-spies/-/chai-spies-0.7.1.tgz" }, + "chalk": { + "version": "1.1.3", + "from": "chalk@>=1.1.3 <2.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "dev": true, + "dependencies": { + "supports-color": { + "version": "2.0.0", + "from": "supports-color@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "dev": true + } + } + }, "character-parser": { "version": "1.2.0", "from": "character-parser@1.2.0", @@ -415,6 +495,20 @@ } } }, + "cliff": { + "version": "0.1.10", + "from": "cliff@>=0.1.9 <0.2.0", + "resolved": "https://registry.npmjs.org/cliff/-/cliff-0.1.10.tgz", + "dev": true, + "dependencies": { + "colors": { + "version": "1.0.3", + "from": "colors@>=1.0.3 <1.1.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "dev": true + } + } + }, "cliui": { "version": "2.1.0", "from": "cliui@>=2.1.0 <3.0.0", @@ -568,6 +662,12 @@ "from": "cookie-signature@1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" }, + "cookies": { + "version": "0.7.1", + "from": "cookies@>=0.2.2", + "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.7.1.tgz", + "dev": true + }, "core-js": { "version": "1.2.7", "from": "core-js@>=1.2.0 <2.0.0", @@ -640,6 +740,19 @@ } } }, + "ctype": { + "version": "0.5.3", + "from": "ctype@0.5.3", + "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz", + "dev": true, + "optional": true + }, + "cycle": { + "version": "1.0.3", + "from": "cycle@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", + "dev": true + }, "dashdash": { "version": "1.14.1", "from": "dashdash@>=1.12.0 <2.0.0", @@ -677,6 +790,20 @@ "from": "decamelize@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" }, + "deep-eql": { + "version": "0.1.3", + "from": "deep-eql@>=0.1.3 <0.2.0", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "dev": true, + "dependencies": { + "type-detect": { + "version": "0.1.1", + "from": "type-detect@0.1.1", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", + "dev": true + } + } + }, "deep-equal": { "version": "1.0.1", "from": "deep-equal@>=1.0.1 <1.1.0", @@ -739,6 +866,12 @@ "from": "diff@1.0.7", "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz" }, + "director": { + "version": "1.2.7", + "from": "director@1.2.7", + "resolved": "https://registry.npmjs.org/director/-/director-1.2.7.tgz", + "dev": true + }, "dns-prefetch-control": { "version": "0.1.0", "from": "dns-prefetch-control@0.1.0", @@ -749,6 +882,38 @@ "from": "doctypes@>=1.1.0 <2.0.0", "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz" }, + "dom-serializer": { + "version": "0.1.0", + "from": "dom-serializer@>=0.0.0 <1.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "dev": true, + "dependencies": { + "domelementtype": { + "version": "1.1.3", + "from": "domelementtype@>=1.1.1 <1.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "dev": true + } + } + }, + "domelementtype": { + "version": "1.3.0", + "from": "domelementtype@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", + "dev": true + }, + "domhandler": { + "version": "2.4.1", + "from": "domhandler@>=2.3.0 <3.0.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.1.tgz", + "dev": true + }, + "domutils": { + "version": "1.6.2", + "from": "domutils@>=1.5.1 <2.0.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.6.2.tgz", + "dev": true + }, "dont-sniff-mimetype": { "version": "1.0.0", "from": "dont-sniff-mimetype@1.0.0", @@ -764,12 +929,6 @@ "from": "double-ended-queue@>=2.1.0-0 <3.0.0", "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz" }, - "dtrace-provider": { - "version": "0.2.8", - "from": "dtrace-provider@0.2.8", - "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.2.8.tgz", - "optional": true - }, "each-series": { "version": "1.0.0", "from": "each-series@>=1.0.0 <2.0.0", @@ -781,6 +940,11 @@ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", "optional": true }, + "ecdsa-sig-formatter": { + "version": "1.0.9", + "from": "ecdsa-sig-formatter@1.0.9", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz" + }, "ee-first": { "version": "1.1.1", "from": "ee-first@1.1.1", @@ -791,6 +955,12 @@ "from": "ejs@>=0.8.3 <0.9.0", "resolved": "https://registry.npmjs.org/ejs/-/ejs-0.8.8.tgz" }, + "electron-to-chromium": { + "version": "1.3.24", + "from": "electron-to-chromium@>=1.2.7 <2.0.0", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.24.tgz", + "dev": true + }, "encodeurl": { "version": "1.0.1", "from": "encodeurl@>=1.0.1 <1.1.0", @@ -813,6 +983,12 @@ "from": "end-of-stream@>=0.1.3 <0.2.0", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-0.1.5.tgz" }, + "entities": { + "version": "1.1.1", + "from": "entities@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", + "dev": true + }, "es-abstract": { "version": "1.8.2", "from": "es-abstract@>=1.5.0 <2.0.0", @@ -823,6 +999,12 @@ "from": "es-to-primitive@>=1.1.1 <2.0.0", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz" }, + "es6-promise": { + "version": "4.1.1", + "from": "es6-promise@>=4.0.5 <5.0.0", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.1.1.tgz", + "dev": true + }, "escape-html": { "version": "1.0.2", "from": "escape-html@1.0.2", @@ -843,6 +1025,20 @@ "from": "etag@>=1.7.0 <1.8.0", "resolved": "https://registry.npmjs.org/etag/-/etag-1.7.0.tgz" }, + "event-stream": { + "version": "0.5.3", + "from": "event-stream@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-0.5.3.tgz", + "dev": true, + "dependencies": { + "optimist": { + "version": "0.2.8", + "from": "optimist@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.2.8.tgz", + "dev": true + } + } + }, "eventemitter2": { "version": "0.4.14", "from": "eventemitter2@>=0.4.13 <0.5.0", @@ -932,11 +1128,23 @@ "from": "extsprintf@1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz" }, + "eyes": { + "version": "0.1.8", + "from": "eyes@>=0.1.8 <0.2.0", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "dev": true + }, "failure": { "version": "1.1.1", "from": "failure@>=1.1.0 <1.2.0", "resolved": "https://registry.npmjs.org/failure/-/failure-1.1.1.tgz" }, + "faye-websocket": { + "version": "0.10.0", + "from": "faye-websocket@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "dev": true + }, "file-utils": { "version": "0.1.5", "from": "file-utils@>=0.1.5 <0.2.0", @@ -978,6 +1186,20 @@ } } }, + "flatiron": { + "version": "0.4.3", + "from": "flatiron@>=0.4.2 <0.5.0", + "resolved": "https://registry.npmjs.org/flatiron/-/flatiron-0.4.3.tgz", + "dev": true, + "dependencies": { + "optimist": { + "version": "0.6.0", + "from": "optimist@0.6.0", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.0.tgz", + "dev": true + } + } + }, "flexbuffer": { "version": "0.0.6", "from": "flexbuffer@0.0.6", @@ -1005,16 +1227,42 @@ "from": "foreach@>=2.0.5 <3.0.0", "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz" }, + "forever": { + "version": "0.14.2", + "from": "forever@>=0.14.1 <0.15.0", + "resolved": "https://registry.npmjs.org/forever/-/forever-0.14.2.tgz", + "dev": true + }, "forever-agent": { "version": "0.6.1", "from": "forever-agent@>=0.6.1 <0.7.0", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" }, + "forever-monitor": { + "version": "1.5.2", + "from": "forever-monitor@>=1.5.1 <1.6.0", + "resolved": "https://registry.npmjs.org/forever-monitor/-/forever-monitor-1.5.2.tgz", + "dev": true, + "dependencies": { + "minimatch": { + "version": "1.0.0", + "from": "minimatch@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-1.0.0.tgz", + "dev": true + } + } + }, "form-data": { "version": "2.1.4", "from": "form-data@>=2.1.1 <2.2.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz" }, + "formatio": { + "version": "1.1.1", + "from": "formatio@1.1.1", + "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.1.1.tgz", + "dev": true + }, "forwarded": { "version": "0.1.1", "from": "forwarded@>=0.1.0 <0.2.0", @@ -1089,6 +1337,12 @@ "from": "gauge@>=2.7.3 <2.8.0", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz" }, + "gaze": { + "version": "1.1.2", + "from": "gaze@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.2.tgz", + "dev": true + }, "generic-pool": { "version": "2.4.2", "from": "generic-pool@2.4.2", @@ -1128,6 +1382,26 @@ } } }, + "globule": { + "version": "1.2.0", + "from": "globule@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz", + "dev": true, + "dependencies": { + "glob": { + "version": "7.1.2", + "from": "glob@>=7.1.1 <7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "from": "minimatch@>=3.0.2 <3.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "dev": true + } + } + }, "graceful-fs": { "version": "4.1.11", "from": "graceful-fs@>=4.1.2 <5.0.0", @@ -1195,6 +1469,26 @@ } } }, + "grunt-available-tasks": { + "version": "0.4.1", + "from": "grunt-available-tasks@0.4.1", + "resolved": "https://registry.npmjs.org/grunt-available-tasks/-/grunt-available-tasks-0.4.1.tgz", + "dev": true, + "dependencies": { + "lodash": { + "version": "2.4.2", + "from": "lodash@>=2.4.0 <2.5.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "dev": true + }, + "underscore.string": { + "version": "2.3.3", + "from": "underscore.string@>=2.3.3 <2.4.0", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", + "dev": true + } + } + }, "grunt-bunyan": { "version": "0.5.0", "from": "grunt-bunyan@>=0.5.0 <0.6.0", @@ -1207,11 +1501,151 @@ } } }, + "grunt-contrib-clean": { + "version": "0.5.0", + "from": "grunt-contrib-clean@0.5.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-0.5.0.tgz", + "dev": true + }, + "grunt-contrib-coffee": { + "version": "0.10.0", + "from": "grunt-contrib-coffee@0.10.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.10.0.tgz", + "dev": true, + "dependencies": { + "ansi-styles": { + "version": "1.0.0", + "from": "ansi-styles@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz", + "dev": true + }, + "chalk": { + "version": "0.4.0", + "from": "chalk@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz", + "dev": true + }, + "coffee-script": { + "version": "1.7.1", + "from": "coffee-script@>=1.7.0 <1.8.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz", + "dev": true + }, + "lodash": { + "version": "2.4.2", + "from": "lodash@~2.4.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "dev": true + }, + "mkdirp": { + "version": "0.3.5", + "from": "mkdirp@>=0.3.5 <0.4.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "dev": true + }, + "strip-ansi": { + "version": "0.1.1", + "from": "strip-ansi@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz", + "dev": true + } + } + }, + "grunt-contrib-less": { + "version": "0.9.0", + "from": "grunt-contrib-less@0.9.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-less/-/grunt-contrib-less-0.9.0.tgz", + "dev": true, + "dependencies": { + "ansi-styles": { + "version": "1.0.0", + "from": "ansi-styles@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz", + "dev": true + }, + "chalk": { + "version": "0.4.0", + "from": "chalk@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz", + "dev": true + }, + "strip-ansi": { + "version": "0.1.1", + "from": "strip-ansi@~0.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz", + "dev": true + } + } + }, + "grunt-contrib-requirejs": { + "version": "0.4.1", + "from": "grunt-contrib-requirejs@0.4.1", + "resolved": "https://registry.npmjs.org/grunt-contrib-requirejs/-/grunt-contrib-requirejs-0.4.1.tgz", + "dev": true + }, + "grunt-contrib-watch": { + "version": "1.0.0", + "from": "grunt-contrib-watch@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-1.0.0.tgz", + "dev": true, + "dependencies": { + "async": { + "version": "1.5.2", + "from": "async@>=1.5.0 <2.0.0", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "dev": true + }, + "lodash": { + "version": "3.10.1", + "from": "lodash@>=3.10.1 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "dev": true + } + } + }, + "grunt-env": { + "version": "0.4.4", + "from": "grunt-env@0.4.4", + "resolved": "https://registry.npmjs.org/grunt-env/-/grunt-env-0.4.4.tgz", + "dev": true, + "dependencies": { + "lodash": { + "version": "2.4.2", + "from": "lodash@~2.4.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "dev": true + } + } + }, + "grunt-exec": { + "version": "0.4.7", + "from": "grunt-exec@>=0.4.7 <0.5.0", + "resolved": "https://registry.npmjs.org/grunt-exec/-/grunt-exec-0.4.7.tgz", + "dev": true + }, "grunt-execute": { "version": "0.2.2", "from": "grunt-execute@>=0.2.2 <0.3.0", "resolved": "https://registry.npmjs.org/grunt-execute/-/grunt-execute-0.2.2.tgz" }, + "grunt-file-append": { + "version": "0.0.6", + "from": "grunt-file-append@0.0.6", + "resolved": "https://registry.npmjs.org/grunt-file-append/-/grunt-file-append-0.0.6.tgz", + "dev": true + }, + "grunt-forever": { + "version": "0.4.7", + "from": "grunt-forever@>=0.4.7 <0.5.0", + "resolved": "https://registry.npmjs.org/grunt-forever/-/grunt-forever-0.4.7.tgz", + "dev": true + }, + "grunt-git-rev-parse": { + "version": "0.1.5", + "from": "grunt-git-rev-parse@>=0.1.4 <0.2.0", + "resolved": "https://registry.npmjs.org/grunt-git-rev-parse/-/grunt-git-rev-parse-0.1.5.tgz", + "dev": true + }, "grunt-legacy-log": { "version": "0.1.3", "from": "grunt-legacy-log@>=0.1.0 <0.2.0", @@ -1263,6 +1697,90 @@ } } }, + "grunt-lib-contrib": { + "version": "0.6.1", + "from": "grunt-lib-contrib@>=0.6.1 <0.7.0", + "resolved": "https://registry.npmjs.org/grunt-lib-contrib/-/grunt-lib-contrib-0.6.1.tgz", + "dev": true + }, + "grunt-mocha-test": { + "version": "0.9.0", + "from": "grunt-mocha-test@0.9.0", + "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.9.0.tgz", + "dev": true + }, + "grunt-newer": { + "version": "1.3.0", + "from": "grunt-newer@>=1.2.0 <2.0.0", + "resolved": "https://registry.npmjs.org/grunt-newer/-/grunt-newer-1.3.0.tgz", + "dev": true, + "dependencies": { + "async": { + "version": "1.5.2", + "from": "async@^1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "dev": true + }, + "glob": { + "version": "7.1.2", + "from": "glob@^7.0.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "from": "minimatch@^3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "dev": true + }, + "rimraf": { + "version": "2.6.2", + "from": "rimraf@>=2.5.2 <3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "dev": true + } + } + }, + "grunt-parallel": { + "version": "0.5.1", + "from": "grunt-parallel@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/grunt-parallel/-/grunt-parallel-0.5.1.tgz", + "dev": true, + "dependencies": { + "q": { + "version": "0.8.12", + "from": "q@>=0.8.12 <0.9.0", + "resolved": "https://registry.npmjs.org/q/-/q-0.8.12.tgz", + "dev": true + } + } + }, + "grunt-postcss": { + "version": "0.8.0", + "from": "grunt-postcss@>=0.8.0 <0.9.0", + "resolved": "https://registry.npmjs.org/grunt-postcss/-/grunt-postcss-0.8.0.tgz", + "dev": true, + "dependencies": { + "diff": { + "version": "2.2.3", + "from": "diff@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-2.2.3.tgz", + "dev": true + } + } + }, + "grunt-sed": { + "version": "0.1.1", + "from": "grunt-sed@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/grunt-sed/-/grunt-sed-0.1.1.tgz", + "dev": true + }, + "grunt-shell": { + "version": "2.1.0", + "from": "grunt-shell@>=2.1.0 <3.0.0", + "resolved": "https://registry.npmjs.org/grunt-shell/-/grunt-shell-2.1.0.tgz", + "dev": true + }, "hang": { "version": "1.0.0", "from": "hang@>=1.0.0 <1.1.0", @@ -1283,6 +1801,24 @@ "from": "has@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz" }, + "has-ansi": { + "version": "2.0.0", + "from": "has-ansi@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "dev": true + }, + "has-color": { + "version": "0.1.7", + "from": "has-color@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz", + "dev": true + }, + "has-flag": { + "version": "1.0.0", + "from": "has-flag@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "dev": true + }, "has-unicode": { "version": "2.0.1", "from": "has-unicode@>=2.0.0 <3.0.0", @@ -1338,11 +1874,43 @@ "from": "hsts@2.1.0", "resolved": "https://registry.npmjs.org/hsts/-/hsts-2.1.0.tgz" }, + "htmlparser2": { + "version": "3.9.2", + "from": "htmlparser2@>=3.9.0 <4.0.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", + "dev": true, + "dependencies": { + "isarray": { + "version": "1.0.0", + "from": "isarray@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "dev": true + }, + "readable-stream": { + "version": "2.3.3", + "from": "readable-stream@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "dev": true + }, + "string_decoder": { + "version": "1.0.3", + "from": "string_decoder@>=1.0.3 <1.1.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "dev": true + } + } + }, "http-errors": { "version": "1.6.2", "from": "http-errors@>=1.6.2 <1.7.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz" }, + "http-parser-js": { + "version": "0.4.8", + "from": "http-parser-js@>=0.4.0", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.8.tgz", + "dev": true + }, "http-proxy": { "version": "1.16.2", "from": "http-proxy@>=1.8.1 <2.0.0", @@ -1353,6 +1921,18 @@ "from": "http-signature@>=1.1.0 <1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz" }, + "i": { + "version": "0.3.5", + "from": "i@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/i/-/i-0.3.5.tgz", + "dev": true + }, + "i18next": { + "version": "1.7.10", + "from": "i18next@>=1.7.1 <1.8.0", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-1.7.10.tgz", + "dev": true + }, "iconv-lite": { "version": "0.2.11", "from": "iconv-lite@>=0.2.11 <0.3.0", @@ -1492,6 +2072,12 @@ "from": "jmespath@0.15.0", "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz" }, + "js-base64": { + "version": "2.3.2", + "from": "js-base64@>=2.1.9 <3.0.0", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.3.2.tgz", + "dev": true + }, "js-stringify": { "version": "1.0.2", "from": "js-stringify@>=1.0.1 <2.0.0", @@ -1523,6 +2109,12 @@ "from": "json-stringify-safe@>=5.0.1 <5.1.0", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" }, + "json5": { + "version": "0.2.0", + "from": "json5@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.2.0.tgz", + "dev": true + }, "jsonfile": { "version": "1.1.1", "from": "jsonfile@>=1.1.0 <1.2.0", @@ -1533,6 +2125,11 @@ "from": "jsonify@>=0.0.0 <0.1.0", "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" }, + "jsonwebtoken": { + "version": "8.0.1", + "from": "jsonwebtoken@latest", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.0.1.tgz" + }, "jsprim": { "version": "1.4.1", "from": "jsprim@>=1.2.2 <2.0.0", @@ -1567,16 +2164,38 @@ "from": "just-extend@>=1.1.22 <2.0.0", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-1.1.22.tgz" }, + "jwa": { + "version": "1.1.5", + "from": "jwa@>=1.1.4 <2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.5.tgz" + }, + "jws": { + "version": "3.1.4", + "from": "jws@>=3.1.4 <4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.4.tgz" + }, "kareem": { "version": "1.5.0", "from": "kareem@1.5.0", "resolved": "https://registry.npmjs.org/kareem/-/kareem-1.5.0.tgz" }, + "keygrip": { + "version": "1.0.2", + "from": "keygrip@>=1.0.2 <1.1.0", + "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.0.2.tgz", + "dev": true + }, "kind-of": { "version": "3.2.2", "from": "kind-of@>=3.0.2 <4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz" }, + "lazy": { + "version": "1.0.11", + "from": "lazy@>=1.0.11 <1.1.0", + "resolved": "https://registry.npmjs.org/lazy/-/lazy-1.0.11.tgz", + "dev": true + }, "lazy-cache": { "version": "1.0.4", "from": "lazy-cache@>=1.0.3 <2.0.0", @@ -1708,6 +2327,42 @@ } } }, + "less": { + "version": "1.6.3", + "from": "less@>=1.6.0 <1.7.0", + "resolved": "https://registry.npmjs.org/less/-/less-1.6.3.tgz", + "dev": true, + "dependencies": { + "clean-css": { + "version": "2.0.8", + "from": "clean-css@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-2.0.8.tgz", + "dev": true, + "optional": true + }, + "commander": { + "version": "2.0.0", + "from": "commander@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz", + "dev": true, + "optional": true + }, + "mime": { + "version": "1.2.11", + "from": "mime@>=1.2.0 <1.3.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", + "dev": true, + "optional": true + }, + "mkdirp": { + "version": "0.3.5", + "from": "mkdirp@~0.3.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "dev": true, + "optional": true + } + } + }, "libbase64": { "version": "0.1.0", "from": "libbase64@0.1.0", @@ -1730,6 +2385,12 @@ "from": "libqp@1.1.0", "resolved": "https://registry.npmjs.org/libqp/-/libqp-1.1.0.tgz" }, + "livereload-js": { + "version": "2.2.2", + "from": "livereload-js@>=2.2.0 <3.0.0", + "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.2.2.tgz", + "dev": true + }, "loads": { "version": "0.0.4", "from": "loads@>=0.0.0 <0.1.0", @@ -1740,6 +2401,41 @@ "from": "lodash@>=4.13.1 <5.0.0", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" }, + "lodash.includes": { + "version": "4.3.0", + "from": "lodash.includes@>=4.3.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz" + }, + "lodash.isboolean": { + "version": "3.0.3", + "from": "lodash.isboolean@>=3.0.3 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz" + }, + "lodash.isinteger": { + "version": "4.0.4", + "from": "lodash.isinteger@>=4.0.4 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz" + }, + "lodash.isnumber": { + "version": "3.0.3", + "from": "lodash.isnumber@>=3.0.3 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "from": "lodash.isplainobject@>=4.0.6 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz" + }, + "lodash.isstring": { + "version": "4.0.1", + "from": "lodash.isstring@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz" + }, + "lodash.once": { + "version": "4.1.1", + "from": "lodash.once@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz" + }, "lodash.reduce": { "version": "4.6.0", "from": "lodash.reduce@4.6.0", @@ -1962,11 +2658,23 @@ } } }, + "lolex": { + "version": "1.3.2", + "from": "lolex@1.3.2", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.3.2.tgz", + "dev": true + }, "longest": { "version": "1.0.1", "from": "longest@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz" }, + "lpad": { + "version": "0.1.0", + "from": "lpad@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/lpad/-/lpad-0.1.0.tgz", + "dev": true + }, "lru-cache": { "version": "2.7.3", "from": "lru-cache@>=2.0.0 <3.0.0", @@ -2310,6 +3018,12 @@ "from": "muri@1.2.2", "resolved": "https://registry.npmjs.org/muri/-/muri-1.2.2.tgz" }, + "mute-stream": { + "version": "0.0.7", + "from": "mute-stream@>=0.0.4 <0.1.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "dev": true + }, "mv": { "version": "0.0.5", "from": "mv@0.0.5", @@ -2326,6 +3040,26 @@ "from": "native-promise-only@>=0.8.1 <0.9.0", "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz" }, + "nconf": { + "version": "0.6.9", + "from": "nconf@>=0.6.9 <0.7.0", + "resolved": "https://registry.npmjs.org/nconf/-/nconf-0.6.9.tgz", + "dev": true, + "dependencies": { + "async": { + "version": "0.2.9", + "from": "async@0.2.9", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.9.tgz", + "dev": true + }, + "optimist": { + "version": "0.6.0", + "from": "optimist@0.6.0", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.0.tgz", + "dev": true + } + } + }, "ncp": { "version": "2.0.0", "from": "ncp@>=2.0.0 <2.1.0", @@ -2441,16 +3175,60 @@ "from": "nodemailer-wellknown@0.1.7", "resolved": "https://registry.npmjs.org/nodemailer-wellknown/-/nodemailer-wellknown-0.1.7.tgz" }, + "nomnom": { + "version": "1.6.2", + "from": "nomnom@>=1.6.0 <1.7.0", + "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.6.2.tgz", + "dev": true, + "dependencies": { + "colors": { + "version": "0.5.1", + "from": "colors@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", + "dev": true + }, + "underscore": { + "version": "1.4.4", + "from": "underscore@>=1.4.4 <1.5.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", + "dev": true + } + } + }, "nopt": { "version": "3.0.6", "from": "nopt@>=3.0.1 <3.1.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz" }, + "normalize-range": { + "version": "0.1.2", + "from": "normalize-range@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "dev": true + }, + "npm-run-path": { + "version": "2.0.2", + "from": "npm-run-path@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "dev": true + }, "npmlog": { "version": "4.1.2", "from": "npmlog@>=4.0.0 <5.0.0", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz" }, + "nssocket": { + "version": "0.5.3", + "from": "nssocket@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/nssocket/-/nssocket-0.5.3.tgz", + "dev": true + }, + "num2fraction": { + "version": "1.2.2", + "from": "num2fraction@>=1.2.2 <2.0.0", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "dev": true + }, "number-is-nan": { "version": "1.0.1", "from": "number-is-nan@>=1.0.0 <2.0.0", @@ -2501,6 +3279,154 @@ "from": "one-time@>=0.0.0 <0.1.0", "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz" }, + "onesky": { + "version": "0.1.6", + "from": "onesky@>=0.1.5 <0.2.0", + "resolved": "https://registry.npmjs.org/onesky/-/onesky-0.1.6.tgz", + "dev": true, + "dependencies": { + "asn1": { + "version": "0.1.11", + "from": "asn1@0.1.11", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz", + "dev": true, + "optional": true + }, + "assert-plus": { + "version": "0.1.5", + "from": "assert-plus@>=0.1.5 <0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", + "dev": true, + "optional": true + }, + "async": { + "version": "0.9.2", + "from": "async@>=0.9.0 <0.10.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "dev": true, + "optional": true + }, + "aws-sign2": { + "version": "0.5.0", + "from": "aws-sign2@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz", + "dev": true, + "optional": true + }, + "boom": { + "version": "0.4.2", + "from": "boom@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz", + "dev": true + }, + "combined-stream": { + "version": "0.0.7", + "from": "combined-stream@>=0.0.4 <0.1.0", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", + "dev": true, + "optional": true + }, + "cryptiles": { + "version": "0.2.2", + "from": "cryptiles@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz", + "dev": true, + "optional": true + }, + "delayed-stream": { + "version": "0.0.5", + "from": "delayed-stream@0.0.5", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz", + "dev": true, + "optional": true + }, + "forever-agent": { + "version": "0.5.2", + "from": "forever-agent@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz", + "dev": true + }, + "form-data": { + "version": "0.1.4", + "from": "form-data@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", + "dev": true, + "optional": true + }, + "hawk": { + "version": "1.1.1", + "from": "hawk@1.1.1", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.1.1.tgz", + "dev": true, + "optional": true + }, + "hoek": { + "version": "0.9.1", + "from": "hoek@>=0.9.0 <0.10.0", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz", + "dev": true + }, + "http-signature": { + "version": "0.10.1", + "from": "http-signature@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", + "dev": true, + "optional": true + }, + "mime": { + "version": "1.2.11", + "from": "mime@~1.2.11", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", + "dev": true, + "optional": true + }, + "mime-types": { + "version": "1.0.2", + "from": "mime-types@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-1.0.2.tgz", + "dev": true + }, + "node-uuid": { + "version": "1.4.8", + "from": "node-uuid@>=1.4.0 <1.5.0", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", + "dev": true + }, + "oauth-sign": { + "version": "0.3.0", + "from": "oauth-sign@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz", + "dev": true, + "optional": true + }, + "qs": { + "version": "1.0.2", + "from": "qs@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-1.0.2.tgz", + "dev": true + }, + "request": { + "version": "2.40.0", + "from": "request@>=2.40.0 <2.41.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.40.0.tgz", + "dev": true + }, + "sntp": { + "version": "0.2.4", + "from": "sntp@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz", + "dev": true, + "optional": true + }, + "tunnel-agent": { + "version": "0.4.3", + "from": "tunnel-agent@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", + "dev": true, + "optional": true + } + } + }, "optimist": { "version": "0.6.1", "from": "optimist@0.6.1", @@ -2595,6 +3521,12 @@ "from": "path-is-absolute@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" }, + "path-key": { + "version": "2.0.1", + "from": "path-key@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "dev": true + }, "path-parse": { "version": "1.0.5", "from": "path-parse@>=1.0.5 <2.0.0", @@ -2620,6 +3552,12 @@ "from": "performance-now@>=0.2.0 <0.3.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz" }, + "pkginfo": { + "version": "0.3.1", + "from": "pkginfo@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", + "dev": true + }, "platform": { "version": "1.3.4", "from": "platform@1.3.4", @@ -2642,6 +3580,26 @@ } } }, + "postcss": { + "version": "5.2.17", + "from": "postcss@>=5.2.16 <6.0.0", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "dev": true, + "dependencies": { + "source-map": { + "version": "0.5.7", + "from": "source-map@>=0.5.6 <0.6.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "dev": true + } + } + }, + "postcss-value-parser": { + "version": "3.3.0", + "from": "postcss-value-parser@>=3.2.3 <4.0.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", + "dev": true + }, "precond": { "version": "0.2.3", "from": "precond@>=0.2.0 <0.3.0", @@ -2657,11 +3615,23 @@ "from": "promise@>=2.0.0 <2.1.0", "resolved": "https://registry.npmjs.org/promise/-/promise-2.0.0.tgz" }, + "prompt": { + "version": "0.2.14", + "from": "prompt@0.2.14", + "resolved": "https://registry.npmjs.org/prompt/-/prompt-0.2.14.tgz", + "dev": true + }, "proxy-addr": { "version": "1.0.10", "from": "proxy-addr@>=1.0.8 <1.1.0", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.0.10.tgz" }, + "ps-tree": { + "version": "0.0.3", + "from": "ps-tree@>=0.0.0 <0.1.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-0.0.3.tgz", + "dev": true + }, "pseudomap": { "version": "1.0.2", "from": "pseudomap@>=1.0.1 <2.0.0", @@ -2856,6 +3826,12 @@ } } }, + "read": { + "version": "1.0.7", + "from": "read@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "dev": true + }, "readable-stream": { "version": "1.0.34", "from": "readable-stream@>=1.0.24 <1.1.0", @@ -3118,11 +4094,31 @@ "from": "regexp-clone@0.0.1", "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-0.0.1.tgz" }, + "regexp-quote": { + "version": "0.0.0", + "from": "regexp-quote@0.0.0", + "resolved": "https://registry.npmjs.org/regexp-quote/-/regexp-quote-0.0.0.tgz", + "dev": true + }, "repeat-string": { "version": "1.6.1", "from": "repeat-string@>=1.5.2 <2.0.0", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" }, + "replace": { + "version": "0.2.10", + "from": "replace@>=0.2.4 <0.3.0", + "resolved": "https://registry.npmjs.org/replace/-/replace-0.2.10.tgz", + "dev": true, + "dependencies": { + "colors": { + "version": "0.5.1", + "from": "colors@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", + "dev": true + } + } + }, "request": { "version": "2.81.0", "from": "request@>=2.69.0 <3.0.0", @@ -3150,6 +4146,12 @@ "from": "require-like@0.1.2", "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz" }, + "requirejs": { + "version": "2.1.22", + "from": "requirejs@>=2.1.0 <2.2.0", + "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.1.22.tgz", + "dev": true + }, "requires-port": { "version": "1.0.0", "from": "requires-port@>=1.0.0 <2.0.0", @@ -3182,6 +4184,12 @@ } } }, + "revalidator": { + "version": "0.1.8", + "from": "revalidator@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz", + "dev": true + }, "right-align": { "version": "0.1.3", "from": "right-align@>=0.1.1 <0.2.0", @@ -3218,6 +4226,26 @@ "from": "samsam@1.1.2", "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz" }, + "sandboxed-module": { + "version": "0.2.0", + "from": "sandboxed-module@0.2.0", + "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-0.2.0.tgz", + "dev": true, + "dependencies": { + "stack-trace": { + "version": "0.0.6", + "from": "stack-trace@0.0.6", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.6.tgz", + "dev": true + } + } + }, + "sanitize-html": { + "version": "1.14.1", + "from": "sanitize-html@>=1.14.1 <2.0.0", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.14.1.tgz", + "dev": true + }, "sanitizer": { "version": "0.1.1", "from": "sanitizer@0.1.1", @@ -3368,6 +4396,12 @@ "from": "signal-exit@>=3.0.0 <4.0.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz" }, + "sinon": { + "version": "1.17.7", + "from": "sinon@>=1.17.0 <2.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.17.7.tgz", + "dev": true + }, "sixpack-client": { "version": "1.0.0", "from": "sixpack-client@>=1.0.0 <2.0.0", @@ -3465,6 +4499,12 @@ "from": "strip-json-comments@>=2.0.1 <2.1.0", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" }, + "supports-color": { + "version": "3.2.3", + "from": "supports-color@>=3.2.3 <4.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "dev": true + }, "tape": { "version": "4.8.0", "from": "tape@>=4.6.3 <5.0.0", @@ -3574,6 +4614,84 @@ "from": "timekeeper@latest", "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-2.0.0.tgz" }, + "timespan": { + "version": "2.3.0", + "from": "timespan@>=2.3.0 <2.4.0", + "resolved": "https://registry.npmjs.org/timespan/-/timespan-2.3.0.tgz", + "dev": true + }, + "tiny-lr": { + "version": "0.2.1", + "from": "tiny-lr@>=0.2.1 <0.3.0", + "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.2.1.tgz", + "dev": true, + "dependencies": { + "body-parser": { + "version": "1.14.2", + "from": "body-parser@>=1.14.0 <1.15.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.14.2.tgz", + "dev": true, + "dependencies": { + "qs": { + "version": "5.2.0", + "from": "qs@5.2.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-5.2.0.tgz", + "dev": true + } + } + }, + "bytes": { + "version": "2.2.0", + "from": "bytes@2.2.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.2.0.tgz", + "dev": true + }, + "debug": { + "version": "2.2.0", + "from": "debug@>=2.2.0 <2.3.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "dev": true + }, + "http-errors": { + "version": "1.3.1", + "from": "http-errors@>=1.3.1 <1.4.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz", + "dev": true + }, + "iconv-lite": { + "version": "0.4.13", + "from": "iconv-lite@0.4.13", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", + "dev": true + }, + "ms": { + "version": "0.7.1", + "from": "ms@0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "dev": true + }, + "qs": { + "version": "5.1.0", + "from": "qs@>=5.1.0 <5.2.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-5.1.0.tgz", + "dev": true + }, + "raw-body": { + "version": "2.1.7", + "from": "raw-body@>=2.1.5 <2.2.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.1.7.tgz", + "dev": true, + "dependencies": { + "bytes": { + "version": "2.4.0", + "from": "bytes@2.4.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz", + "dev": true + } + } + } + } + }, "to-mongodb-core": { "version": "2.0.0", "from": "to-mongodb-core@>=2.0.0 <3.0.0", @@ -3611,6 +4729,20 @@ } } }, + "translations-sharelatex": { + "version": "0.1.4", + "from": "git+https://github.com/sharelatex/translations-sharelatex.git#master", + "resolved": "git+https://github.com/sharelatex/translations-sharelatex.git#12d5cefc591bcc0b4e54b5ce9acfbd980208b9e1", + "dev": true, + "dependencies": { + "async": { + "version": "2.5.0", + "from": "async@>=2.1.4 <3.0.0", + "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", + "dev": true + } + } + }, "tsscmp": { "version": "1.0.5", "from": "tsscmp@1.0.5", @@ -3627,6 +4759,12 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "optional": true }, + "type-detect": { + "version": "1.0.0", + "from": "type-detect@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz", + "dev": true + }, "type-is": { "version": "1.6.15", "from": "type-is@>=1.6.15 <1.7.0", @@ -3708,6 +4846,26 @@ "from": "util-deprecate@>=1.0.1 <1.1.0", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" }, + "utile": { + "version": "0.2.1", + "from": "utile@>=0.2.1 <0.3.0", + "resolved": "https://registry.npmjs.org/utile/-/utile-0.2.1.tgz", + "dev": true, + "dependencies": { + "async": { + "version": "0.2.10", + "from": "async@>=0.2.9 <0.3.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "dev": true + }, + "ncp": { + "version": "0.4.2", + "from": "ncp@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz", + "dev": true + } + } + }, "utils-merge": { "version": "1.0.0", "from": "utils-merge@1.0.0", @@ -3841,6 +4999,32 @@ "from": "void-elements@>=2.0.1 <3.0.0", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz" }, + "watch": { + "version": "0.13.0", + "from": "watch@>=0.13.0 <0.14.0", + "resolved": "https://registry.npmjs.org/watch/-/watch-0.13.0.tgz", + "dev": true, + "dependencies": { + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "dev": true + } + } + }, + "websocket-driver": { + "version": "0.7.0", + "from": "websocket-driver@>=0.5.1", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", + "dev": true + }, + "websocket-extensions": { + "version": "0.1.2", + "from": "websocket-extensions@>=0.1.1", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.2.tgz", + "dev": true + }, "which": { "version": "1.0.9", "from": "which@>=1.0.5 <1.1.0", @@ -3856,6 +5040,20 @@ "from": "window-size@0.1.0", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz" }, + "winston": { + "version": "0.8.3", + "from": "winston@>=0.8.1 <0.9.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-0.8.3.tgz", + "dev": true, + "dependencies": { + "async": { + "version": "0.2.10", + "from": "async@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "dev": true + } + } + }, "with": { "version": "3.0.1", "from": "with@>=3.0.0 <3.1.0", @@ -3966,6 +5164,12 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" } } + }, + "zlib-browserify": { + "version": "0.0.1", + "from": "zlib-browserify@0.0.1", + "resolved": "https://registry.npmjs.org/zlib-browserify/-/zlib-browserify-0.0.1.tgz", + "dev": true } } } diff --git a/services/web/package.json b/services/web/package.json index 25122f635d..a35688bfe7 100644 --- a/services/web/package.json +++ b/services/web/package.json @@ -30,6 +30,7 @@ "http-proxy": "^1.8.1", "ioredis": "^2.4.0", "jade": "~1.3.1", + "jsonwebtoken": "^8.0.1", "ldapjs": "^0.7.1", "lodash": "^4.13.1", "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#master", From 15b73713d2b98c561633df09fe0f05854cbeb36d Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 3 Oct 2017 10:43:17 +0100 Subject: [PATCH 091/206] Add OL + SL image from blog --- services/web/public/img/ol_plus_sl.png | Bin 0 -> 62033 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 services/web/public/img/ol_plus_sl.png diff --git a/services/web/public/img/ol_plus_sl.png b/services/web/public/img/ol_plus_sl.png new file mode 100644 index 0000000000000000000000000000000000000000..b0b8523dc7aaef8d6cea9fabf5c41916cc5e6653 GIT binary patch literal 62033 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 fd=_pOHf=>#oeGI7!#*rj(@-p7 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 literal 0 HcmV?d00001 From 91e0397bf54b7f88c0c29ecee79f13789b42ba86 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Tue, 3 Oct 2017 10:48:59 +0100 Subject: [PATCH 092/206] Update signup date check to today --- .../web/app/coffee/Features/Project/ProjectController.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/app/coffee/Features/Project/ProjectController.coffee b/services/web/app/coffee/Features/Project/ProjectController.coffee index 7c0328fe17..17adeae4c1 100644 --- a/services/web/app/coffee/Features/Project/ProjectController.coffee +++ b/services/web/app/coffee/Features/Project/ProjectController.coffee @@ -254,7 +254,7 @@ module.exports = ProjectController = return cb() timestamp = user_id.toString().substring(0,8) userSignupDate = new Date( parseInt( timestamp, 16 ) * 1000 ) - if userSignupDate > new Date("2017-10-05") + if userSignupDate > new Date("2017-10-03") # Don't show for users who registered after it was released return cb(null, false) timeout = setTimeout cb, 500 From d14723f24a8a45005a267ff5b69fe6df77605a7e Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 3 Oct 2017 16:16:21 +0100 Subject: [PATCH 093/206] add rate limits for autocompiles global rate limit for all users and a lower rate limit for free users --- .../Features/Compile/CompileManager.coffee | 46 +++++++++++++++---- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/services/web/app/coffee/Features/Compile/CompileManager.coffee b/services/web/app/coffee/Features/Compile/CompileManager.coffee index 61fe7038a6..756222121a 100755 --- a/services/web/app/coffee/Features/Compile/CompileManager.coffee +++ b/services/web/app/coffee/Features/Compile/CompileManager.coffee @@ -34,12 +34,16 @@ module.exports = CompileManager = return callback(error) if error? for key, value of limits options[key] = value - # only pass user_id down to clsi if this is a per-user compile - compileAsUser = if Settings.disablePerUserCompiles then undefined else user_id - ClsiManager.sendRequest project_id, compileAsUser, options, (error, status, outputFiles, clsiServerId, validationProblems) -> - return callback(error) if error? - logger.log files: outputFiles, "output files" - callback(null, status, outputFiles, clsiServerId, limits, validationProblems) + # Put a lower limit on autocompiles for free users + CompileManager._checkIfFreeAutoCompileLimitHasBeenHit options.isAutoCompile, limits.compileGroup, (err, canCompile)-> + if !canCompile + return callback null, "autocompile-backoff", [] + # only pass user_id down to clsi if this is a per-user compile + compileAsUser = if Settings.disablePerUserCompiles then undefined else user_id + ClsiManager.sendRequest project_id, compileAsUser, options, (error, status, outputFiles, clsiServerId, validationProblems) -> + return callback(error) if error? + logger.log files: outputFiles, "output files" + callback(null, status, outputFiles, clsiServerId, limits, validationProblems) stopCompile: (project_id, user_id, callback = (error) ->) -> @@ -75,15 +79,41 @@ module.exports = CompileManager = _checkIfAutoCompileLimitHasBeenHit: (isAutoCompile, callback = (err, canCompile)->)-> if !isAutoCompile return callback(null, true) - opts = + Metrics.inc "auto-compile" + opts = endpointName:"auto_compile" timeInterval:20 subjectName:"everyone" - throttle: 25 + throttle: 200 rateLimiter.addCount opts, (err, canCompile)-> if err? canCompile = false logger.log canCompile:canCompile, opts:opts, "checking if auto compile limit has been hit" + if !canCompile + Metrics.inc "auto-compile-rate-limited" + callback err, canCompile + + + _checkIfFreeAutoCompileLimitHasBeenHit: (isAutoCompile, compileGroup, callback = (err, canCompile)->)-> + if !isAutoCompile + return callback(null, true) + + if compileGroup is "priority" + Metrics.inc "auto-compile-priority" + return callback(null, true) + + Metrics.inc "auto-compile-free" + opts = + endpointName:"auto_compile" + timeInterval:20 + subjectName:"free" + throttle: 100 + rateLimiter.addCount opts, (err, canCompile)-> + if err? + canCompile = false + logger.log canCompile:canCompile, opts:opts, "checking if free users auto compile limit has been hit" + if !canCompile + Metrics.inc "auto-compile-free-rate-limited" callback err, canCompile _ensureRootDocumentIsSet: (project_id, callback = (error) ->) -> From 2723537f824d757fa07f61bc1892571357c73d69 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 3 Oct 2017 16:23:49 +0100 Subject: [PATCH 094/206] disable autocompile when rate limit is hit --- services/web/app/views/project/editor/pdf.pug | 6 ++++++ .../coffee/ide/pdf/controllers/PdfController.coffee | 12 ++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/services/web/app/views/project/editor/pdf.pug b/services/web/app/views/project/editor/pdf.pug index 377f09f71a..02531e9584 100644 --- a/services/web/app/views/project/editor/pdf.pug +++ b/services/web/app/views/project/editor/pdf.pug @@ -391,6 +391,12 @@ div.full-size.pdf(ng-controller="PdfController") ng-click="startFreeTrial('compile-timeout')" ) #{translate("start_free_trial")} + + .alert.alert-danger(ng-show="pdf.autocompile_disabled") + p + strong #{translate("autocompile_disabled")}. + span #{translate("autocompile_disabled_reason")} + .alert.alert-danger(ng-show="pdf.projectTooLarge") strong #{translate("project_too_large")} span #{translate("project_too_large_please_reduce")} diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index 73ae9a4380..e77d4a22cb 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -127,7 +127,7 @@ define [ sendCompileRequest = (options = {}) -> url = "/project/#{$scope.project_id}/compile" params = {} - if options.isAutoCompile + if options.isAutoCompile or options.isBackgroundAutoCompile params["auto_compile"]=true # if the previous run was a check, clear the error logs $scope.pdf.logEntries = [] if $scope.check @@ -168,6 +168,7 @@ define [ $scope.pdf.compileExited = false $scope.pdf.failedCheck = false $scope.pdf.compileInProgress = false + $scope.pdf.autocompile_disabled = false # make a cache to look up files by name fileByPath = {} @@ -206,7 +207,13 @@ define [ $scope.shouldShowLogs = true fetchLogs(fileByPath) else if response.status == "autocompile-backoff" - $scope.pdf.view = 'uncompiled' + if $scope.pdf.isAutoCompile # initial autocompile + $scope.pdf.view = 'uncompiled' + else # background autocompile from typing + $scope.pdf.view = 'errors' + $scope.pdf.autocompile_disabled = true + $scope.autocompile_enabled = false # disable any further autocompiles + event_tracking.sendMB "autocompile-rate-limited", {hasPremiumCompile: $scope.hasPremiumCompile} else if response.status == "project-too-large" $scope.pdf.view = 'errors' $scope.pdf.projectTooLarge = true @@ -415,6 +422,7 @@ define [ event_tracking.sendMBSampled "editor-recompile-sampled", options $scope.pdf.compiling = true + $scope.pdf.isAutoCompile = options?.isAutoCompile # initial autocompile if options?.force # for forced compile, turn off validation check and ignore errors From 8366ea271f50b2c4791d7cb903b78baa1d5b6ac3 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Tue, 3 Oct 2017 17:08:19 +0100 Subject: [PATCH 095/206] Restrict autocompile onboarding to users in rollout (factor of 100) --- .../coffee/Features/Project/ProjectController.coffee | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/services/web/app/coffee/Features/Project/ProjectController.coffee b/services/web/app/coffee/Features/Project/ProjectController.coffee index 17adeae4c1..89b47b5782 100644 --- a/services/web/app/coffee/Features/Project/ProjectController.coffee +++ b/services/web/app/coffee/Features/Project/ProjectController.coffee @@ -252,8 +252,15 @@ module.exports = ProjectController = cb = underscore.once(cb) if !user_id? return cb() - timestamp = user_id.toString().substring(0,8) - userSignupDate = new Date( parseInt( timestamp, 16 ) * 1000 ) + + # Extract data from user's ObjectId + timestamp = parseInt(user_id.toString().substring(0, 8), 16) + counter = parseInt(user_id.toString().substring(18, 24), 16) + + if counter % 5000 != 0 + # Don't show if user is not part of roll out + return cb(null, false) + userSignupDate = new Date(timestamp * 1000) if userSignupDate > new Date("2017-10-03") # Don't show for users who registered after it was released return cb(null, false) From 550e7d75ca703a66214cdb6c9605ddae5daf7fd9 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Tue, 3 Oct 2017 17:19:51 +0100 Subject: [PATCH 096/206] Check if user is part of autocompile rollout when showing setting or running autocompile --- services/web/app/views/project/editor/pdf.pug | 2 +- .../web/public/coffee/ide/pdf/controllers/PdfController.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/app/views/project/editor/pdf.pug b/services/web/app/views/project/editor/pdf.pug index 69fc28cc54..2e0bc3701f 100644 --- a/services/web/app/views/project/editor/pdf.pug +++ b/services/web/app/views/project/editor/pdf.pug @@ -27,7 +27,7 @@ div.full-size.pdf(ng-controller="PdfController") span.caret ul.dropdown-menu.dropdown-menu-left // Only show on beta program? - if user.betaProgram + if user.betaProgram || showAutoCompileOnboarding li.dropdown-header | #{translate("auto_compile")} span.beta-feature-badge diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index 1e0403bb76..1a482e8ede 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -109,7 +109,7 @@ define [ toggleAutoCompile(newValue) event_tracking.sendMB "autocompile-setting-changed", { value: newValue } - if window.user?.betaProgram and $scope.autocompile_enabled + if (window.user?.betaProgram or window.showAutoCompileOnboarding) and $scope.autocompile_enabled toggleAutoCompile(true) # abort compile if syntax checks fail From 8b4843cf8b655eb8798a4604f0862983f3374292 Mon Sep 17 00:00:00 2001 From: Nate Stemen Date: Tue, 3 Oct 2017 17:32:34 +0100 Subject: [PATCH 097/206] cleaning up usepackage completion --- .../auto-complete/CommandManager.coffee | 4 ++-- .../auto-complete/PackageManager.coffee | 20 ++++++------------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/CommandManager.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/CommandManager.coffee index c513f6854d..88cd3a2e84 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/CommandManager.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/CommandManager.coffee @@ -12,7 +12,7 @@ define [], () -> 'Lambda', 'Xi', 'Pi', 'Sigma', 'Upsilon', 'Phi', 'Psi', 'Omega' ] singleArgumentCommands = [ - 'chapter', 'usepackage', 'section', 'label', 'textbf', 'subsection', + 'chapter', 'section', 'label', 'textbf', 'subsection', 'vspace', 'cite', 'textit', 'documentclass', 'includegraphics', 'input', 'emph','caption', 'ref', 'title', 'author', 'texttt', 'include', 'hspace', 'bibitem', 'url', 'large', 'subsubsection', 'textsc', 'date', @@ -172,7 +172,7 @@ define [], () -> commands = parser.parse() completions = [] for command in commands - if command[0] not in rawCommands + if command[0] not in rawCommands and command[0] != "usepackage" caption = "\\#{command[0]}" score = if caption == prefix then 99 else 50 snippet = caption diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/PackageManager.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/PackageManager.coffee index af8cac861a..ac30d5a431 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/PackageManager.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/PackageManager.coffee @@ -27,20 +27,12 @@ define () -> meta: "pkg" } - parseLoadedPackages = (text) -> - re = /^\\usepackage(?:\[(.*?)])?.*?{((?:.|\n)*?)}/gm - result = [] - iterations = 0 - while match = re.exec(text) - if match[2] - for pkg in match[2].split(",") - cleaned = pkg.replace(/%.*\n/gm,'').trim() - if cleaned not in result - result.push cleaned - iterations += 1 - if iterations >= 1000 - return result - return result + packageSnippets.push { + caption: "\\usepackage{}" + snippet: "\\usepackage{}" + meta: "pkg" + score: 70 + } class PackageManager getCompletions: (editor, session, pos, prefix, callback) -> From afe40809ad0daa8fbd5e8df72726e85d147b0e69 Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 4 Oct 2017 14:57:08 +0100 Subject: [PATCH 098/206] Add in account merge module to build step --- services/web/Jenkinsfile | 1 + 1 file changed, 1 insertion(+) diff --git a/services/web/Jenkinsfile b/services/web/Jenkinsfile index 6421174e68..5c7b4e160f 100644 --- a/services/web/Jenkinsfile +++ b/services/web/Jenkinsfile @@ -42,6 +42,7 @@ pipeline { checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'modules/templates'], [$class: 'CloneOption', shallow: true]], userRemoteConfigs: [[credentialsId: 'GIT_DEPLOY_KEY', url: 'git@github.com:sharelatex/templates-webmodule.git']]]) checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'modules/track-changes'], [$class: 'CloneOption', shallow: true]], userRemoteConfigs: [[credentialsId: 'GIT_DEPLOY_KEY', url: 'git@github.com:sharelatex/track-changes-web-module.git']]]) checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'modules/overleaf-integration'], [$class: 'CloneOption', shallow: true]], userRemoteConfigs: [[credentialsId: 'GIT_DEPLOY_KEY', url: 'git@github.com:sharelatex/overleaf-integration-web-module.git']]]) + checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'modules/overleaf-account-merge'], [$class: 'CloneOption', shallow: true]], userRemoteConfigs: [[credentialsId: 'GIT_DEPLOY_KEY', url: 'git@github.com:sharelatex/overleaf-account-merge.git']]]) } } From 166a0605a3b9ada2125d4452bceaa3934fb97950 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Thu, 5 Oct 2017 10:49:56 +0100 Subject: [PATCH 099/206] Make form elements consistent with button sizes. --- services/web/public/stylesheets/core/ol-variables.less | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index 26c975b277..6946a697f3 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -78,6 +78,9 @@ @navbar-subdued-hover-bg : #FFF; @navbar-subdued-hover-color : @ol-green; +// Forms +@input-border-radius : unit(@line-height-base, em); +@input-height-base : @line-height-computed + (@padding-base-vertical * 2); // TODO Warning color-orange? @btn-warning-color : #FFF; @btn-warning-bg : @ol-red; From 9c1faa43950c36b86c0745cda85eb7aa5f1daa3a Mon Sep 17 00:00:00 2001 From: James Allen Date: Thu, 5 Oct 2017 14:26:34 +0100 Subject: [PATCH 100/206] Add method to transfer projects from one user_id to another --- .../Collaborators/CollaboratorsHandler.coffee | 70 ++++++++++++ .../CollaboratorsHandlerTests.coffee | 100 ++++++++++++++++++ 2 files changed, 170 insertions(+) diff --git a/services/web/app/coffee/Features/Collaborators/CollaboratorsHandler.coffee b/services/web/app/coffee/Features/Collaborators/CollaboratorsHandler.coffee index 44b6bd9ec2..ff6a52e471 100644 --- a/services/web/app/coffee/Features/Collaborators/CollaboratorsHandler.coffee +++ b/services/web/app/coffee/Features/Collaborators/CollaboratorsHandler.coffee @@ -157,3 +157,73 @@ module.exports = CollaboratorsHandler = return callback(error) {owner, members} = ProjectEditorHandler.buildOwnerAndMembersViews(rawMembers) callback(null, members) + + transferProjects: (from_user_id, to_user_id, callback=(err, projects) ->) -> + MEMBER_KEYS = ['collaberator_refs', 'readOnly_refs'] + + # jobs = [] + # project_ids = [] + # jobs.push (cb) -> + # Project.find { owner_ref: from_user_id }, { _id: 1 }, (error, projects = []) -> + # return callback(error) if error? + # project_ids = project_ids.concat(projects.map (p) -> p._id) + # Project.update { owner_ref: from_user_id }, { $set: { owner_ref: to_user_id }}, { multi: true }, cb + + # for key in MEMBER_KEYS + # do (key) -> + # jobs.push (cb) -> + # query = {} + # query[] + # Project.find { owner_ref: from_user_id }, { _id: 1 }, (error, projects = []) -> + # return callback(error) if error? + # project_ids = project_ids.concat(projects.map (p) -> p._id) + # Project.update { owner_ref: from_user_id }, { $set: { owner_ref: to_user_id }}, { multi: true }, cb + + # Find all the projects this user is part of so we can flush them to TPDS + query = + $or: + [{ owner_ref: from_user_id }] + .concat( + MEMBER_KEYS.map (key) -> + q = {} + q[key] = from_user_id + return q + ) # [{ collaberator_refs: from_user_id }, ...] + Project.find query, { _id: 1 }, (error, projects = []) -> + return callback(error) if error? + + project_ids = projects.map (p) -> p._id + logger.log {project_ids, from_user_id, to_user_id}, "transferring projects" + + update_jobs = [] + update_jobs.push (cb) -> + Project.update { owner_ref: from_user_id }, { $set: { owner_ref: to_user_id }}, { multi: true }, cb + for key in MEMBER_KEYS + do (key) -> + update_jobs.push (cb) -> + query = {} + addNewUserUpdate = $addToSet: {} + removeOldUserUpdate = $pull: {} + query[key] = from_user_id + removeOldUserUpdate.$pull[key] = from_user_id + addNewUserUpdate.$addToSet[key] = to_user_id + # Mongo won't let us pull and addToSet in the same query, so do it in + # two. Note we need to add first, since the query is based on the old user. + Project.update query, addNewUserUpdate, { multi: true }, (error) -> + return cb(error) if error? + Project.update query, removeOldUserUpdate, { multi: true }, cb + + # Flush each project to TPDS to add files to new user's Dropbox + ProjectEntityHandler = require("../Project/ProjectEntityHandler") + flush_jobs = [] + for project_id in project_ids + do (project_id) -> + flush_jobs.push (cb) -> + ProjectEntityHandler.flushProjectToThirdPartyDataStore project_id, cb + + # Flush in background, no need to block on this + async.series flush_jobs, (error) -> + if error? + logger.err {err: error, project_ids, from_user_id, to_user_id}, "error flushing tranferred projects to TPDS" + + async.series update_jobs, callback diff --git a/services/web/test/UnitTests/coffee/Collaborators/CollaboratorsHandlerTests.coffee b/services/web/test/UnitTests/coffee/Collaborators/CollaboratorsHandlerTests.coffee index 9ba099f60b..ee231f7c9f 100644 --- a/services/web/test/UnitTests/coffee/Collaborators/CollaboratorsHandlerTests.coffee +++ b/services/web/test/UnitTests/coffee/Collaborators/CollaboratorsHandlerTests.coffee @@ -344,3 +344,103 @@ describe "CollaboratorsHandler", -> it 'should not call ProjectEditorHandler.buildOwnerAndMembersViews', -> @ProjectEditorHandler.buildOwnerAndMembersViews.callCount.should.equal 0 + + describe 'transferProjects', -> + beforeEach -> + @from_user_id = "from-user-id" + @to_user_id = "to-user-id" + @projects = [{ + _id: "project-id-1" + }, { + _id: "project-id-2" + }] + @Project.find = sinon.stub().yields(null, @projects) + @Project.update = sinon.stub().yields() + @ProjectEntityHandler.flushProjectToThirdPartyDataStore = sinon.stub().yields() + + describe "successfully", -> + beforeEach -> + @CollaboratorHandler.transferProjects @from_user_id, @to_user_id, @callback + + it "should look up the affected projects", -> + @Project.find + .calledWith({ + $or : [ + { owner_ref: @from_user_id } + { collaberator_refs: @from_user_id } + { readOnly_refs: @from_user_id } + ] + }) + .should.equal true + + it "should transfer owned projects", -> + @Project.update + .calledWith({ + owner_ref: @from_user_id + }, { + $set: { owner_ref: @to_user_id } + }, { + multi: true + }) + .should.equal true + + it "should transfer collaborator projects", -> + @Project.update + .calledWith({ + collaberator_refs: @from_user_id + }, { + $addToSet: { collaberator_refs: @to_user_id } + }, { + multi: true + }) + .should.equal true + @Project.update + .calledWith({ + collaberator_refs: @from_user_id + }, { + $pull: { collaberator_refs: @from_user_id } + }, { + multi: true + }) + .should.equal true + + it "should transfer read only collaborator projects", -> + @Project.update + .calledWith({ + readOnly_refs: @from_user_id + }, { + $addToSet: { readOnly_refs: @to_user_id } + }, { + multi: true + }) + .should.equal true + @Project.update + .calledWith({ + readOnly_refs: @from_user_id + }, { + $pull: { readOnly_refs: @from_user_id } + }, { + multi: true + }) + .should.equal true + + it "should flush each project to the TPDS", -> + for project in @projects + @ProjectEntityHandler.flushProjectToThirdPartyDataStore + .calledWith(project._id) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + describe "when flushing to TPDS fails", -> + beforeEach -> + @ProjectEntityHandler.flushProjectToThirdPartyDataStore = sinon.stub().yields(new Error('oops')) + @CollaboratorHandler.transferProjects @from_user_id, @to_user_id, @callback + + it "should log an error", -> + @logger.err.called.should.equal true + + it "should not return an error since it happens in the background", -> + @callback.called.should.equal true + @callback.calledWith(new Error('oops')).should.equal false \ No newline at end of file From 8b98455674ce0099c7837a70b8d1c24e31278c66 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Thu, 5 Oct 2017 15:26:49 +0100 Subject: [PATCH 101/206] Adjustments to the folders menu. --- .../web/app/views/project/list/side-bar.pug | 2 +- .../public/stylesheets/app/project-list.less | 49 +++++++++++-------- .../stylesheets/core/_common-variables.less | 22 +++++++++ .../public/stylesheets/core/ol-variables.less | 37 +++++++++++--- 4 files changed, 80 insertions(+), 30 deletions(-) diff --git a/services/web/app/views/project/list/side-bar.pug b/services/web/app/views/project/list/side-bar.pug index 93eeb46125..570372001f 100644 --- a/services/web/app/views/project/list/side-bar.pug +++ b/services/web/app/views/project/list/side-bar.pug @@ -45,7 +45,7 @@ a(href) #{translate("shared_with_you")} li(ng-class="{active: (filter == 'archived')}", ng-click="filterProjects('archived')") a(href) #{translate("deleted_projects")} - li + li.separator h2 #{translate("folders")} li.tag( ng-repeat="tag in tags | orderBy:'name'", diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less index e5f4a9caff..3dd793a167 100644 --- a/services/web/public/stylesheets/app/project-list.less +++ b/services/web/public/stylesheets/app/project-list.less @@ -95,19 +95,21 @@ ul.folders-menu { } > li { cursor: pointer; - line-height: 1.8; position: relative; > a { + line-height: @folders-menu-line-height; display: block; - font-size: 0.9rem; color: @sidebar-link-color; - padding: (@line-height-computed / 4) (@grid-gutter-width / 2); - line-height: 1.2; + padding: @folders-menu-item-v-padding (@grid-gutter-width / 2); + border-bottom: solid 1px transparent; &:hover { background-color: @sidebar-hover-bg; text-decoration: @sidebar-hover-text-decoration; } } + &.separator { + cursor: auto; + } } > li.active { //border-right: 4px solid @red; @@ -125,10 +127,14 @@ ul.folders-menu { color: @gray; } h2 { - margin-top: @line-height-computed / 2; - margin-bottom: @line-height-computed / 4; - font-size: @font-size-base; - font-weight: 500; + margin-top: @folders-title-margin-top; + margin-bottom: @folders-title-margin-bottom; + font-size: @folders-title-font-size; + color: @folders-title-color; + line-height: @folders-title-line-height; + text-transform: @folders-title-text-transform; + padding: @folders-menu-item-v-padding (@grid-gutter-width / 2); + font-weight: @folders-title-font-weight; font-family: @font-family-sans-serif; } > li.tag { @@ -137,7 +143,7 @@ ul.folders-menu { color: white; border-color: white; &:hover { - background-color: darken(@brand-primary, 10%); + background-color: @folders-tag-menu-active-hover; } } } @@ -145,7 +151,7 @@ ul.folders-menu { font-style: italic; margin-bottom: @line-height-computed / 4; a { - line-height: 1.7; + line-height: @folders-untagged-line-height; &:hover, &:focus { text-decoration: none; @@ -157,7 +163,7 @@ ul.folders-menu { } &:hover { &:not(.active) { - background-color: darken(@gray-lightest, 2%); + background-color: @folders-tag-hover; } .tag-menu { display: block @@ -165,30 +171,30 @@ ul.folders-menu { } &:not(.active) { .tag-menu > a:hover { - background-color: @gray-light; + background-color: @folders-tag-menu-hover; } } a.tag-name { - padding: 2px (@line-height-computed / 4); - margin-right: 18px; - display: inline-block; + padding: @folders-tag-padding; + margin-right: @folders-tag-margin-right; + display: @folders-tag-display; position: relative; i { - position: absolute; + position: @folders-tag-icon-position; top: 5px; left: 6px; } span.name { display: inline-block; - padding-left: 22px; + padding-left: @folders-tag-spacing; line-height: 1.4; } } .tag-menu { > a { - border: 1px solid @gray; + border: 1px solid @folders-tag-border-color; border-radius: @border-radius-small; - color: @text-color; + color: @folders-tag-menu-color; display: block; width: 16px; height: 16px; @@ -201,7 +207,8 @@ ul.folders-menu { } display: none; position: absolute; - top: 6px; + top: 50%; + margin-top: -8px; // Half the element height. right: 4px; &.open { display: block; @@ -272,7 +279,7 @@ ul.structured-list { ul.project-list { li { - line-height: 3; + line-height: @structured-list-line-height; padding: 0; // .last-modified, .owner { // font-size: .8rem; diff --git a/services/web/public/stylesheets/core/_common-variables.less b/services/web/public/stylesheets/core/_common-variables.less index 75a57c5e7f..723c7d4145 100644 --- a/services/web/public/stylesheets/core/_common-variables.less +++ b/services/web/public/stylesheets/core/_common-variables.less @@ -825,6 +825,28 @@ @sidebar-hover-bg: transparent; @sidebar-hover-text-decoration: underline; +@structured-list-line-height: inherit; +@folders-menu-line-height: 1.8; +@folders-menu-item-v-padding: (@line-height-computed / 4); +@folders-title-margin-top: @line-height-computed / 2; +@folders-title-margin-bottom: @line-height-computed / 4; +@folders-title-font-size: @font-size-base; +@folders-title-font-weight: 500; +@folders-title-line-height: @headings-line-height; +@folders-title-color: inherit; +@folders-title-text-transform: none; +@folders-tag-icon-position: absolute; +@folders-tag-padding: 2px (@line-height-computed / 4); +@folders-tag-spacing: 22px; +@folders-tag-display: inline-block; +@folders-tag-margin-right: 18px; +@folders-tag-menu-color: @gray; +@folders-tag-hover: darken(@gray-lightest, 2%); +@folders-tag-border-color: @text-color; +@folders-tag-menu-active-hover: darken(@brand-primary, 10%); +@folders-tag-menu-hover: @gray-light; +@folders-untagged-line-height: 1.7; + @btn-border-width: 1px; @btn-border-bottom-width: 2px; diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index 6946a697f3..a788e51998 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -80,18 +80,18 @@ // Forms @input-border-radius : unit(@line-height-base, em); -@input-height-base : @line-height-computed + (@padding-base-vertical * 2); +@input-height-base : @line-height-computed + (@padding-base-vertical * 2) - 1; // TODO Warning color-orange? -@btn-warning-color : #FFF; -@btn-warning-bg : @ol-red; -@btn-warning-border : transparent; +@btn-warning-color : #FFF; +@btn-warning-bg : @ol-red; +@btn-warning-border : transparent; -@btn-danger-color : #FFF; -@btn-danger-bg : @ol-red; -@btn-danger-border : transparent; +@btn-danger-color : #FFF; +@btn-danger-bg : @ol-red; +@btn-danger-border : transparent; // Cards -@card-box-shadow : none; +@card-box-shadow : none; // Sidebar @sidebar-bg : @ol-blue-gray-5; @@ -107,6 +107,27 @@ @structured-list-border-color : @ol-blue-gray-1; @structured-list-hover-color : lighten(@ol-blue-gray-1, 5%); +@structured-list-line-height : 2.5; +@folders-menu-line-height : @structured-list-line-height; +@folders-menu-item-v-padding : 0; +@folders-title-margin-top : 0; +@folders-title-margin-bottom : 0; +@folders-title-font-weight : normal; +@folders-title-font-size : @font-size-small; +@folders-title-line-height : unit((@font-size-base / @font-size-small) * @folders-menu-line-height); +@folders-title-color : @ol-blue-gray-3; +@folders-title-text-transform : uppercase; +@folders-tag-display : block; +@folders-tag-margin-right : 0; +@folders-tag-icon-position : static; +@folders-tag-padding : @folders-menu-item-v-padding (@grid-gutter-width / 2); +@folders-tag-spacing : 0.5em; +@folders-tag-menu-color : #FFF; +@folders-tag-hover : @sidebar-hover-bg; +@folders-tag-border-color : @folders-tag-menu-color; +@folders-tag-menu-hover : rgba(0, 0, 0, .1); +@folders-tag-menu-active-hover : rgba(0, 0, 0, .1); +@folders-untagged-line-height : @folders-menu-line-height; //== Colors // //## Gray and brand colors for use across Bootstrap. From 3c54419eb338ae17acfd42c85499b982f33f2fa5 Mon Sep 17 00:00:00 2001 From: James Allen Date: Thu, 5 Oct 2017 16:12:26 +0100 Subject: [PATCH 102/206] Remove commented debug code --- .../Collaborators/CollaboratorsHandler.coffee | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/services/web/app/coffee/Features/Collaborators/CollaboratorsHandler.coffee b/services/web/app/coffee/Features/Collaborators/CollaboratorsHandler.coffee index ff6a52e471..a38d5370e4 100644 --- a/services/web/app/coffee/Features/Collaborators/CollaboratorsHandler.coffee +++ b/services/web/app/coffee/Features/Collaborators/CollaboratorsHandler.coffee @@ -161,24 +161,6 @@ module.exports = CollaboratorsHandler = transferProjects: (from_user_id, to_user_id, callback=(err, projects) ->) -> MEMBER_KEYS = ['collaberator_refs', 'readOnly_refs'] - # jobs = [] - # project_ids = [] - # jobs.push (cb) -> - # Project.find { owner_ref: from_user_id }, { _id: 1 }, (error, projects = []) -> - # return callback(error) if error? - # project_ids = project_ids.concat(projects.map (p) -> p._id) - # Project.update { owner_ref: from_user_id }, { $set: { owner_ref: to_user_id }}, { multi: true }, cb - - # for key in MEMBER_KEYS - # do (key) -> - # jobs.push (cb) -> - # query = {} - # query[] - # Project.find { owner_ref: from_user_id }, { _id: 1 }, (error, projects = []) -> - # return callback(error) if error? - # project_ids = project_ids.concat(projects.map (p) -> p._id) - # Project.update { owner_ref: from_user_id }, { $set: { owner_ref: to_user_id }}, { multi: true }, cb - # Find all the projects this user is part of so we can flush them to TPDS query = $or: From 3c662f6d054ebbcb1720b8155bfa9f1488f2785b Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Thu, 5 Oct 2017 16:35:27 +0100 Subject: [PATCH 103/206] Basic footer colors. --- services/web/public/stylesheets/components/footer.less | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/services/web/public/stylesheets/components/footer.less b/services/web/public/stylesheets/components/footer.less index e4e4f82da1..c86c773aee 100644 --- a/services/web/public/stylesheets/components/footer.less +++ b/services/web/public/stylesheets/components/footer.less @@ -1,4 +1,5 @@ footer.site-footer { + background-color: @footer-bg-color; border-top: 1px solid @gray-lighter; padding: 2em; font-size: 0.9rem; @@ -22,6 +23,14 @@ footer.site-footer { vertical-align: text-bottom; } } + + a { + color: @footer-link-color; + &:hover, + &:focus { + color: @footer-link-hover-color; + } + } } .sprite-icon-lang { From 15eb23da54fcabbd83d8c5c2c3278c4acc8fe6cf Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Thu, 5 Oct 2017 16:35:53 +0100 Subject: [PATCH 104/206] Overall project list page layout; flex-based, i.e. needs lots of testing --- services/web/app/views/project/list.pug | 6 +-- .../public/stylesheets/app/project-list.less | 37 ++++++++++++++----- .../public/stylesheets/components/navbar.less | 2 +- .../stylesheets/core/_common-variables.less | 6 ++- .../public/stylesheets/core/ol-variables.less | 7 ++++ .../public/stylesheets/core/scaffolding.less | 3 ++ 6 files changed, 47 insertions(+), 14 deletions(-) diff --git a/services/web/app/views/project/list.pug b/services/web/app/views/project/list.pug index dead200279..17b5d7d1fb 100644 --- a/services/web/app/views/project/list.pug +++ b/services/web/app/views/project/list.pug @@ -56,8 +56,8 @@ block content ng-click="showAll();" ) Show all - .row(ng-cloak) - span(ng-if="projects.length > 0") + .row.project-list-row(ng-cloak) + .project-list-container(ng-if="projects.length > 0") aside.project-list-sidebar.col-md-2.col-xs-3 include ./list/side-bar @@ -65,7 +65,7 @@ block content include ./list/notifications include ./list/project-list - span(ng-if="projects.length === 0") + .project-list-empty(ng-if="projects.length === 0") .col-md-offset-2.col-md-8.col-md-offset-2.col-xs-8 include ./list/empty-project-list diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less index 3dd793a167..f32e96f2fd 100644 --- a/services/web/public/stylesheets/app/project-list.less +++ b/services/web/public/stylesheets/app/project-list.less @@ -18,21 +18,40 @@ } .project-list-page { + display: flex; + flex-direction: column; + flex-grow: 1; position: relative; padding-top: 0; padding-bottom: 0; + width: 100%; } - .project-list-sidebar { - background-color: @sidebar-bg; - padding-top: @content-margin-top; - padding-bottom: @content-margin-top; - } - - .project-list-main { - padding-top: @content-margin-top; - padding-bottom: @content-margin-top; + .project-list-row { + display: flex; + flex-direction: column; + flex-grow: 1; } + .project-list-container { + display: flex; + align-items: stretch; + flex-grow: 1; + } + + .project-list-empty { + + } + + .project-list-sidebar { + background-color: @sidebar-bg; + padding-top: @content-margin-top; + padding-bottom: @content-margin-top; + } + + .project-list-main { + padding-top: @content-margin-top; + padding-bottom: @content-margin-top; + } .project-header { .btn-group > .btn { diff --git a/services/web/public/stylesheets/components/navbar.less b/services/web/public/stylesheets/components/navbar.less index 12c8f552f7..230d62b6b4 100755 --- a/services/web/public/stylesheets/components/navbar.less +++ b/services/web/public/stylesheets/components/navbar.less @@ -384,7 +384,7 @@ position: absolute; top: 5px; bottom: 5px; - width: 180px; + width: @navbar-brand-width; padding: 0; background-image: @navbar-brand-image-url; background-size: contain; diff --git a/services/web/public/stylesheets/core/_common-variables.less b/services/web/public/stylesheets/core/_common-variables.less index 723c7d4145..c813b9448b 100644 --- a/services/web/public/stylesheets/core/_common-variables.less +++ b/services/web/public/stylesheets/core/_common-variables.less @@ -795,7 +795,7 @@ @content-alt-bg-color: lighten(@gray-lightest, 2.5%); @navbar-default-padding: 1rem 2rem; - +@navbar-brand-width: 180px; @navbar-btn-font-size: @font-size-base * 0.8; @navbar-btn-border-radius: @border-radius-base; @navbar-btn-font-weight: 700; @@ -847,6 +847,10 @@ @folders-tag-menu-hover: @gray-light; @folders-untagged-line-height: 1.7; +@footer-link-color: @link-color; +@footer-link-hover-color: @link-hover-color; +@footer-bg-color: transparent; + @btn-border-width: 1px; @btn-border-bottom-width: 2px; diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index a788e51998..ef23d37974 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -18,6 +18,7 @@ @ol-type-color : @ol-blue-gray-3; // Navbar customization +@navbar-brand-width : 130px; @navbar-default-color : #FFF; @navbar-default-bg : @ol-blue-gray-6; @navbar-default-border : transparent; @@ -128,6 +129,12 @@ @folders-tag-menu-hover : rgba(0, 0, 0, .1); @folders-tag-menu-active-hover : rgba(0, 0, 0, .1); @folders-untagged-line-height : @folders-menu-line-height; + +// Footer +@footer-bg-color : #FFF; +@footer-link-color : @ol-green; +@footer-link-hover-color : @ol-dark-green; + //== Colors // //## Gray and brand colors for use across Bootstrap. diff --git a/services/web/public/stylesheets/core/scaffolding.less b/services/web/public/stylesheets/core/scaffolding.less index 0d433739db..30a5fa5f1e 100755 --- a/services/web/public/stylesheets/core/scaffolding.less +++ b/services/web/public/stylesheets/core/scaffolding.less @@ -29,6 +29,9 @@ body { line-height: @line-height-base; color: @text-color; background-color: @body-bg; + min-height: 100vh; + display: flex; + flex-direction: column; } // Reset fonts for relevant elements From 60a76c45c355bf9283f8b30519be95aefdd321a0 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Thu, 5 Oct 2017 16:41:11 +0100 Subject: [PATCH 105/206] Some more footer adjustments. --- services/web/app/views/layout/footer.pug | 2 +- services/web/public/stylesheets/components/footer.less | 2 +- services/web/public/stylesheets/core/_common-variables.less | 1 + services/web/public/stylesheets/core/ol-variables.less | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/services/web/app/views/layout/footer.pug b/services/web/app/views/layout/footer.pug index 62a98ecfaa..009b2085ca 100644 --- a/services/web/app/views/layout/footer.pug +++ b/services/web/app/views/layout/footer.pug @@ -1,7 +1,7 @@ footer.site-footer - .container + .container-fluid .row ul.col-md-9 diff --git a/services/web/public/stylesheets/components/footer.less b/services/web/public/stylesheets/components/footer.less index c86c773aee..dacd257c83 100644 --- a/services/web/public/stylesheets/components/footer.less +++ b/services/web/public/stylesheets/components/footer.less @@ -1,7 +1,7 @@ footer.site-footer { background-color: @footer-bg-color; border-top: 1px solid @gray-lighter; - padding: 2em; + padding: @footer-padding; font-size: 0.9rem; ul { list-style: none; diff --git a/services/web/public/stylesheets/core/_common-variables.less b/services/web/public/stylesheets/core/_common-variables.less index c813b9448b..37e44c6d9f 100644 --- a/services/web/public/stylesheets/core/_common-variables.less +++ b/services/web/public/stylesheets/core/_common-variables.less @@ -850,6 +850,7 @@ @footer-link-color: @link-color; @footer-link-hover-color: @link-hover-color; @footer-bg-color: transparent; +@footer-padding: 2em; @btn-border-width: 1px; @btn-border-bottom-width: 2px; diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index ef23d37974..e9c8955cea 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -134,6 +134,7 @@ @footer-bg-color : #FFF; @footer-link-color : @ol-green; @footer-link-hover-color : @ol-dark-green; +@footer-padding : 2em 0; //== Colors // From 9047e9a365a4bf72c88a4b402734c92df32b7dd8 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Thu, 5 Oct 2017 18:16:52 +0100 Subject: [PATCH 106/206] Make SL theme closer to original. --- services/web/app/views/layout/footer.pug | 2 +- services/web/app/views/project/list.pug | 101 +++++++++--------- .../public/stylesheets/app/project-list.less | 45 +++++--- .../public/stylesheets/components/footer.less | 7 ++ .../stylesheets/core/_common-variables.less | 7 +- .../public/stylesheets/core/ol-variables.less | 17 +-- 6 files changed, 107 insertions(+), 72 deletions(-) diff --git a/services/web/app/views/layout/footer.pug b/services/web/app/views/layout/footer.pug index 009b2085ca..a0a05c2d0c 100644 --- a/services/web/app/views/layout/footer.pug +++ b/services/web/app/views/layout/footer.pug @@ -1,7 +1,7 @@ footer.site-footer - .container-fluid + .site-footer-content .row ul.col-md-9 diff --git a/services/web/app/views/project/list.pug b/services/web/app/views/project/list.pug index 17b5d7d1fb..7cab946d98 100644 --- a/services/web/app/views/project/list.pug +++ b/services/web/app/views/project/list.pug @@ -15,58 +15,59 @@ block content } }; - .container-fluid.content.content-alt.project-list-page(ng-controller="ProjectPageController") - .announcements( - ng-controller="AnnouncementsController" - ng-class="{ 'announcements-open': ui.isOpen }" - ng-cloak - ) - .announcements-backdrop( - ng-if="ui.isOpen" - ng-click="toggleAnnouncementsUI();" + .content.content-alt.project-list-page(ng-controller="ProjectPageController") + .project-list-content + .announcements( + ng-controller="AnnouncementsController" + ng-class="{ 'announcements-open': ui.isOpen }" + ng-cloak ) - a.announcements-btn( - href - ng-if="announcements.length" - ng-click="toggleAnnouncementsUI();" - ng-class="{ 'announcements-btn-open': ui.isOpen, 'announcements-btn-has-new': ui.newItems }" - ) - span.announcements-badge(ng-if="ui.newItems") {{ ui.newItems }} - .announcements-body( - ng-if="ui.isOpen" - ) - .announcements-scroller - .announcement( - ng-repeat="announcement in announcements | filter:(ui.newItems ? { read: false } : '') track by announcement.id" - ) - h2.announcement-header {{ announcement.title }} - p.announcement-description(ng-bind-html="announcement.excerpt") - .announcement-meta - p.announcement-date {{ announcement.date | date:"longDate" }} - a.announcement-link( - ng-href="{{ announcement.url }}" - ng-click="logAnnouncementClick()", - target="_blank" - ) Read more - div.text-center( - ng-if="ui.newItems > 0 && ui.newItems < announcements.length" - ) - a.btn.btn-default.btn-sm( - href - ng-click="showAll();" - ) Show all + .announcements-backdrop( + ng-if="ui.isOpen" + ng-click="toggleAnnouncementsUI();" + ) + a.announcements-btn( + href + ng-if="announcements.length" + ng-click="toggleAnnouncementsUI();" + ng-class="{ 'announcements-btn-open': ui.isOpen, 'announcements-btn-has-new': ui.newItems }" + ) + span.announcements-badge(ng-if="ui.newItems") {{ ui.newItems }} + .announcements-body( + ng-if="ui.isOpen" + ) + .announcements-scroller + .announcement( + ng-repeat="announcement in announcements | filter:(ui.newItems ? { read: false } : '') track by announcement.id" + ) + h2.announcement-header {{ announcement.title }} + p.announcement-description(ng-bind-html="announcement.excerpt") + .announcement-meta + p.announcement-date {{ announcement.date | date:"longDate" }} + a.announcement-link( + ng-href="{{ announcement.url }}" + ng-click="logAnnouncementClick()", + target="_blank" + ) Read more + div.text-center( + ng-if="ui.newItems > 0 && ui.newItems < announcements.length" + ) + a.btn.btn-default.btn-sm( + href + ng-click="showAll();" + ) Show all - .row.project-list-row(ng-cloak) - .project-list-container(ng-if="projects.length > 0") - aside.project-list-sidebar.col-md-2.col-xs-3 - include ./list/side-bar + .row.project-list-row(ng-cloak) + .project-list-container(ng-if="projects.length > 0") + aside.project-list-sidebar.col-md-2.col-xs-3 + include ./list/side-bar - .project-list-main.col-md-10.col-xs-9 - include ./list/notifications - include ./list/project-list - - .project-list-empty(ng-if="projects.length === 0") - .col-md-offset-2.col-md-8.col-md-offset-2.col-xs-8 - include ./list/empty-project-list + .project-list-main.col-md-10.col-xs-9 + include ./list/notifications + include ./list/project-list + + .project-list-empty(ng-if="projects.length === 0") + .col-md-offset-2.col-md-8.col-md-offset-2.col-xs-8 + include ./list/empty-project-list include ./list/modals diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less index f32e96f2fd..eb7aa6f833 100644 --- a/services/web/public/stylesheets/app/project-list.less +++ b/services/web/public/stylesheets/app/project-list.less @@ -27,6 +27,13 @@ width: 100%; } +.project-list-content when (@is-overleaf = true) { + .container-fluid; +} +.project-list-content when (@is-overleaf = false) { + .container; +} + .project-list-row { display: flex; flex-direction: column; @@ -108,7 +115,7 @@ } ul.folders-menu { - margin: 0 -(@grid-gutter-width / 2); + margin: @folders-menu-margin; .subdued { color: @gray-light; } @@ -116,6 +123,7 @@ ul.folders-menu { cursor: pointer; position: relative; > a { + font-size: 0.9rem; line-height: @folders-menu-line-height; display: block; color: @sidebar-link-color; @@ -126,6 +134,9 @@ ul.folders-menu { text-decoration: @sidebar-hover-text-decoration; } } + > a when (@is-overleaf = false) { + font-size: 0.9rem; + } &.separator { cursor: auto; } @@ -270,9 +281,13 @@ ul.structured-list { a { color: @structured-list-link-color; } - .header { + .header when (@is-overleaf = true) { font-weight: 600; } + + .header when (@is-overleaf = false) { + text-transform: uppercase; + } .select-item, .select-all { display: inline-block; margin: 0 (@line-height-computed / 4); @@ -299,16 +314,19 @@ ul.structured-list { ul.project-list { li { line-height: @structured-list-line-height; - padding: 0; - // .last-modified, .owner { - // font-size: .8rem; - // } - // .owner { - // margin-right: 0; - // } - // .projectName { - // margin-right: @line-height-computed / 4; - // } + .last-modified when (@is-overleaf = false) { + font-size: .8rem; + } + .owner when (@is-overleaf = false) { + font-size: .8rem; + } + .owner when (@is-overleaf = false) { + margin-right: 0; + } + .projectName when (@is-overleaf = false) { + margin-right: @line-height-computed / 4; + } + .tag-label { margin-left: @line-height-computed / 4; position: relative; @@ -338,6 +356,9 @@ ul.project-list { border-bottom-left-radius: 0; } } + li when (@is-overleaf = true) { + padding: 0; + } i.tablesort { padding-left: 8px; } diff --git a/services/web/public/stylesheets/components/footer.less b/services/web/public/stylesheets/components/footer.less index dacd257c83..673f46d672 100644 --- a/services/web/public/stylesheets/components/footer.less +++ b/services/web/public/stylesheets/components/footer.less @@ -33,6 +33,13 @@ footer.site-footer { } } +.site-footer-content when (@is-overleaf = true) { + .container-fluid; +} +.site-footer-content when (@is-overleaf = false) { + .container; +} + .sprite-icon-lang { display: inline-block; vertical-align: middle; diff --git a/services/web/public/stylesheets/core/_common-variables.less b/services/web/public/stylesheets/core/_common-variables.less index 37e44c6d9f..9d29d7b91c 100644 --- a/services/web/public/stylesheets/core/_common-variables.less +++ b/services/web/public/stylesheets/core/_common-variables.less @@ -792,6 +792,8 @@ @content-margin-top: @line-height-computed; // Custom +@is-overleaf: false; + @content-alt-bg-color: lighten(@gray-lightest, 2.5%); @navbar-default-padding: 1rem 2rem; @@ -826,7 +828,8 @@ @sidebar-hover-text-decoration: underline; @structured-list-line-height: inherit; -@folders-menu-line-height: 1.8; +@folders-menu-margin: 0; +@folders-menu-line-height: 1.2; @folders-menu-item-v-padding: (@line-height-computed / 4); @folders-title-margin-top: @line-height-computed / 2; @folders-title-margin-bottom: @line-height-computed / 4; @@ -838,7 +841,7 @@ @folders-tag-icon-position: absolute; @folders-tag-padding: 2px (@line-height-computed / 4); @folders-tag-spacing: 22px; -@folders-tag-display: inline-block; +@folders-tag-display: block; @folders-tag-margin-right: 18px; @folders-tag-menu-color: @gray; @folders-tag-hover: darken(@gray-lightest, 2%); diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index e9c8955cea..1a0e4ddde3 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -1,5 +1,7 @@ @import "./_common-variables.less"; +@is-overleaf: true; + // Styleguide colors @ol-blue-gray-1 : #E4E8EE; @ol-blue-gray-2 : #9DA7B7; @@ -102,13 +104,7 @@ @sidebar-hover-bg : @ol-blue-gray-4; @sidebar-hover-text-decoration : none; -// Project table -@structured-list-link-color : @ol-blue; -@structured-header-border-color : shade(@ol-blue-gray-1, 5%); -@structured-list-border-color : @ol-blue-gray-1; -@structured-list-hover-color : lighten(@ol-blue-gray-1, 5%); - -@structured-list-line-height : 2.5; +@folders-menu-margin : 0 -(@grid-gutter-width / 2);; @folders-menu-line-height : @structured-list-line-height; @folders-menu-item-v-padding : 0; @folders-title-margin-top : 0; @@ -130,6 +126,13 @@ @folders-tag-menu-active-hover : rgba(0, 0, 0, .1); @folders-untagged-line-height : @folders-menu-line-height; +// Project table +@structured-list-line-height : 2.5; +@structured-list-link-color : @ol-blue; +@structured-header-border-color : shade(@ol-blue-gray-1, 5%); +@structured-list-border-color : @ol-blue-gray-1; +@structured-list-hover-color : lighten(@ol-blue-gray-1, 5%); + // Footer @footer-bg-color : #FFF; @footer-link-color : @ol-green; From 045fae7109af4bfcc3f7bdb07530c9c8a56150ae Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Fri, 6 Oct 2017 10:50:59 +0100 Subject: [PATCH 107/206] Some more adjustments to make the SL theme closer to original. --- services/web/app/views/project/list/side-bar.pug | 2 +- services/web/public/stylesheets/app/project-list.less | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/services/web/app/views/project/list/side-bar.pug b/services/web/app/views/project/list/side-bar.pug index 570372001f..fe053f0019 100644 --- a/services/web/app/views/project/list/side-bar.pug +++ b/services/web/app/views/project/list/side-bar.pug @@ -1,5 +1,5 @@ .dropdown(dropdown) - a.btn.btn-primary.btn-block.dropdown-toggle( + a.btn.btn-primary.sidebar-new-proj-btn.dropdown-toggle( href="#", data-toggle="dropdown", dropdown-toggle diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less index eb7aa6f833..f47374381e 100644 --- a/services/web/public/stylesheets/app/project-list.less +++ b/services/web/public/stylesheets/app/project-list.less @@ -34,6 +34,10 @@ .container; } +.sidebar-new-proj-btn when (@is-overleaf) { + .btn-block; +} + .project-list-row { display: flex; flex-direction: column; @@ -307,7 +311,7 @@ ul.structured-list { } } -.project-list-card { +.project-list-card when (@is-overleaf) { padding: 0 (@line-height-computed / 4); } From 1c9d5828b17cda5daaf13700ffb67f80ab696e7c Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Fri, 6 Oct 2017 12:02:20 +0100 Subject: [PATCH 108/206] Even more adjustments to make the SL theme closer to original. --- .../web/public/stylesheets/app/project-list.less | 15 ++++----------- .../stylesheets/core/_common-variables.less | 12 ++++++------ .../web/public/stylesheets/core/ol-variables.less | 8 ++++---- 3 files changed, 14 insertions(+), 21 deletions(-) diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less index f47374381e..73c44d68e3 100644 --- a/services/web/public/stylesheets/app/project-list.less +++ b/services/web/public/stylesheets/app/project-list.less @@ -131,7 +131,7 @@ ul.folders-menu { line-height: @folders-menu-line-height; display: block; color: @sidebar-link-color; - padding: @folders-menu-item-v-padding (@grid-gutter-width / 2); + padding: @folders-menu-item-v-padding @folders-menu-item-h-padding; border-bottom: solid 1px transparent; &:hover { background-color: @sidebar-hover-bg; @@ -167,7 +167,7 @@ ul.folders-menu { color: @folders-title-color; line-height: @folders-title-line-height; text-transform: @folders-title-text-transform; - padding: @folders-menu-item-v-padding (@grid-gutter-width / 2); + padding: @folders-title-padding; font-weight: @folders-title-font-weight; font-family: @font-family-sans-serif; } @@ -210,18 +210,11 @@ ul.folders-menu { } a.tag-name { padding: @folders-tag-padding; - margin-right: @folders-tag-margin-right; display: @folders-tag-display; position: relative; - i { - position: @folders-tag-icon-position; - top: 5px; - left: 6px; - } span.name { - display: inline-block; - padding-left: @folders-tag-spacing; - line-height: 1.4; + padding-left: 0.5em; + line-height: @folders-tag-line-height; } } .tag-menu { diff --git a/services/web/public/stylesheets/core/_common-variables.less b/services/web/public/stylesheets/core/_common-variables.less index 9d29d7b91c..0a7c415049 100644 --- a/services/web/public/stylesheets/core/_common-variables.less +++ b/services/web/public/stylesheets/core/_common-variables.less @@ -831,18 +831,18 @@ @folders-menu-margin: 0; @folders-menu-line-height: 1.2; @folders-menu-item-v-padding: (@line-height-computed / 4); -@folders-title-margin-top: @line-height-computed / 2; -@folders-title-margin-bottom: @line-height-computed / 4; +@folders-menu-item-h-padding: (@line-height-computed / 4); +@folders-title-padding: 0; +@folders-title-margin-top: (@line-height-computed / 2); +@folders-title-margin-bottom: (@line-height-computed / 4); @folders-title-font-size: @font-size-base; @folders-title-font-weight: 500; @folders-title-line-height: @headings-line-height; @folders-title-color: inherit; @folders-title-text-transform: none; -@folders-tag-icon-position: absolute; -@folders-tag-padding: 2px (@line-height-computed / 4); -@folders-tag-spacing: 22px; +@folders-tag-padding: 2px 20px 2px @folders-menu-item-h-padding; +@folders-tag-line-height: 1.8; @folders-tag-display: block; -@folders-tag-margin-right: 18px; @folders-tag-menu-color: @gray; @folders-tag-hover: darken(@gray-lightest, 2%); @folders-tag-border-color: @text-color; diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index 1a0e4ddde3..c5abf25ffe 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -107,6 +107,8 @@ @folders-menu-margin : 0 -(@grid-gutter-width / 2);; @folders-menu-line-height : @structured-list-line-height; @folders-menu-item-v-padding : 0; +@folders-menu-item-h-padding : (@grid-gutter-width / 2); +@folders-title-padding : @folders-menu-item-v-padding @folders-menu-item-v-padding; @folders-title-margin-top : 0; @folders-title-margin-bottom : 0; @folders-title-font-weight : normal; @@ -115,10 +117,8 @@ @folders-title-color : @ol-blue-gray-3; @folders-title-text-transform : uppercase; @folders-tag-display : block; -@folders-tag-margin-right : 0; -@folders-tag-icon-position : static; -@folders-tag-padding : @folders-menu-item-v-padding (@grid-gutter-width / 2); -@folders-tag-spacing : 0.5em; +@folders-tag-line-height : 1.4; +@folders-tag-padding : @folders-menu-item-v-padding 20px @folders-menu-item-v-padding @folders-menu-item-v-padding; @folders-tag-menu-color : #FFF; @folders-tag-hover : @sidebar-hover-bg; @folders-tag-border-color : @folders-tag-menu-color; From e1775128e45b4e3d482d99ed6c5b00d784676fc6 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Fri, 6 Oct 2017 12:18:13 +0100 Subject: [PATCH 109/206] Fixed flexbox based width issues. --- services/web/public/stylesheets/app/project-list.less | 8 ++++++++ services/web/public/stylesheets/core/ol-variables.less | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less index 73c44d68e3..e0292c09de 100644 --- a/services/web/public/stylesheets/app/project-list.less +++ b/services/web/public/stylesheets/app/project-list.less @@ -29,11 +29,19 @@ .project-list-content when (@is-overleaf = true) { .container-fluid; + margin: 0; } + .project-list-content when (@is-overleaf = false) { .container; } +.project-list-content { + display: flex; + flex-direction: column; + flex: 1 0 100%; +} + .sidebar-new-proj-btn when (@is-overleaf) { .btn-block; } diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index c5abf25ffe..9c3a5fcdba 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -118,7 +118,7 @@ @folders-title-text-transform : uppercase; @folders-tag-display : block; @folders-tag-line-height : 1.4; -@folders-tag-padding : @folders-menu-item-v-padding 20px @folders-menu-item-v-padding @folders-menu-item-v-padding; +@folders-tag-padding : @folders-menu-item-v-padding 20px @folders-menu-item-v-padding @folders-menu-item-h-padding; @folders-tag-menu-color : #FFF; @folders-tag-hover : @sidebar-hover-bg; @folders-tag-border-color : @folders-tag-menu-color; From f405dc49b3eb1b07a2363a4dd6225ee5fc5ca20d Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Fri, 6 Oct 2017 14:22:06 +0100 Subject: [PATCH 110/206] Implement two different scrolling models for OL and SL. --- .../public/stylesheets/app/project-list.less | 38 ++++++++++--------- .../public/stylesheets/components/footer.less | 4 ++ .../public/stylesheets/components/navbar.less | 5 ++- .../stylesheets/core/_common-variables.less | 2 + .../public/stylesheets/core/ol-variables.less | 2 + .../public/stylesheets/core/scaffolding.less | 5 +-- 6 files changed, 34 insertions(+), 22 deletions(-) diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less index e0292c09de..91dc87e875 100644 --- a/services/web/public/stylesheets/app/project-list.less +++ b/services/web/public/stylesheets/app/project-list.less @@ -18,10 +18,12 @@ } .project-list-page { - display: flex; - flex-direction: column; - flex-grow: 1; - position: relative; + // display: flex; + // flex-direction: column; + // flex-grow: 1; + position: absolute; + top: @header-height; + bottom: @footer-height; padding-top: 0; padding-bottom: 0; width: 100%; @@ -37,9 +39,7 @@ } .project-list-content { - display: flex; - flex-direction: column; - flex: 1 0 100%; + height: 100%; } .sidebar-new-proj-btn when (@is-overleaf) { @@ -47,29 +47,31 @@ } .project-list-row { - display: flex; - flex-direction: column; - flex-grow: 1; + height: 100%; } .project-list-container { - display: flex; - align-items: stretch; - flex-grow: 1; + height: 100%; } - - .project-list-empty { - + .project-list-content when (@is-overleaf = false) { + overflow: scroll; } .project-list-sidebar { background-color: @sidebar-bg; padding-top: @content-margin-top; - padding-bottom: @content-margin-top; + padding-bottom: @content-margin-top; } + .project-list-sidebar when (@is-overleaf) { + height: 100%; + overflow: scroll; + } + .project-list-main { padding-top: @content-margin-top; - padding-bottom: @content-margin-top; + padding-bottom: @content-margin-top; + height: 100%; + overflow: hidden; } .project-header { diff --git a/services/web/public/stylesheets/components/footer.less b/services/web/public/stylesheets/components/footer.less index 673f46d672..d1477a1b95 100644 --- a/services/web/public/stylesheets/components/footer.less +++ b/services/web/public/stylesheets/components/footer.less @@ -3,6 +3,10 @@ footer.site-footer { border-top: 1px solid @gray-lighter; padding: @footer-padding; font-size: 0.9rem; + position: absolute; + bottom: 0; + width: 100%; + ul { list-style: none; margin: 0px; diff --git a/services/web/public/stylesheets/components/navbar.less b/services/web/public/stylesheets/components/navbar.less index 230d62b6b4..0445ad8c00 100755 --- a/services/web/public/stylesheets/components/navbar.less +++ b/services/web/public/stylesheets/components/navbar.less @@ -379,7 +379,10 @@ background-color: @navbar-default-bg; border-color: @navbar-default-border; padding: @navbar-default-padding; - + position: absolute; + top: 0; + width: 100%; + .navbar-brand { position: absolute; top: 5px; diff --git a/services/web/public/stylesheets/core/_common-variables.less b/services/web/public/stylesheets/core/_common-variables.less index 0a7c415049..1b5873a7dd 100644 --- a/services/web/public/stylesheets/core/_common-variables.less +++ b/services/web/public/stylesheets/core/_common-variables.less @@ -793,6 +793,8 @@ // Custom @is-overleaf: false; +@header-height: 69px; +@footer-height: 81px; @content-alt-bg-color: lighten(@gray-lightest, 2.5%); diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index 9c3a5fcdba..9067d2744f 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -1,6 +1,8 @@ @import "./_common-variables.less"; @is-overleaf: true; +@header-height: 69px; +@footer-height: 81px; // Styleguide colors @ol-blue-gray-1 : #E4E8EE; diff --git a/services/web/public/stylesheets/core/scaffolding.less b/services/web/public/stylesheets/core/scaffolding.less index 30a5fa5f1e..78c919d8bb 100755 --- a/services/web/public/stylesheets/core/scaffolding.less +++ b/services/web/public/stylesheets/core/scaffolding.less @@ -29,9 +29,8 @@ body { line-height: @line-height-base; color: @text-color; background-color: @body-bg; - min-height: 100vh; - display: flex; - flex-direction: column; + height: 100vh; + position: relative; } // Reset fonts for relevant elements From 9d35ce5fe1c6e9592dbf221b2c6b5eb2147e1101 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Fri, 6 Oct 2017 14:26:34 +0100 Subject: [PATCH 111/206] Fix label issues. --- services/web/public/stylesheets/core/_common-variables.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/public/stylesheets/core/_common-variables.less b/services/web/public/stylesheets/core/_common-variables.less index 1b5873a7dd..927663b65d 100644 --- a/services/web/public/stylesheets/core/_common-variables.less +++ b/services/web/public/stylesheets/core/_common-variables.less @@ -861,8 +861,8 @@ @btn-border-bottom-width: 2px; @tag-border-radius: 0.25em; -@tag-bg-color: inherit; -@tag-bg-hover-color: inherit; +@tag-bg-color: @label-default-bg; +@tag-bg-hover-color: darken(@label-default-bg, 10%); @left-menu-width: 260px; @left-menu-animation-duration: 0.35s; From fd215280a8e8cd22c4691a259476f7840502e8b4 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Fri, 6 Oct 2017 15:54:54 +0100 Subject: [PATCH 112/206] Some more scroll fixes. --- .../main/project-list/project-list.coffee | 4 +-- .../public/stylesheets/app/project-list.less | 27 ++++++++++--------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/services/web/public/coffee/main/project-list/project-list.coffee b/services/web/public/coffee/main/project-list/project-list.coffee index cafbd7c81b..62bae9d2db 100644 --- a/services/web/public/coffee/main/project-list/project-list.coffee +++ b/services/web/public/coffee/main/project-list/project-list.coffee @@ -38,8 +38,8 @@ define [ # footer. So we now start to track to the bottom of the window, with a 25px padding # since the footer is hidden below the fold. Don't ever get bigger than the sidebar # though since that's what triggered this happening in the first place. - if height < sideBarHeight - height = Math.min(sideBarHeight, $window.innerHeight - topOffset - 25) + # if height < sideBarHeight + # height = Math.min(sideBarHeight, $window.innerHeight - topOffset - 25) $scope.projectListHeight = height diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less index 91dc87e875..41e5df7ce2 100644 --- a/services/web/public/stylesheets/app/project-list.less +++ b/services/web/public/stylesheets/app/project-list.less @@ -18,44 +18,45 @@ } .project-list-page { - // display: flex; - // flex-direction: column; - // flex-grow: 1; position: absolute; top: @header-height; bottom: @footer-height; padding-top: 0; padding-bottom: 0; width: 100%; + overflow: scroll; } -.project-list-content when (@is-overleaf = true) { +.project-list-content when (@is-overleaf) { .container-fluid; margin: 0; + height: 100%; } .project-list-content when (@is-overleaf = false) { .container; } -.project-list-content { - height: 100%; -} +// .project-list-content when (@is-overleaf) { +// height: 100%; +// } + +// .project-list-content when (@is-overleaf = false) { +// overflow: scroll; +// } + + .sidebar-new-proj-btn when (@is-overleaf) { .btn-block; } - .project-list-row { + .project-list-row when (@is-overleaf) { height: 100%; } - .project-list-container { + .project-list-container when (@is-overleaf) { height: 100%; } - .project-list-content when (@is-overleaf = false) { - overflow: scroll; - } - .project-list-sidebar { background-color: @sidebar-bg; padding-top: @content-margin-top; From 4b246501d22980a8b8a0d78eb938ff18bf34383e Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Mon, 9 Oct 2017 10:28:03 +0100 Subject: [PATCH 113/206] Fix the footer. --- services/web/public/stylesheets/components/footer.less | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/web/public/stylesheets/components/footer.less b/services/web/public/stylesheets/components/footer.less index d1477a1b95..a5e0719157 100644 --- a/services/web/public/stylesheets/components/footer.less +++ b/services/web/public/stylesheets/components/footer.less @@ -1,11 +1,12 @@ footer.site-footer { background-color: @footer-bg-color; border-top: 1px solid @gray-lighter; - padding: @footer-padding; font-size: 0.9rem; position: absolute; bottom: 0; width: 100%; + height: @footer-height; + line-height: @footer-height; ul { list-style: none; From 6e6c058d49950f3cbdbe142e8beaa8b8005ff4e2 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Mon, 9 Oct 2017 10:28:21 +0100 Subject: [PATCH 114/206] Fix the header. --- services/web/public/stylesheets/components/navbar.less | 1 + 1 file changed, 1 insertion(+) diff --git a/services/web/public/stylesheets/components/navbar.less b/services/web/public/stylesheets/components/navbar.less index 0445ad8c00..113d67e7c2 100755 --- a/services/web/public/stylesheets/components/navbar.less +++ b/services/web/public/stylesheets/components/navbar.less @@ -382,6 +382,7 @@ position: absolute; top: 0; width: 100%; + height: @header-height; .navbar-brand { position: absolute; From 435a5b6a43bebcae03bf437abfb2a10047b1256e Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Mon, 9 Oct 2017 10:30:09 +0100 Subject: [PATCH 115/206] Adjust the body to take header and footer into account with padding. --- services/web/public/stylesheets/core/_common-variables.less | 2 +- services/web/public/stylesheets/core/ol-variables.less | 4 ++-- services/web/public/stylesheets/core/scaffolding.less | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/services/web/public/stylesheets/core/_common-variables.less b/services/web/public/stylesheets/core/_common-variables.less index 927663b65d..039c0c890d 100644 --- a/services/web/public/stylesheets/core/_common-variables.less +++ b/services/web/public/stylesheets/core/_common-variables.less @@ -794,7 +794,7 @@ // Custom @is-overleaf: false; @header-height: 69px; -@footer-height: 81px; +@footer-height: 50px; @content-alt-bg-color: lighten(@gray-lightest, 2.5%); diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index 9067d2744f..0a884fbc3f 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -1,8 +1,8 @@ @import "./_common-variables.less"; @is-overleaf: true; -@header-height: 69px; -@footer-height: 81px; +@header-height: 70px; +@footer-height: 50px; // Styleguide colors @ol-blue-gray-1 : #E4E8EE; diff --git a/services/web/public/stylesheets/core/scaffolding.less b/services/web/public/stylesheets/core/scaffolding.less index 78c919d8bb..3258e36c2c 100755 --- a/services/web/public/stylesheets/core/scaffolding.less +++ b/services/web/public/stylesheets/core/scaffolding.less @@ -29,8 +29,10 @@ body { line-height: @line-height-base; color: @text-color; background-color: @body-bg; - height: 100vh; + min-height: 100vh; position: relative; + padding-top: @header-height; + padding-bottom: @footer-height; } // Reset fonts for relevant elements From 500dcc5bc2fde67d3d00b2c795563fc48853cfaf Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Mon, 9 Oct 2017 11:59:03 +0100 Subject: [PATCH 116/206] Quick accessibility fixes for the dashboard. --- services/web/app/views/layout/footer.pug | 2 +- services/web/app/views/layout/navbar.pug | 6 +++--- services/web/app/views/project/list/project-list.pug | 3 +++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/services/web/app/views/layout/footer.pug b/services/web/app/views/layout/footer.pug index a0a05c2d0c..7bf47b24f7 100644 --- a/services/web/app/views/layout/footer.pug +++ b/services/web/app/views/layout/footer.pug @@ -38,6 +38,6 @@ footer.site-footer each item in nav.right_footer li if item.url - a(href=item.url, class=item.class) !{item.text} + a(href=item.url, class=item.class, aria-label=item.label) !{item.text} else | !{item.text} diff --git a/services/web/app/views/layout/navbar.pug b/services/web/app/views/layout/navbar.pug index 54509d6565..9e242e1f25 100644 --- a/services/web/app/views/layout/navbar.pug +++ b/services/web/app/views/layout/navbar.pug @@ -4,11 +4,11 @@ nav.navbar.navbar-default button.navbar-toggle(ng-init="navCollapsed = true", ng-click="navCollapsed = !navCollapsed", ng-class="{active: !navCollapsed}") i.fa.fa-bars if settings.nav.custom_logo - a(href='/', style='background-image:url("'+settings.nav.custom_logo+'")').navbar-brand + a(href='/', aria-label=settings.appName, style='background-image:url("'+settings.nav.custom_logo+'")').navbar-brand else if (nav.title) - a(href='/').navbar-title #{nav.title} + a(href='/', aria-label=settings.appName).navbar-title #{nav.title} else - a(href='/').navbar-brand + a(href='/', aria-label=settings.appName).navbar-brand .navbar-collapse.collapse(collapse="navCollapsed") diff --git a/services/web/app/views/project/list/project-list.pug b/services/web/app/views/project/list/project-list.pug index 629c3dc62d..a4295a1843 100644 --- a/services/web/app/views/project/list/project-list.pug +++ b/services/web/app/views/project/list/project-list.pug @@ -7,6 +7,7 @@ .form-group.has-feedback.has-feedback-left.col-md-7.col-xs-12 input.form-control.col-md-7.col-xs-12( placeholder=translate('search_projects')+"…", + aria-label=translate('search_projects')+"…", autofocus='autofocus', ng-model="searchText.value", focus-on='search:clear', @@ -127,6 +128,7 @@ input.select-all( select-all, type="checkbox" + aria-label="Select all" ) span.header.clickable(ng-click="changePredicate('name')") #{translate("title")} i.tablesort.fa(ng-class="getSortIconClass('name')") @@ -147,6 +149,7 @@ type="checkbox", ng-model="project.selected" stop-propagation="click" + aria-label="Select {{ project.name }}" ) span a.projectName( From 9eec24535873996db00cb2c63fb470ea4035716f Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 9 Oct 2017 14:17:28 +0100 Subject: [PATCH 117/206] allow docupdater to flush docs in background --- .../web/app/coffee/Features/Compile/ClsiManager.coffee | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/services/web/app/coffee/Features/Compile/ClsiManager.coffee b/services/web/app/coffee/Features/Compile/ClsiManager.coffee index 46cb4e35e3..d5f9b2e16c 100755 --- a/services/web/app/coffee/Features/Compile/ClsiManager.coffee +++ b/services/web/app/coffee/Features/Compile/ClsiManager.coffee @@ -146,13 +146,8 @@ module.exports = ClsiManager = logger.log project_id: project_id, projectStateHash: projectStateHash, docs: docUpdaterDocs?, "checked project state" # see if we can send an incremental update to the CLSI if docUpdaterDocs? and (options.syncType isnt "full") and not error? - # Workaround: for now, always flush project to mongo on compile - # until we have automatic periodic flushing on the docupdater - # side, to prevent documents staying in redis too long. - DocumentUpdaterHandler.flushProjectToMongo project_id, (error) -> - return callback(error) if error? - Metrics.inc "compile-from-redis" - ClsiManager._buildRequestFromDocupdater project_id, options, project, projectStateHash, docUpdaterDocs, callback + Metrics.inc "compile-from-redis" + ClsiManager._buildRequestFromDocupdater project_id, options, project, projectStateHash, docUpdaterDocs, callback else Metrics.inc "compile-from-mongo" ClsiManager._buildRequestFromMongo project_id, options, project, projectStateHash, callback From 8c9ad69dfe967344377d16de333a1b4f4be3c085 Mon Sep 17 00:00:00 2001 From: James Allen Date: Mon, 9 Oct 2017 14:22:04 +0100 Subject: [PATCH 118/206] Always npm install translations from master --- services/web/Jenkinsfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/web/Jenkinsfile b/services/web/Jenkinsfile index 5c7b4e160f..714adc5f3d 100644 --- a/services/web/Jenkinsfile +++ b/services/web/Jenkinsfile @@ -60,6 +60,9 @@ pipeline { sh 'mv app/views/external/googlebdb0f8f7f4a17241.html public/googlebdb0f8f7f4a17241.html' sh 'npm install' sh 'npm rebuild' + // It's too easy to end shrinkwrapping to a local, outdated version of translations. + // Ensure translations are always latest, regardless of shrinkwrap + sh 'npm install git+https://github.com/sharelatex/translations-sharelatex.git#master' sh 'npm install --quiet grunt' sh 'npm install --quiet grunt-cli' sh 'ls -l node_modules/.bin' From 24dd89bc3861e943f0add5bc1cbc3b6c962584b9 Mon Sep 17 00:00:00 2001 From: James Allen Date: Mon, 9 Oct 2017 14:23:05 +0100 Subject: [PATCH 119/206] Adjust wording --- services/web/Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/Jenkinsfile b/services/web/Jenkinsfile index 714adc5f3d..50ced29d60 100644 --- a/services/web/Jenkinsfile +++ b/services/web/Jenkinsfile @@ -60,7 +60,7 @@ pipeline { sh 'mv app/views/external/googlebdb0f8f7f4a17241.html public/googlebdb0f8f7f4a17241.html' sh 'npm install' sh 'npm rebuild' - // It's too easy to end shrinkwrapping to a local, outdated version of translations. + // It's too easy to end up shrinkwrapping to an outdated version of translations. // Ensure translations are always latest, regardless of shrinkwrap sh 'npm install git+https://github.com/sharelatex/translations-sharelatex.git#master' sh 'npm install --quiet grunt' From 15e2deed7321a1edcb3410a5d317cb25a0f143a2 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 9 Oct 2017 15:18:55 +0100 Subject: [PATCH 120/206] rename isAutoCompile and isBackgroundAutoCompile changed to isAutoCompileOnLoad and isAutoCompileOnChange --- .../coffee/ide/pdf/controllers/PdfController.coffee | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index e77d4a22cb..bb27057fc7 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -68,7 +68,7 @@ define [ $scope.$on "project:joined", () -> return if !autoCompile autoCompile = false - $scope.recompile(isAutoCompile: true) + $scope.recompile(isAutoCompileOnLoad: true) $scope.hasPremiumCompile = $scope.project.features.compileGroup == "priority" $scope.$on "pdf:error:display", () -> @@ -86,7 +86,7 @@ define [ if isTimeNonMonotonic || timeSinceLastCompile >= AUTO_COMPILE_TIMEOUT if (!ide.$scope.hasLintingError) - $scope.recompile(isBackgroundAutoCompile: true) + $scope.recompile(isAutoCompileOnChange: true) else # Extend remainder of timeout autoCompileTimeout = setTimeout () -> @@ -127,7 +127,7 @@ define [ sendCompileRequest = (options = {}) -> url = "/project/#{$scope.project_id}/compile" params = {} - if options.isAutoCompile or options.isBackgroundAutoCompile + if options.isAutoCompileOnLoad or options.isAutoCompileOnChange params["auto_compile"]=true # if the previous run was a check, clear the error logs $scope.pdf.logEntries = [] if $scope.check @@ -207,7 +207,7 @@ define [ $scope.shouldShowLogs = true fetchLogs(fileByPath) else if response.status == "autocompile-backoff" - if $scope.pdf.isAutoCompile # initial autocompile + if $scope.pdf.isAutoCompileOnLoad # initial autocompile $scope.pdf.view = 'uncompiled' else # background autocompile from typing $scope.pdf.view = 'errors' @@ -422,7 +422,7 @@ define [ event_tracking.sendMBSampled "editor-recompile-sampled", options $scope.pdf.compiling = true - $scope.pdf.isAutoCompile = options?.isAutoCompile # initial autocompile + $scope.pdf.isAutoCompileOnLoad = options?.isAutoCompileOnLoad # initial autocompile if options?.force # for forced compile, turn off validation check and ignore errors From ea896380106b51c6ef45f401fb9f314d8fca28b9 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 9 Oct 2017 15:21:01 +0100 Subject: [PATCH 121/206] rename autocompile_disabled to autoCompileDisabled for consistency --- services/web/app/views/project/editor/pdf.pug | 2 +- .../public/coffee/ide/pdf/controllers/PdfController.coffee | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/services/web/app/views/project/editor/pdf.pug b/services/web/app/views/project/editor/pdf.pug index 02531e9584..f48171c7d0 100644 --- a/services/web/app/views/project/editor/pdf.pug +++ b/services/web/app/views/project/editor/pdf.pug @@ -392,7 +392,7 @@ div.full-size.pdf(ng-controller="PdfController") ) #{translate("start_free_trial")} - .alert.alert-danger(ng-show="pdf.autocompile_disabled") + .alert.alert-danger(ng-show="pdf.autoCompileDisabled") p strong #{translate("autocompile_disabled")}. span #{translate("autocompile_disabled_reason")} diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index bb27057fc7..2eb1a59eb2 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -168,7 +168,7 @@ define [ $scope.pdf.compileExited = false $scope.pdf.failedCheck = false $scope.pdf.compileInProgress = false - $scope.pdf.autocompile_disabled = false + $scope.pdf.autoCompileDisabled = false # make a cache to look up files by name fileByPath = {} @@ -211,7 +211,7 @@ define [ $scope.pdf.view = 'uncompiled' else # background autocompile from typing $scope.pdf.view = 'errors' - $scope.pdf.autocompile_disabled = true + $scope.pdf.autoCompileDisabled = true $scope.autocompile_enabled = false # disable any further autocompiles event_tracking.sendMB "autocompile-rate-limited", {hasPremiumCompile: $scope.hasPremiumCompile} else if response.status == "project-too-large" From 407157e3eb97750d8019b7becac7783b7e29288e Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Mon, 9 Oct 2017 15:27:01 +0100 Subject: [PATCH 122/206] Minor adjustmnents. --- services/web/public/stylesheets/app/project-list.less | 7 +++++++ .../web/public/stylesheets/core/_common-variables.less | 4 +++- services/web/public/stylesheets/core/ol-variables.less | 9 ++++++--- services/web/public/stylesheets/core/type.less | 2 +- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less index 41e5df7ce2..b5193ddefd 100644 --- a/services/web/public/stylesheets/app/project-list.less +++ b/services/web/public/stylesheets/app/project-list.less @@ -102,6 +102,9 @@ } p { margin-bottom: @line-height-computed / 4; + &.small { + color: @sidebar-color; + } } } @@ -148,11 +151,15 @@ ul.folders-menu { background-color: @sidebar-hover-bg; text-decoration: @sidebar-hover-text-decoration; } + &:focus { + text-decoration: none; + } } > a when (@is-overleaf = false) { font-size: 0.9rem; } &.separator { + padding: @folders-menu-item-v-padding @folders-menu-item-h-padding; cursor: auto; } } diff --git a/services/web/public/stylesheets/core/_common-variables.less b/services/web/public/stylesheets/core/_common-variables.less index 039c0c890d..576aa6d6d4 100644 --- a/services/web/public/stylesheets/core/_common-variables.less +++ b/services/web/public/stylesheets/core/_common-variables.less @@ -793,8 +793,9 @@ // Custom @is-overleaf: false; -@header-height: 69px; +@header-height: 68px; @footer-height: 50px; +@text-small-color: @gray; @content-alt-bg-color: lighten(@gray-lightest, 2.5%); @@ -823,6 +824,7 @@ @structured-list-hover-color: @gray-lightest; @sidebar-bg: transparent; +@sidebar-color: @gray; @sidebar-link-color: #333; @sidebar-active-border-radius: @border-radius-small; @sidebar-active-bg: @link-color; diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index 0a884fbc3f..4598490c4a 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -1,7 +1,7 @@ @import "./_common-variables.less"; @is-overleaf: true; -@header-height: 70px; +@header-height: 68px; @footer-height: 50px; // Styleguide colors @@ -29,10 +29,11 @@ @navbar-brand-image-url : url(/img/ol-brand/overleaf-white.svg); // Backgrounds -@body-bg : @ol-blue-gray-1; +@body-bg : #FFF; @content-alt-bg-color : @ol-blue-gray-1; // Typography +@text-small-color : @ol-type-color; @text-color : @ol-type-color; @link-color : @ol-blue; @link-hover-color : @ol-dark-blue; @@ -84,6 +85,7 @@ @navbar-subdued-hover-color : @ol-green; // Forms +@input-color : @ol-blue-gray-3; @input-border-radius : unit(@line-height-base, em); @input-height-base : @line-height-computed + (@padding-base-vertical * 2) - 1; // TODO Warning color-orange? @@ -100,6 +102,7 @@ // Sidebar @sidebar-bg : @ol-blue-gray-5; +@sidebar-color : @ol-blue-gray-2; @sidebar-link-color : #FFF; @sidebar-active-border-radius : 0; @sidebar-active-bg : @ol-blue-gray-6; @@ -116,7 +119,7 @@ @folders-title-font-weight : normal; @folders-title-font-size : @font-size-small; @folders-title-line-height : unit((@font-size-base / @font-size-small) * @folders-menu-line-height); -@folders-title-color : @ol-blue-gray-3; +@folders-title-color : @ol-blue-gray-2; @folders-title-text-transform : uppercase; @folders-tag-display : block; @folders-tag-line-height : 1.4; diff --git a/services/web/public/stylesheets/core/type.less b/services/web/public/stylesheets/core/type.less index 56c7d236ea..ac1dcf3765 100755 --- a/services/web/public/stylesheets/core/type.less +++ b/services/web/public/stylesheets/core/type.less @@ -81,7 +81,7 @@ p { // Ex: 14px base font * 85% = about 12px small, -.small { font-size: 90%; color: @gray } +.small { font-size: 90%; color: @text-small-color; } // Undo browser default styling cite { font-style: normal; } From 8a3f104c60613ce6b22609a99fe4b8b0f8dd6e94 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Mon, 9 Oct 2017 16:19:02 +0100 Subject: [PATCH 123/206] Fix some issues with longer project names. --- .../public/stylesheets/app/project-list.less | 23 +++++++------------ .../stylesheets/core/_common-variables.less | 2 +- .../public/stylesheets/core/ol-variables.less | 3 +-- 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less index b5193ddefd..19a6c18e77 100644 --- a/services/web/public/stylesheets/app/project-list.less +++ b/services/web/public/stylesheets/app/project-list.less @@ -141,8 +141,6 @@ ul.folders-menu { cursor: pointer; position: relative; > a { - font-size: 0.9rem; - line-height: @folders-menu-line-height; display: block; color: @sidebar-link-color; padding: @folders-menu-item-v-padding @folders-menu-item-h-padding; @@ -183,7 +181,6 @@ ul.folders-menu { margin-bottom: @folders-title-margin-bottom; font-size: @folders-title-font-size; color: @folders-title-color; - line-height: @folders-title-line-height; text-transform: @folders-title-text-transform; padding: @folders-title-padding; font-weight: @folders-title-font-weight; @@ -303,17 +300,17 @@ ul.structured-list { .header when (@is-overleaf = false) { text-transform: uppercase; } - .select-item, .select-all { - display: inline-block; - margin: 0 (@line-height-computed / 4); - } // .select-item, .select-all { - // position: absolute; - // left: @line-height-computed; + // display: inline-block; + // margin: 0 (@line-height-computed / 4); // } + .select-item, .select-all { + position: absolute; + left: @line-height-computed; + } .select-item + span, .select-all + span { display: inline-block; - padding-left: (@line-height-computed / 4); + padding-left: @line-height-computed * 1.5; max-width: 100%; overflow: hidden; text-overflow: ellipsis; @@ -328,7 +325,6 @@ ul.structured-list { ul.project-list { li { - line-height: @structured-list-line-height; .last-modified when (@is-overleaf = false) { font-size: .8rem; } @@ -338,7 +334,7 @@ ul.project-list { .owner when (@is-overleaf = false) { margin-right: 0; } - .projectName when (@is-overleaf = false) { + .projectName { margin-right: @line-height-computed / 4; } @@ -371,9 +367,6 @@ ul.project-list { border-bottom-left-radius: 0; } } - li when (@is-overleaf = true) { - padding: 0; - } i.tablesort { padding-left: 8px; } diff --git a/services/web/public/stylesheets/core/_common-variables.less b/services/web/public/stylesheets/core/_common-variables.less index 576aa6d6d4..644fe4c88e 100644 --- a/services/web/public/stylesheets/core/_common-variables.less +++ b/services/web/public/stylesheets/core/_common-variables.less @@ -822,6 +822,7 @@ @structured-header-border-color: @gray-lightest; @structured-list-border-color: @gray-lightest; @structured-list-hover-color: @gray-lightest; +@structured-list-line-height: @line-height-base; @sidebar-bg: transparent; @sidebar-color: @gray; @@ -831,7 +832,6 @@ @sidebar-hover-bg: transparent; @sidebar-hover-text-decoration: underline; -@structured-list-line-height: inherit; @folders-menu-margin: 0; @folders-menu-line-height: 1.2; @folders-menu-item-v-padding: (@line-height-computed / 4); diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index 4598490c4a..e8aa2fe22b 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -111,14 +111,13 @@ @folders-menu-margin : 0 -(@grid-gutter-width / 2);; @folders-menu-line-height : @structured-list-line-height; -@folders-menu-item-v-padding : 0; +@folders-menu-item-v-padding : (@line-height-computed / 4); @folders-menu-item-h-padding : (@grid-gutter-width / 2); @folders-title-padding : @folders-menu-item-v-padding @folders-menu-item-v-padding; @folders-title-margin-top : 0; @folders-title-margin-bottom : 0; @folders-title-font-weight : normal; @folders-title-font-size : @font-size-small; -@folders-title-line-height : unit((@font-size-base / @font-size-small) * @folders-menu-line-height); @folders-title-color : @ol-blue-gray-2; @folders-title-text-transform : uppercase; @folders-tag-display : block; From 5b0d3d1429cfc369bd6bcd1d0f21ac8f46645c7f Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 9 Oct 2017 16:31:01 +0100 Subject: [PATCH 124/206] simplify rate-limit checking code --- .../Features/Compile/CompileManager.coffee | 47 ++++++------------- services/web/config/settings.defaults.coffee | 5 ++ .../coffee/Compile/CompileManagerTests.coffee | 16 +++---- 3 files changed, 28 insertions(+), 40 deletions(-) diff --git a/services/web/app/coffee/Features/Compile/CompileManager.coffee b/services/web/app/coffee/Features/Compile/CompileManager.coffee index 756222121a..9e50b280e2 100755 --- a/services/web/app/coffee/Features/Compile/CompileManager.coffee +++ b/services/web/app/coffee/Features/Compile/CompileManager.coffee @@ -18,7 +18,7 @@ module.exports = CompileManager = timer.done() _callback(args...) - @_checkIfAutoCompileLimitHasBeenHit options.isAutoCompile, (err, canCompile)-> + @_checkIfAutoCompileLimitHasBeenHit options.isAutoCompile, "everyone", (err, canCompile)-> if !canCompile return callback null, "autocompile-backoff", [] logger.log project_id: project_id, user_id: user_id, "compiling project" @@ -34,8 +34,8 @@ module.exports = CompileManager = return callback(error) if error? for key, value of limits options[key] = value - # Put a lower limit on autocompiles for free users - CompileManager._checkIfFreeAutoCompileLimitHasBeenHit options.isAutoCompile, limits.compileGroup, (err, canCompile)-> + # Put a lower limit on autocompiles for free users, based on compileGroup + CompileManager._checkCompileGroupAutoCompileLimit options.isAutoCompile, limits.compileGroup, (err, canCompile)-> if !canCompile return callback null, "autocompile-backoff", [] # only pass user_id down to clsi if this is a per-user compile @@ -76,44 +76,27 @@ module.exports = CompileManager = else return callback null, true - _checkIfAutoCompileLimitHasBeenHit: (isAutoCompile, callback = (err, canCompile)->)-> + _checkCompileGroupAutoCompileLimit: (isAutoCompile, compileGroup, callback = (err, canCompile)->)-> + if compileGroup is "default" + CompileManager._checkIfAutoCompileLimitHasBeenHit isAutoCompile, compileGroup, callback + else + Metrics.inc "auto-compile-#{compileGroup}" + return callback(null, true) # always allow priority group users to compile + + _checkIfAutoCompileLimitHasBeenHit: (isAutoCompile, compileGroup, callback = (err, canCompile)->)-> if !isAutoCompile return callback(null, true) - Metrics.inc "auto-compile" + Metrics.inc "auto-compile-#{compileGroup}" opts = endpointName:"auto_compile" timeInterval:20 - subjectName:"everyone" - throttle: 200 + subjectName:compileGroup + throttle: Settings?.rateLimit?.autoCompile?[compileGroup] || 25 rateLimiter.addCount opts, (err, canCompile)-> if err? canCompile = false - logger.log canCompile:canCompile, opts:opts, "checking if auto compile limit has been hit" if !canCompile - Metrics.inc "auto-compile-rate-limited" - callback err, canCompile - - - _checkIfFreeAutoCompileLimitHasBeenHit: (isAutoCompile, compileGroup, callback = (err, canCompile)->)-> - if !isAutoCompile - return callback(null, true) - - if compileGroup is "priority" - Metrics.inc "auto-compile-priority" - return callback(null, true) - - Metrics.inc "auto-compile-free" - opts = - endpointName:"auto_compile" - timeInterval:20 - subjectName:"free" - throttle: 100 - rateLimiter.addCount opts, (err, canCompile)-> - if err? - canCompile = false - logger.log canCompile:canCompile, opts:opts, "checking if free users auto compile limit has been hit" - if !canCompile - Metrics.inc "auto-compile-free-rate-limited" + Metrics.inc "auto-compile-#{compileGroup}-limited" callback err, canCompile _ensureRootDocumentIsSet: (project_id, callback = (error) ->) -> diff --git a/services/web/config/settings.defaults.coffee b/services/web/config/settings.defaults.coffee index 2456590709..94e9ae5007 100644 --- a/services/web/config/settings.defaults.coffee +++ b/services/web/config/settings.defaults.coffee @@ -437,3 +437,8 @@ module.exports = settings = # name : "all projects", # url: "/templates/all" #}] + + rateLimits: + autoCompile: + everyone: 100 + standard: 25 diff --git a/services/web/test/UnitTests/coffee/Compile/CompileManagerTests.coffee b/services/web/test/UnitTests/coffee/Compile/CompileManagerTests.coffee index 21327a50c9..195da8b850 100644 --- a/services/web/test/UnitTests/coffee/Compile/CompileManagerTests.coffee +++ b/services/web/test/UnitTests/coffee/Compile/CompileManagerTests.coffee @@ -44,7 +44,7 @@ describe "CompileManager", -> describe "succesfully", -> beforeEach -> - @CompileManager._checkIfAutoCompileLimitHasBeenHit = (_, cb)-> cb(null, true) + @CompileManager._checkIfAutoCompileLimitHasBeenHit = (isAutoCompile, compileGroup, cb)-> cb(null, true) @CompileManager.compile @project_id, @user_id, {}, @callback it "should check the project has not been recently compiled", -> @@ -84,7 +84,7 @@ describe "CompileManager", -> describe "when the project has been recently compiled", -> it "should return", (done)-> - @CompileManager._checkIfAutoCompileLimitHasBeenHit = (_, cb)-> cb(null, true) + @CompileManager._checkIfAutoCompileLimitHasBeenHit = (isAutoCompile, compileGroup, cb)-> cb(null, true) @CompileManager._checkIfRecentlyCompiled = sinon.stub().callsArgWith(2, null, true) @CompileManager.compile @project_id, @user_id, {}, (err, status)-> status.should.equal "too-recently-compiled" @@ -92,7 +92,7 @@ describe "CompileManager", -> describe "should check the rate limit", -> it "should return", (done)-> - @CompileManager._checkIfAutoCompileLimitHasBeenHit = sinon.stub().callsArgWith(1, null, false) + @CompileManager._checkIfAutoCompileLimitHasBeenHit = sinon.stub().callsArgWith(2, null, false) @CompileManager.compile @project_id, @user_id, {}, (err, status)-> status.should.equal "autocompile-backoff" done() @@ -222,14 +222,14 @@ describe "CompileManager", -> describe "_checkIfAutoCompileLimitHasBeenHit", -> it "should be able to compile if it is not an autocompile", (done)-> - @ratelimiter.addCount.callsArgWith(1, null, true) - @CompileManager._checkIfAutoCompileLimitHasBeenHit false, (err, canCompile)=> + @ratelimiter.addCount.callsArgWith(2, null, true) + @CompileManager._checkIfAutoCompileLimitHasBeenHit false, "everyone", (err, canCompile)=> canCompile.should.equal true done() it "should be able to compile if rate limit has remianing", (done)-> @ratelimiter.addCount.callsArgWith(1, null, true) - @CompileManager._checkIfAutoCompileLimitHasBeenHit true, (err, canCompile)=> + @CompileManager._checkIfAutoCompileLimitHasBeenHit true, "everyone", (err, canCompile)=> args = @ratelimiter.addCount.args[0][0] args.throttle.should.equal 25 args.subjectName.should.equal "everyone" @@ -240,13 +240,13 @@ describe "CompileManager", -> it "should be not able to compile if rate limit has no remianing", (done)-> @ratelimiter.addCount.callsArgWith(1, null, false) - @CompileManager._checkIfAutoCompileLimitHasBeenHit true, (err, canCompile)=> + @CompileManager._checkIfAutoCompileLimitHasBeenHit true, "everyone", (err, canCompile)=> canCompile.should.equal false done() it "should return false if there is an error in the rate limit", (done)-> @ratelimiter.addCount.callsArgWith(1, "error") - @CompileManager._checkIfAutoCompileLimitHasBeenHit true, (err, canCompile)=> + @CompileManager._checkIfAutoCompileLimitHasBeenHit true, "everyone", (err, canCompile)=> canCompile.should.equal false done() From 274be4932a6393ed0145837c703227b36a68894e Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Mon, 9 Oct 2017 17:29:55 +0100 Subject: [PATCH 125/206] Adjust padding values. --- services/web/public/stylesheets/core/ol-variables.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index e8aa2fe22b..1595a46ede 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -113,7 +113,7 @@ @folders-menu-line-height : @structured-list-line-height; @folders-menu-item-v-padding : (@line-height-computed / 4); @folders-menu-item-h-padding : (@grid-gutter-width / 2); -@folders-title-padding : @folders-menu-item-v-padding @folders-menu-item-v-padding; +@folders-title-padding : @folders-menu-item-v-padding 0; @folders-title-margin-top : 0; @folders-title-margin-bottom : 0; @folders-title-font-weight : normal; From f017a94b7ebb5afe409716ef40d94262f9cfe465 Mon Sep 17 00:00:00 2001 From: Hayden Faulds Date: Wed, 11 Oct 2017 11:18:20 +0100 Subject: [PATCH 126/206] remove unused HistoryManager --- .../Features/History/HistoryManager.coffee | 28 --------- .../coffee/History/HistoryManagerTests.coffee | 63 ------------------- 2 files changed, 91 deletions(-) delete mode 100644 services/web/app/coffee/Features/History/HistoryManager.coffee delete mode 100644 services/web/test/UnitTests/coffee/History/HistoryManagerTests.coffee diff --git a/services/web/app/coffee/Features/History/HistoryManager.coffee b/services/web/app/coffee/Features/History/HistoryManager.coffee deleted file mode 100644 index ea3f492613..0000000000 --- a/services/web/app/coffee/Features/History/HistoryManager.coffee +++ /dev/null @@ -1,28 +0,0 @@ -settings = require "settings-sharelatex" -request = require "request" -logger = require "logger-sharelatex" - -module.exports = HistoryManager = - flushProject: (project_id, callback = (error) ->) -> - logger.log project_id: project_id, "flushing project in track-changes api" - url = "#{settings.apis.trackchanges.url}/project/#{project_id}/flush" - request.post url, (error, res, body) -> - return callback(error) if error? - if 200 <= res.statusCode < 300 - callback(null) - else - error = new Error("track-changes api responded with non-success code: #{res.statusCode} #{url}") - logger.error err: error, project_id: project_id, "error flushing project in track-changes api" - callback(error) - - archiveProject: (project_id, callback = ()->)-> - logger.log project_id: project_id, "archving project in track-changes api" - url = "#{settings.apis.trackchanges.url}/project/#{project_id}/archive" - request.post url, (error, res, body) -> - return callback(error) if error? - if 200 <= res.statusCode < 300 - callback(null) - else - error = new Error("track-changes api responded with non-success code: #{res.statusCode} #{url}") - logger.error err: error, project_id: project_id, "error archving project in track-changes api" - callback(error) \ No newline at end of file diff --git a/services/web/test/UnitTests/coffee/History/HistoryManagerTests.coffee b/services/web/test/UnitTests/coffee/History/HistoryManagerTests.coffee deleted file mode 100644 index 65b22812ea..0000000000 --- a/services/web/test/UnitTests/coffee/History/HistoryManagerTests.coffee +++ /dev/null @@ -1,63 +0,0 @@ -chai = require('chai') -expect = chai.expect -chai.should() -sinon = require("sinon") -modulePath = "../../../../app/js/Features/History/HistoryManager" -SandboxedModule = require('sandboxed-module') - -describe "HistoryManager", -> - beforeEach -> - @HistoryManager = SandboxedModule.require modulePath, requires: - "request" : @request = sinon.stub() - "settings-sharelatex": @settings = - apis: - trackchanges: - url: "trackchanges.sharelatex.com" - "logger-sharelatex": @logger = {log: sinon.stub(), error: sinon.stub()} - @project_id = "project-id-123" - @callback = sinon.stub() - @request.post = sinon.stub() - - describe "flushProject", -> - describe "with a successful response code", -> - beforeEach -> - @request.post = sinon.stub().callsArgWith(1, null, statusCode: 204, "") - @HistoryManager.flushProject @project_id, @callback - - it "should flush the project in the track changes api", -> - @request.post - .calledWith("#{@settings.apis.trackchanges.url}/project/#{@project_id}/flush") - .should.equal true - - it "should call the callback without an error", -> - @callback.calledWith(null).should.equal true - - describe "with a failed response code", -> - beforeEach -> - @request.post = sinon.stub().callsArgWith(1, null, statusCode: 500, "") - @HistoryManager.flushProject @project_id, @callback - - it "should call the callback with an error", -> - @callback.calledWith(new Error("track-changes api responded with a non-success code: 500")).should.equal true - - it "should log the error", -> - @logger.error - .calledWith({ - err: new Error("track-changes api responded with a non-success code: 500") - project_id: @project_id - }, "error flushing project in track-changes api") - .should.equal true - - describe "ArchiveProject", -> - - it "should call the post endpoint", (done)-> - @request.post.callsArgWith(1, null, {}) - @HistoryManager.archiveProject @project_id, (err)=> - @request.post.calledWith("#{@settings.apis.trackchanges.url}/project/#{@project_id}/archive") - done() - - it "should return an error on a non success", (done)-> - @request.post.callsArgWith(1, null, {statusCode:500}) - @HistoryManager.archiveProject @project_id, (err)=> - expect(err).to.exist - done() \ No newline at end of file From 5047213c3c6e261db08f1fec5e38d0b6f5802b6e Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Wed, 11 Oct 2017 14:47:39 +0100 Subject: [PATCH 127/206] Change rollout detection to account for changes in proportion preserving rolled out users --- .../web/app/coffee/Features/Project/ProjectController.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/web/app/coffee/Features/Project/ProjectController.coffee b/services/web/app/coffee/Features/Project/ProjectController.coffee index 89b47b5782..5915fef8fd 100644 --- a/services/web/app/coffee/Features/Project/ProjectController.coffee +++ b/services/web/app/coffee/Features/Project/ProjectController.coffee @@ -257,7 +257,8 @@ module.exports = ProjectController = timestamp = parseInt(user_id.toString().substring(0, 8), 16) counter = parseInt(user_id.toString().substring(18, 24), 16) - if counter % 5000 != 0 + rolloutProportion = 10 + if counter % 1000 < rolloutProportion # Don't show if user is not part of roll out return cb(null, false) userSignupDate = new Date(timestamp * 1000) From 38b3626014186ef5b0f50797bf4fc91c7314fc7a Mon Sep 17 00:00:00 2001 From: Joe Green Date: Wed, 11 Oct 2017 15:21:21 +0100 Subject: [PATCH 128/206] Try email only on master branch (#47) --- services/web/Jenkinsfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/web/Jenkinsfile b/services/web/Jenkinsfile index 50ced29d60..2da3a825f7 100644 --- a/services/web/Jenkinsfile +++ b/services/web/Jenkinsfile @@ -160,6 +160,10 @@ pipeline { post { failure { + when { + branch 'master' + } + mail(from: "${EMAIL_ALERT_FROM}", to: "${EMAIL_ALERT_TO}", subject: "Jenkins build failed: ${JOB_NAME}:${BUILD_NUMBER}", From cf25ff058ffa1ca8b0fb31a01758e8a60f2c2a2c Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 11 Oct 2017 15:50:21 +0100 Subject: [PATCH 129/206] use new endpoint docupdater get_and_flush_if_old --- .../Features/DocumentUpdater/DocumentUpdaterHandler.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/app/coffee/Features/DocumentUpdater/DocumentUpdaterHandler.coffee b/services/web/app/coffee/Features/DocumentUpdater/DocumentUpdaterHandler.coffee index 466152924e..e806ddf8dd 100644 --- a/services/web/app/coffee/Features/DocumentUpdater/DocumentUpdaterHandler.coffee +++ b/services/web/app/coffee/Features/DocumentUpdater/DocumentUpdaterHandler.coffee @@ -128,9 +128,9 @@ module.exports = DocumentUpdaterHandler = # docs from redis via the docupdater. Otherwise we will need to # fall back to getting them from mongo. timer = new metrics.Timer("get-project-docs") - url = "#{settings.apis.documentupdater.url}/project/#{project_id}/doc?state=#{projectStateHash}" + url = "#{settings.apis.documentupdater.url}/project/#{project_id}/get_and_flush_if_old?state=#{projectStateHash}" logger.log project_id:project_id, "getting project docs from document updater" - request.get url, (error, res, body)-> + request.post url, (error, res, body)-> timer.done() if error? logger.error err:error, url:url, project_id:project_id, "error getting project docs from doc updater" From 2d5426391d54259a6164b2ec063f1418da77cb27 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Thu, 12 Oct 2017 11:53:19 +0100 Subject: [PATCH 130/206] Avoid generating unneeded scroll. --- services/web/public/stylesheets/components/footer.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/public/stylesheets/components/footer.less b/services/web/public/stylesheets/components/footer.less index a5e0719157..ca33db8705 100644 --- a/services/web/public/stylesheets/components/footer.less +++ b/services/web/public/stylesheets/components/footer.less @@ -6,7 +6,7 @@ footer.site-footer { bottom: 0; width: 100%; height: @footer-height; - line-height: @footer-height; + line-height: @footer-height - 1; // Hack — in Chrome, using @line-height would generate vertical scrolling ul { list-style: none; From c488916c69b38d47ee02928986f63dcdc1ed1d42 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Thu, 12 Oct 2017 12:18:56 +0100 Subject: [PATCH 131/206] Make content section expand at least to all available height. --- services/web/public/stylesheets/core/scaffolding.less | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/services/web/public/stylesheets/core/scaffolding.less b/services/web/public/stylesheets/core/scaffolding.less index 3258e36c2c..d13976686b 100755 --- a/services/web/public/stylesheets/core/scaffolding.less +++ b/services/web/public/stylesheets/core/scaffolding.less @@ -33,6 +33,11 @@ body { position: relative; padding-top: @header-height; padding-bottom: @footer-height; + display: flex; + align-items: stretch; + > .content { + flex-grow: 1; + } } // Reset fonts for relevant elements From f47dac64147e8ceb832294e2ce5e29ac59f4a15f Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Thu, 12 Oct 2017 14:19:31 +0100 Subject: [PATCH 132/206] Take potential padding into account when calculating proj list height. --- .../main/project-list/project-list.coffee | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/services/web/public/coffee/main/project-list/project-list.coffee b/services/web/public/coffee/main/project-list/project-list.coffee index 62bae9d2db..d84c348218 100644 --- a/services/web/public/coffee/main/project-list/project-list.coffee +++ b/services/web/public/coffee/main/project-list/project-list.coffee @@ -26,22 +26,12 @@ define [ storedUIOpts = JSON.parse(localStorage("project_list")) recalculateProjectListHeight = () -> - topOffset = $(".project-list-card")?.offset()?.top - bottomOffset = $("footer").outerHeight() + 25 - sideBarHeight = $("aside").height() - 56 - # When footer is visible and page doesn't need to scroll we just make it - # span between header and footer - height = $window.innerHeight - topOffset - bottomOffset - - # When page is small enough that this pushes the project list smaller than - # the side bar, then the window going to have to scroll to take into account the - # footer. So we now start to track to the bottom of the window, with a 25px padding - # since the footer is hidden below the fold. Don't ever get bigger than the sidebar - # though since that's what triggered this happening in the first place. - # if height < sideBarHeight - # height = Math.min(sideBarHeight, $window.innerHeight - topOffset - 25) + $projListCard = $(".project-list-card") + topOffset = $projListCard?.offset()?.top + cardPadding = $projListCard?.outerHeight() - $projListCard?.height() + bottomOffset = $("footer").outerHeight() + height = $window.innerHeight - topOffset - bottomOffset - cardPadding $scope.projectListHeight = height - angular.element($window).bind "resize", () -> recalculateProjectListHeight() From fdffc92de214c7f36df4d3dbcd5038ed25e7b286 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 12 Oct 2017 15:01:11 +0100 Subject: [PATCH 133/206] fix unit tests --- .../coffee/Compile/ClsiManagerTests.coffee | 6 +++--- .../DocumentUpdaterHandlerTests.coffee | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/services/web/test/UnitTests/coffee/Compile/ClsiManagerTests.coffee b/services/web/test/UnitTests/coffee/Compile/ClsiManagerTests.coffee index 32deed5522..59c3e3d018 100644 --- a/services/web/test/UnitTests/coffee/Compile/ClsiManagerTests.coffee +++ b/services/web/test/UnitTests/coffee/Compile/ClsiManagerTests.coffee @@ -247,12 +247,12 @@ describe "ClsiManager", -> .calledWith(@project_id, {compiler:1, rootDoc_id: 1, imageName: 1, rootFolder: 1}) .should.equal true - it "should flush the project to the database", -> + it "should not explicitly flush the project to the database", -> @DocumentUpdaterHandler.flushProjectToMongo .calledWith(@project_id) - .should.equal true + .should.equal false - it "should get only the live docs from the docupdater", -> + it "should get only the live docs from the docupdater with a background flush in docupdater", -> @DocumentUpdaterHandler.getProjectDocsIfMatch .calledWith(@project_id) .should.equal true diff --git a/services/web/test/UnitTests/coffee/DocumentUpdater/DocumentUpdaterHandlerTests.coffee b/services/web/test/UnitTests/coffee/DocumentUpdater/DocumentUpdaterHandlerTests.coffee index b2cbeeba16..d7e88a6615 100644 --- a/services/web/test/UnitTests/coffee/DocumentUpdater/DocumentUpdaterHandlerTests.coffee +++ b/services/web/test/UnitTests/coffee/DocumentUpdater/DocumentUpdaterHandlerTests.coffee @@ -265,19 +265,19 @@ describe 'DocumentUpdaterHandler', -> v: @version @docs = [ @doc0, @doc0, @doc0 ] @body = JSON.stringify @docs - @request.get = sinon.stub().callsArgWith(1, null, {statusCode: 200}, @body) + @request.post = sinon.stub().callsArgWith(1, null, {statusCode: 200}, @body) @handler.getProjectDocsIfMatch @project_id, @project_state_hash, @callback it 'should get the documenst from the document updater', -> - url = "#{@settings.apis.documentupdater.url}/project/#{@project_id}/doc?state=#{@project_state_hash}" - @request.get.calledWith(url).should.equal true + url = "#{@settings.apis.documentupdater.url}/project/#{@project_id}/get_and_flush_if_old?state=#{@project_state_hash}" + @request.post.calledWith(url).should.equal true it "should call the callback with the documents", -> @callback.calledWithExactly(null, @docs).should.equal true describe "when the document updater API returns an error", -> beforeEach -> - @request.get = sinon.stub().callsArgWith(1, @error = new Error("something went wrong"), null, null) + @request.post = sinon.stub().callsArgWith(1, @error = new Error("something went wrong"), null, null) @handler.getProjectDocsIfMatch @project_id, @project_state_hash, @callback it "should return an error to the callback", -> @@ -285,7 +285,7 @@ describe 'DocumentUpdaterHandler', -> describe "when the document updater returns a conflict error code", -> beforeEach -> - @request.get = sinon.stub().callsArgWith(1, null, { statusCode: 409 }, "Conflict") + @request.post = sinon.stub().callsArgWith(1, null, { statusCode: 409 }, "Conflict") @handler.getProjectDocsIfMatch @project_id, @project_state_hash, @callback it "should return the callback with no documents", -> @@ -312,7 +312,7 @@ describe 'DocumentUpdaterHandler', -> describe "when the document updater API returns an error", -> beforeEach -> - @request.get = sinon.stub().callsArgWith(1, @error = new Error("something went wrong"), null, null) + @request.post = sinon.stub().callsArgWith(1, @error = new Error("something went wrong"), null, null) @handler.getProjectDocsIfMatch @project_id, @project_state_hash, @callback it "should return an error to the callback", -> @@ -320,7 +320,7 @@ describe 'DocumentUpdaterHandler', -> describe "when the document updater returns a conflict error code", -> beforeEach -> - @request.get = sinon.stub().callsArgWith(1, null, { statusCode: 409 }, "Conflict") + @request.post = sinon.stub().callsArgWith(1, null, { statusCode: 409 }, "Conflict") @handler.getProjectDocsIfMatch @project_id, @project_state_hash, @callback it "should return the callback with no documents", -> From ef9651cc884c63088e129a832c566a6c56f88f2c Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Thu, 12 Oct 2017 15:44:06 +0100 Subject: [PATCH 134/206] Style progress bars. --- .../stylesheets/components/progress-bars.less | 6 +++--- .../public/stylesheets/core/_common-variables.less | 4 ++++ .../web/public/stylesheets/core/ol-variables.less | 13 ++++++++++++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/services/web/public/stylesheets/components/progress-bars.less b/services/web/public/stylesheets/components/progress-bars.less index 062ec48df2..df841437c3 100755 --- a/services/web/public/stylesheets/components/progress-bars.less +++ b/services/web/public/stylesheets/components/progress-bars.less @@ -29,8 +29,8 @@ height: @line-height-computed; margin-bottom: @line-height-computed; background-color: @progress-bg; - border-radius: @border-radius-base; - border: 1px solid @progress-border-color; + border-radius: @progress-border-radius; + border: @progress-border-width solid @progress-border-color; .box-shadow(inset 0 1px 2px rgba(0,0,0,.1)); } @@ -44,7 +44,7 @@ color: @progress-bar-color; text-align: center; background-color: @progress-bar-bg; - .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15)); + .box-shadow(@progress-bar-shadow); .transition(width .6s ease); } diff --git a/services/web/public/stylesheets/core/_common-variables.less b/services/web/public/stylesheets/core/_common-variables.less index 644fe4c88e..c181bd4b07 100644 --- a/services/web/public/stylesheets/core/_common-variables.less +++ b/services/web/public/stylesheets/core/_common-variables.less @@ -854,6 +854,10 @@ @folders-tag-menu-hover: @gray-light; @folders-untagged-line-height: 1.7; +@progress-border-radius: @border-radius-base; +@progress-border-width: 1px; +@progress-bar-shadow: inset 0 -1px 0 rgba(0,0,0,.15); + @footer-link-color: @link-color; @footer-link-hover-color: @link-hover-color; @footer-bg-color: transparent; diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index 1595a46ede..9938b301bc 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -63,7 +63,7 @@ @btn-info-border : transparent; // Tags -@tag-border-radius : 999px; +@tag-border-radius : 9999px; @tag-bg-color : @ol-green; @tag-bg-hover-color : @ol-dark-green; @@ -137,6 +137,17 @@ @structured-list-border-color : @ol-blue-gray-1; @structured-list-hover-color : lighten(@ol-blue-gray-1, 5%); + +// Progress bars +@progress-border-radius : @line-height-computed; +@progress-border-width : 0; +@progress-bar-bg : @ol-blue-gray-4; +@progress-bar-success-bg : @ol-green; +@progress-bar-warning-bg : @brand-warning; +@progress-bar-danger-bg : @ol-red; +@progress-bar-info-bg : @ol-blue; +@progress-bar-shadow : none; + // Footer @footer-bg-color : #FFF; @footer-link-color : @ol-green; From 8cf328818715ba7963eb6c121ef82c559b2e2aa0 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 12 Oct 2017 16:03:12 +0100 Subject: [PATCH 135/206] avoid exception in validateProjectName --- .../coffee/Features/Project/ProjectDetailsHandler.coffee | 2 +- .../coffee/Project/ProjectDetailsHandlerTests.coffee | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/services/web/app/coffee/Features/Project/ProjectDetailsHandler.coffee b/services/web/app/coffee/Features/Project/ProjectDetailsHandler.coffee index 5819e03472..09783c58c7 100644 --- a/services/web/app/coffee/Features/Project/ProjectDetailsHandler.coffee +++ b/services/web/app/coffee/Features/Project/ProjectDetailsHandler.coffee @@ -57,7 +57,7 @@ module.exports = ProjectDetailsHandler = MAX_PROJECT_NAME_LENGTH: 150 validateProjectName: (name, callback = (error) ->) -> - if name.length == 0 + if !name? or name.length == 0 return callback(new Errors.InvalidNameError("Project name cannot be blank")) else if name.length > @MAX_PROJECT_NAME_LENGTH return callback(new Errors.InvalidNameError("Project name is too long")) diff --git a/services/web/test/UnitTests/coffee/Project/ProjectDetailsHandlerTests.coffee b/services/web/test/UnitTests/coffee/Project/ProjectDetailsHandlerTests.coffee index abb0b505d0..8d2973e235 100644 --- a/services/web/test/UnitTests/coffee/Project/ProjectDetailsHandlerTests.coffee +++ b/services/web/test/UnitTests/coffee/Project/ProjectDetailsHandlerTests.coffee @@ -126,6 +126,12 @@ describe 'ProjectDetailsHandler', -> done() describe "validateProjectName", -> + + it "should reject undefined names", (done) -> + @handler.validateProjectName undefined, (error) -> + expect(error).to.exist + done() + it "should reject empty names", (done) -> @handler.validateProjectName "", (error) -> expect(error).to.exist From fc3ecddd0e6b8a406be9b6de3f0de2771be30f87 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 12 Oct 2017 16:18:14 +0100 Subject: [PATCH 136/206] fix two bugs in auto compile limit logic 1. the compileGroup is "standard" not default 2. was not excluding normal compiles from metrics --- .../web/app/coffee/Features/Compile/CompileManager.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/services/web/app/coffee/Features/Compile/CompileManager.coffee b/services/web/app/coffee/Features/Compile/CompileManager.coffee index 9e50b280e2..f18dc56c7b 100755 --- a/services/web/app/coffee/Features/Compile/CompileManager.coffee +++ b/services/web/app/coffee/Features/Compile/CompileManager.coffee @@ -77,7 +77,10 @@ module.exports = CompileManager = return callback null, true _checkCompileGroupAutoCompileLimit: (isAutoCompile, compileGroup, callback = (err, canCompile)->)-> - if compileGroup is "default" + if !isAutoCompile + return callback(null, true) + if compileGroup is "standard" + # apply extra limits to the standard compile group CompileManager._checkIfAutoCompileLimitHasBeenHit isAutoCompile, compileGroup, callback else Metrics.inc "auto-compile-#{compileGroup}" From 36e1fafb60da85cc3ce636e6e5bbeadc9fbd434e Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 12 Oct 2017 17:10:41 +0100 Subject: [PATCH 137/206] Set proportion to 0 --- .../web/app/coffee/Features/Project/ProjectController.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/app/coffee/Features/Project/ProjectController.coffee b/services/web/app/coffee/Features/Project/ProjectController.coffee index 5915fef8fd..e7c9ac9bc9 100644 --- a/services/web/app/coffee/Features/Project/ProjectController.coffee +++ b/services/web/app/coffee/Features/Project/ProjectController.coffee @@ -257,7 +257,7 @@ module.exports = ProjectController = timestamp = parseInt(user_id.toString().substring(0, 8), 16) counter = parseInt(user_id.toString().substring(18, 24), 16) - rolloutProportion = 10 + rolloutProportion = 0 if counter % 1000 < rolloutProportion # Don't show if user is not part of roll out return cb(null, false) From d5fba2cc7115eafc715c7ad0d01f319715fcb925 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 13 Oct 2017 08:23:16 +0100 Subject: [PATCH 138/206] enable incremental compiles for all users --- .../public/coffee/ide/pdf/controllers/PdfController.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index b4cf9f6358..f244b55204 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -144,9 +144,9 @@ define [ rootDoc_id: options.rootDocOverride_id or null draft: $scope.draft check: checkType - # use incremental compile for beta users but revert to a full + # use incremental compile for all users but revert to a full # compile if there is a server error - incrementalCompilesEnabled: window.user?.betaProgram and not $scope.pdf.error + incrementalCompilesEnabled: not $scope.pdf.error _csrf: window.csrfToken }, {params: params} From 2efb6b69e5014a5c16fe669ea90fb6179e72cebd Mon Sep 17 00:00:00 2001 From: Nate Stemen Date: Fri, 13 Oct 2017 17:42:10 +0100 Subject: [PATCH 139/206] removing comment --- .../aceEditor/auto-complete/PackageManager.coffee | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/PackageManager.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/PackageManager.coffee index ac30d5a431..c0e1cf5e8f 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/PackageManager.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/PackageManager.coffee @@ -23,7 +23,7 @@ define () -> packageSnippets = for pkg in packages { caption: "\\usepackage{#{pkg}}" - snippet: """\\usepackage{#{pkg}}""" + snippet: "\\usepackage{#{pkg}}" meta: "pkg" } @@ -31,14 +31,13 @@ define () -> caption: "\\usepackage{}" snippet: "\\usepackage{}" meta: "pkg" - score: 70 + score: 55 } class PackageManager getCompletions: (editor, session, pos, prefix, callback) -> docText = session.getValue() - loaded = parseLoadedPackages(docText) - # console.log loaded + loaded = parseLoadedPackages docText callback null, packageSnippets return PackageManager From 6c3ceb48df33ceeb75935e20745e4986431e2fc1 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Mon, 16 Oct 2017 10:25:52 +0100 Subject: [PATCH 140/206] Allow onboarding to be shown outside of beta program --- services/web/public/coffee/ide.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/public/coffee/ide.coffee b/services/web/public/coffee/ide.coffee index 4aaaecc53b..0a83a2dbab 100644 --- a/services/web/public/coffee/ide.coffee +++ b/services/web/public/coffee/ide.coffee @@ -78,7 +78,7 @@ define [ miniReviewPanelVisible: false, } $scope.onboarding = { - autoCompile: if window.user.betaProgram and window.showAutoCompileOnboarding then 'unseen' else 'dismissed' + autoCompile: if window.showAutoCompileOnboarding then 'unseen' else 'dismissed' } $scope.user = window.user From e02c3bbb09798b702f990e43490bea1ab3dd68b4 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Mon, 16 Oct 2017 10:27:33 +0100 Subject: [PATCH 141/206] Remove beta program badge --- services/web/app/views/project/editor/editor.pug | 4 +--- services/web/app/views/project/editor/pdf.pug | 6 ++---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/services/web/app/views/project/editor/editor.pug b/services/web/app/views/project/editor/editor.pug index 89ec5c7828..86cc63a5e5 100644 --- a/services/web/app/views/project/editor/editor.pug +++ b/services/web/app/views/project/editor/editor.pug @@ -101,9 +101,7 @@ div.full-size( ng-class="placement" ) .popover-inner - h3.popover-title - | #{translate("auto_compile")} - span.beta-feature-badge + h3.popover-title #{translate("auto_compile")} .popover-content p #{translate("try_out_auto_compile_setting")} img(src="/img/onboarding/autocompile/setting-dropdown.png" width="100%") diff --git a/services/web/app/views/project/editor/pdf.pug b/services/web/app/views/project/editor/pdf.pug index 2e0bc3701f..43416b8991 100644 --- a/services/web/app/views/project/editor/pdf.pug +++ b/services/web/app/views/project/editor/pdf.pug @@ -26,11 +26,9 @@ div.full-size.pdf(ng-controller="PdfController") ) span.caret ul.dropdown-menu.dropdown-menu-left - // Only show on beta program? + // Only show if on beta program or part of rollout if user.betaProgram || showAutoCompileOnboarding - li.dropdown-header - | #{translate("auto_compile")} - span.beta-feature-badge + li.dropdown-header #{translate("auto_compile")} li a(href, ng-click="autocompile_enabled = true") i.fa.fa-fw(ng-class="{'fa-check': autocompile_enabled}") From 625f52c24877613c8a2679e20c2642eaeb7b22b1 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Mon, 16 Oct 2017 10:28:42 +0100 Subject: [PATCH 142/206] Update cutoff date --- .../web/app/coffee/Features/Project/ProjectController.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/app/coffee/Features/Project/ProjectController.coffee b/services/web/app/coffee/Features/Project/ProjectController.coffee index e7c9ac9bc9..205ac0d718 100644 --- a/services/web/app/coffee/Features/Project/ProjectController.coffee +++ b/services/web/app/coffee/Features/Project/ProjectController.coffee @@ -262,7 +262,7 @@ module.exports = ProjectController = # Don't show if user is not part of roll out return cb(null, false) userSignupDate = new Date(timestamp * 1000) - if userSignupDate > new Date("2017-10-03") + if userSignupDate > new Date("2017-10-16") # Don't show for users who registered after it was released return cb(null, false) timeout = setTimeout cb, 500 From bd6bc42a9a36ec4b9a83966e27056bb3f1aa2943 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Mon, 16 Oct 2017 11:42:15 +0100 Subject: [PATCH 143/206] Don't Show autocompile on load --- .../web/public/coffee/ide/pdf/controllers/PdfController.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index ce2c3eec05..640c224e7b 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -419,7 +419,7 @@ define [ $scope.recompile = (options = {}) -> return if $scope.pdf.compiling - if !options.isAutoCompile and $scope.onboarding.autoCompile == 'unseen' + if !options.isAutoCompileOnLoad and $scope.onboarding.autoCompile == 'unseen' $scope.onboarding.autoCompile = 'show' event_tracking.sendMBSampled "editor-recompile-sampled", options From abb21ac96b371b34888704873e5be3e6fbf32d99 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Mon, 16 Oct 2017 12:44:11 +0100 Subject: [PATCH 144/206] Fix autocompile rollout logic --- .../web/app/coffee/Features/Project/ProjectController.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/app/coffee/Features/Project/ProjectController.coffee b/services/web/app/coffee/Features/Project/ProjectController.coffee index 205ac0d718..63478f385d 100644 --- a/services/web/app/coffee/Features/Project/ProjectController.coffee +++ b/services/web/app/coffee/Features/Project/ProjectController.coffee @@ -258,7 +258,7 @@ module.exports = ProjectController = counter = parseInt(user_id.toString().substring(18, 24), 16) rolloutProportion = 0 - if counter % 1000 < rolloutProportion + if counter % 1000 > rolloutProportion # Don't show if user is not part of roll out return cb(null, false) userSignupDate = new Date(timestamp * 1000) From 60c297eac88cdf0f94e710b87ae753fdd8e8e527 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Mon, 16 Oct 2017 13:40:09 +0100 Subject: [PATCH 145/206] Also prevent rollout to modulo 0 users --- .../web/app/coffee/Features/Project/ProjectController.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/app/coffee/Features/Project/ProjectController.coffee b/services/web/app/coffee/Features/Project/ProjectController.coffee index 63478f385d..68d2c49bca 100644 --- a/services/web/app/coffee/Features/Project/ProjectController.coffee +++ b/services/web/app/coffee/Features/Project/ProjectController.coffee @@ -258,7 +258,7 @@ module.exports = ProjectController = counter = parseInt(user_id.toString().substring(18, 24), 16) rolloutProportion = 0 - if counter % 1000 > rolloutProportion + if counter % 1000 >= rolloutProportion # Don't show if user is not part of roll out return cb(null, false) userSignupDate = new Date(timestamp * 1000) From 67b13d2583054b06d5f648a2ada1fdad52b7ecda Mon Sep 17 00:00:00 2001 From: Joe Green Date: Mon, 16 Oct 2017 14:07:49 +0100 Subject: [PATCH 146/206] Update Jenkinsfile --- services/web/Jenkinsfile | 4 ---- 1 file changed, 4 deletions(-) diff --git a/services/web/Jenkinsfile b/services/web/Jenkinsfile index 2da3a825f7..50ced29d60 100644 --- a/services/web/Jenkinsfile +++ b/services/web/Jenkinsfile @@ -160,10 +160,6 @@ pipeline { post { failure { - when { - branch 'master' - } - mail(from: "${EMAIL_ALERT_FROM}", to: "${EMAIL_ALERT_TO}", subject: "Jenkins build failed: ${JOB_NAME}:${BUILD_NUMBER}", From 829f60fec4587a1999ad7cc8bf5f84e99bfa58b0 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Mon, 16 Oct 2017 14:08:48 +0100 Subject: [PATCH 147/206] Fix scrolling issues in IE11. --- services/web/public/stylesheets/app/project-list.less | 6 ++++-- services/web/public/stylesheets/core/ol-variables.less | 2 +- services/web/public/stylesheets/core/scaffolding.less | 5 ----- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less index 19a6c18e77..d196ef43cf 100644 --- a/services/web/public/stylesheets/app/project-list.less +++ b/services/web/public/stylesheets/app/project-list.less @@ -24,7 +24,8 @@ padding-top: 0; padding-bottom: 0; width: 100%; - overflow: scroll; + overflow-x: hidden; + overflow-y: auto; } .project-list-content when (@is-overleaf) { @@ -65,7 +66,8 @@ .project-list-sidebar when (@is-overleaf) { height: 100%; - overflow: scroll; + overflow-x: hidden; + overflow-y: auto; } .project-list-main { diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index 9938b301bc..7b373ae0ca 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -29,7 +29,7 @@ @navbar-brand-image-url : url(/img/ol-brand/overleaf-white.svg); // Backgrounds -@body-bg : #FFF; +@body-bg : @ol-blue-gray-1; @content-alt-bg-color : @ol-blue-gray-1; // Typography diff --git a/services/web/public/stylesheets/core/scaffolding.less b/services/web/public/stylesheets/core/scaffolding.less index d13976686b..3258e36c2c 100755 --- a/services/web/public/stylesheets/core/scaffolding.less +++ b/services/web/public/stylesheets/core/scaffolding.less @@ -33,11 +33,6 @@ body { position: relative; padding-top: @header-height; padding-bottom: @footer-height; - display: flex; - align-items: stretch; - > .content { - flex-grow: 1; - } } // Reset fonts for relevant elements From d6d76f132eb1e6a760db35fee066325c63fb7f1e Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 13 Oct 2017 16:49:22 +0100 Subject: [PATCH 148/206] replace unzip with yauzl --- .../Features/Uploads/ArchiveManager.coffee | 130 +- services/web/npm-shrinkwrap.json | 5631 ++++++++++++----- services/web/package.json | 4 +- 3 files changed, 4013 insertions(+), 1752 deletions(-) diff --git a/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee b/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee index a7f0ccdbeb..b71b5cd2bc 100644 --- a/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee +++ b/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee @@ -3,54 +3,96 @@ logger = require "logger-sharelatex" metrics = require "metrics-sharelatex" fs = require "fs" Path = require "path" +fse = require "fs-extra" +yauzl = require "yauzl" +Settings = require "settings-sharelatex" _ = require("underscore") ONE_MEG = 1024 * 1024 module.exports = ArchiveManager = - _isZipTooLarge: (source, callback = (err, isTooLarge)->)-> callback = _.once callback - unzip = child.spawn("unzip", ["-l", source]) + totalSizeInBytes = 0 + yauzl.open source, {lazyEntries: true}, (err, zipfile) -> + return callback(err) if err? - output = "" - unzip.stdout.on "data", (d)-> - output += d + if Settings.maxEntitiesPerProject? and zipfile.entryCount > Settings.maxEntitiesPerProject + return callback(null, true) # too many files in zip file - error = null - unzip.stderr.on "data", (chunk) -> - error ||= "" - error += chunk + zipfile.on "error", callback - unzip.on "error", (err) -> - logger.error {err, source}, "unzip failed" - if err.code == "ENOENT" - logger.error "unzip command not found. Please check the unzip command is installed" - callback(err) + # read all the entries + zipfile.readEntry() + zipfile.on "entry", (entry) -> + totalSizeInBytes += entry.uncompressedSize + zipfile.readEntry() # get the next entry - unzip.on "close", (exitCode) -> - if error? - error = new Error(error) - logger.warn err:error, source: source, "error checking zip size" + # no more entries to read + zipfile.on "end", () -> + if !totalSizeInBytes? or isNaN(totalSizeInBytes) + logger.err source:source, totalSizeInBytes:totalSizeInBytes, "error getting bytes of zip" + return callback(new Error("error getting bytes of zip")) + isTooLarge = totalSizeInBytes > (ONE_MEG * 300) + callback(null, isTooLarge) - lines = output.split("\n") - lastLine = lines[lines.length - 2]?.trim() - totalSizeInBytes = lastLine?.split(" ")?[0] + _checkFilePath: (entry, destination, callback = (err, destFile) ->) -> + # check if the entry is a directory + if /\/$/.test(entry.fileName) + return callback() # don't give a destfile for directory + # check that the file does not use a relative path + for dir in entry.fileName.split('/') + if dir == '..' + return callback(new Error("relative path")) + # check that the destination file path is normalized + dest = "#{destination}/#{entry.fileName}" + if dest != Path.normalize(dest) + return callback(new Error("unnormalized path")) + else + return callback(null, dest) - totalSizeInBytesAsInt = parseInt(totalSizeInBytes) + _writeFileEntry: (zipfile, entry, destFile, callback = (err)->) -> + callback = _.once callback - if !totalSizeInBytesAsInt? or isNaN(totalSizeInBytesAsInt) - logger.err source:source, totalSizeInBytes:totalSizeInBytes, totalSizeInBytesAsInt:totalSizeInBytesAsInt, lastLine:lastLine, exitCode:exitCode, "error getting bytes of zip" - return callback(new Error("error getting bytes of zip")) + zipfile.openReadStream entry, (err, readStream) -> + return callback(err) if err? + readStream.on "error", callback + readStream.on "end", callback - isTooLarge = totalSizeInBytes > (ONE_MEG * 300) + fse.ensureDir Path.dirname(destFile), (err) -> + return callback(err) if err? + writeStream = fs.createWriteStream destFile + writeStream.on 'error', (err) -> + return callback(err) + readStream.pipe(writeStream) - callback(error, isTooLarge) + _extractZipFiles: (source, destination, callback = (err) ->) -> + callback = _.once callback + yauzl.open source, {lazyEntries: true}, (err, zipfile) -> + return callback(err) if err? + zipfile.on "error", callback + # read all the entries + zipfile.readEntry() + zipfile.on "entry", (entry) -> + ArchiveManager._checkFilePath entry, destination, (err, destFile) -> + if err? + logger.warn err:err, source:source, destination:destination, "skipping bad file path" + zipfile.readEntry() # bad path, just skip to the next file + return + if destFile? # only write files + ArchiveManager._writeFileEntry zipfile, entry, destFile, (err) -> + if err? + logger.error err:err, source:source, destFile:destFile, "error unzipping file entry" + zipfile.close() # bail out, stop reading file entries + return callback(err) + else + zipfile.readEntry() # continue to the next file + # no more entries to read + zipfile.on "end", callback - extractZipArchive: (source, destination, _callback = (err) ->) -> callback = (args...) -> _callback(args...) @@ -62,36 +104,18 @@ module.exports = ArchiveManager = return callback(err) if isTooLarge - return callback(new Error("zip_too_large")) - + return callback(new Error("zip_too_large")) timer = new metrics.Timer("unzipDirectory") logger.log source: source, destination: destination, "unzipping file" - unzip = child.spawn("unzip", [source, "-d", destination]) + ArchiveManager._extractZipFiles source, destination, (err) -> + if err? + logger.error {err, source, destination}, "unzip failed" + callback(err) + else + callback() - # don't remove this line, some zips need - # us to listen on this for some unknow reason - unzip.stdout.on "data", (d)-> - - error = null - unzip.stderr.on "data", (chunk) -> - error ||= "" - error += chunk - - unzip.on "error", (err) -> - logger.error {err, source, destination}, "unzip failed" - if err.code == "ENOENT" - logger.error "unzip command not found. Please check the unzip command is installed" - callback(err) - - unzip.on "close", () -> - timer.done() - if error? - error = new Error(error) - logger.error err:error, source: source, destination: destination, "error unzipping file" - callback(error) - findTopLevelDirectory: (directory, callback = (error, topLevelDir) ->) -> fs.readdir directory, (error, files) -> return callback(error) if error? diff --git a/services/web/npm-shrinkwrap.json b/services/web/npm-shrinkwrap.json index 5c26a8431e..277e022779 100644 --- a/services/web/npm-shrinkwrap.json +++ b/services/web/npm-shrinkwrap.json @@ -1,2305 +1,3244 @@ { "name": "web-sharelatex", "version": "0.1.4", + "lockfileVersion": 1, + "requires": true, "dependencies": { "@types/geojson": { "version": "1.0.3", - "from": "@types/geojson@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-1.0.3.tgz" + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-1.0.3.tgz", + "integrity": "sha1-+89/pett0QjVE4XMaYfsHyQhRSM=" }, "abbrev": { "version": "1.1.0", - "from": "abbrev@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz" + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", + "integrity": "sha1-0FVMIlZjbi9W58LlrRg/hZQo2B8=" }, "accepts": { "version": "1.2.13", - "from": "accepts@>=1.2.9 <1.3.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.2.13.tgz" + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.2.13.tgz", + "integrity": "sha1-5fHzkoxtlf2WVYw27D2dDeSm7Oo=", + "requires": { + "mime-types": "2.1.17", + "negotiator": "0.5.3" + } }, "acorn": { "version": "3.3.0", - "from": "acorn@>=3.1.0 <4.0.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz" + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=" }, "acorn-globals": { "version": "3.1.0", - "from": "acorn-globals@>=3.0.0 <4.0.0", "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz", + "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=", + "requires": { + "acorn": "4.0.13" + }, "dependencies": { "acorn": { "version": "4.0.13", - "from": "acorn@>=4.0.4 <5.0.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz" + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" } } }, "addressparser": { "version": "0.2.1", - "from": "addressparser@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/addressparser/-/addressparser-0.2.1.tgz" + "resolved": "https://registry.npmjs.org/addressparser/-/addressparser-0.2.1.tgz", + "integrity": "sha1-0RpbLu2gTP7+vfMZbBCuE9ts1gc=" }, "ajv": { "version": "4.11.8", - "from": "ajv@>=4.9.1 <5.0.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz" + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } }, "align-text": { "version": "0.1.4", - "from": "align-text@>=0.1.3 <0.2.0", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz" + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "requires": { + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" + } }, "amdefine": { "version": "1.0.1", - "from": "amdefine@>=0.0.4", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" }, "ansi-regex": { "version": "2.1.1", - "from": "ansi-regex@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "ansi-styles": { "version": "2.2.1", - "from": "ansi-styles@>=2.2.1 <3.0.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, "aproba": { "version": "1.1.2", - "from": "aproba@>=1.0.3 <2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.2.tgz" + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.2.tgz", + "integrity": "sha1-RcZikJTeTpb2k+9+q3SuB5wkD8E=" }, "archiver": { "version": "0.9.0", - "from": "archiver@0.9.0", "resolved": "https://registry.npmjs.org/archiver/-/archiver-0.9.0.tgz", + "integrity": "sha1-ov0RBXpYi8lPAeXrfNPpN+pNjOU=", + "requires": { + "buffer-crc32": "0.2.13", + "file-utils": "0.1.5", + "lazystream": "0.1.0", + "lodash": "2.4.2", + "readable-stream": "1.0.34", + "tar-stream": "0.3.3", + "zip-stream": "0.3.7" + }, "dependencies": { "lodash": { "version": "2.4.2", - "from": "lodash@>=2.4.1 <2.5.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=" } } }, "are-we-there-yet": { "version": "1.1.4", - "from": "are-we-there-yet@>=1.1.2 <1.2.0", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.3" + }, "dependencies": { "isarray": { "version": "1.0.0", - "from": "isarray@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "readable-stream": { "version": "2.3.3", - "from": "readable-stream@>=2.0.6 <3.0.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz" + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha1-No8lEtefnUb9/HE0mueHi7weuVw=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } }, "string_decoder": { "version": "1.0.3", - "from": "string_decoder@>=1.0.3 <1.1.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz" + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", + "requires": { + "safe-buffer": "5.1.1" + } } } }, "argparse": { "version": "0.1.16", - "from": "argparse@>=0.1.11 <0.2.0", "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz", + "integrity": "sha1-z9AeD7uj1srtBJ+9dY1A9lGW9Xw=", + "requires": { + "underscore": "1.7.0", + "underscore.string": "2.4.0" + }, "dependencies": { "underscore": { "version": "1.7.0", - "from": "underscore@>=1.7.0 <1.8.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", + "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=" }, "underscore.string": { "version": "2.4.0", - "from": "underscore.string@>=2.4.0 <2.5.0", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz" + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz", + "integrity": "sha1-jN2PusTi0uoefi6Al8QvRCKA+Fs=" } } }, "array-flatten": { "version": "1.1.0", - "from": "array-flatten@1.1.0", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.0.tgz" + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.0.tgz", + "integrity": "sha1-rD76xxew57vcd4zgvec4GsZgQ5M=" }, "asap": { "version": "2.0.6", - "from": "asap@>=2.0.3 <2.1.0", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz" + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" }, "asn1": { "version": "0.2.3", - "from": "asn1@>=0.2.3 <0.3.0", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz" + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" }, "assert-plus": { "version": "0.2.0", - "from": "assert-plus@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz" + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" }, "assertion-error": { "version": "1.0.2", - "from": "assertion-error@^1.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz" + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz", + "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=" }, "async": { "version": "0.6.2", - "from": "async@0.6.2", - "resolved": "https://registry.npmjs.org/async/-/async-0.6.2.tgz" + "resolved": "https://registry.npmjs.org/async/-/async-0.6.2.tgz", + "integrity": "sha1-Qf0DijgSwKi8GELs8IumPrA5K+8=" }, "asynckit": { "version": "0.4.0", - "from": "asynckit@>=0.4.0 <0.5.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "autoprefixer": { "version": "6.7.7", - "from": "autoprefixer@>=6.6.1 <7.0.0", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", - "dev": true + "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", + "dev": true, + "requires": { + "browserslist": "1.7.7", + "caniuse-db": "1.0.30000740", + "normalize-range": "0.1.2", + "num2fraction": "1.2.2", + "postcss": "5.2.17", + "postcss-value-parser": "3.3.0" + } }, "aws-sdk": { "version": "2.113.0", - "from": "aws-sdk@>=2.2.36 <3.0.0", "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.113.0.tgz", + "integrity": "sha1-j7lfRlToUcdhYDFXx896Qxr2/W0=", + "requires": { + "buffer": "4.9.1", + "crypto-browserify": "1.0.9", + "events": "1.1.1", + "jmespath": "0.15.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "uuid": "3.0.1", + "xml2js": "0.4.17", + "xmlbuilder": "4.2.1" + }, "dependencies": { "uuid": { "version": "3.0.1", - "from": "uuid@3.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz" + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", + "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE=" }, "xml2js": { "version": "0.4.17", - "from": "xml2js@0.4.17", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.17.tgz" + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.17.tgz", + "integrity": "sha1-F76T6q4/O3eTWceVtBlwWogX6Gg=", + "requires": { + "sax": "1.2.1", + "xmlbuilder": "4.2.1" + } } } }, "aws-sign2": { "version": "0.6.0", - "from": "aws-sign2@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz" + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=" }, "aws4": { "version": "1.6.0", - "from": "aws4@>=1.2.1 <2.0.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz" + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" }, "axo": { "version": "0.0.2", - "from": "axo@>=0.0.0 <0.1.0", - "resolved": "https://registry.npmjs.org/axo/-/axo-0.0.2.tgz" + "resolved": "https://registry.npmjs.org/axo/-/axo-0.0.2.tgz", + "integrity": "sha1-STVfu+qzhEm8ppahqsxGml7p/Uc=" }, "babel-runtime": { "version": "6.3.19", - "from": "babel-runtime@>=6.3.19 <6.4.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.3.19.tgz" + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.3.19.tgz", + "integrity": "sha1-8ttpbDyMN5iB4qU2ZeAhhwdNxoE=", + "requires": { + "core-js": "1.2.7" + } }, "backoff": { "version": "2.5.0", - "from": "backoff@>=2.5.0 <3.0.0", - "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz" + "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", + "integrity": "sha1-9hbtqdPktmuMp/ynn2lXIsX44m8=", + "requires": { + "precond": "0.2.3" + } }, "balanced-match": { "version": "1.0.0", - "from": "balanced-match@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "base64-js": { "version": "1.2.1", - "from": "base64-js@>=1.0.2 <2.0.0", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz" + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", + "integrity": "sha1-qRlH2h9KUW6jjltOwOw3c2deCIY=" }, "base64-stream": { "version": "0.1.3", - "from": "base64-stream@>=0.1.2 <0.2.0", "resolved": "https://registry.npmjs.org/base64-stream/-/base64-stream-0.1.3.tgz", + "integrity": "sha1-drA3C3ebuBbRL9QXZKa4Vz61/sM=", + "requires": { + "readable-stream": "2.3.3" + }, "dependencies": { "isarray": { "version": "1.0.0", - "from": "isarray@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "readable-stream": { "version": "2.3.3", - "from": "readable-stream@>=2.0.2 <3.0.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz" + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha1-No8lEtefnUb9/HE0mueHi7weuVw=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } }, "string_decoder": { "version": "1.0.3", - "from": "string_decoder@>=1.0.3 <1.1.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz" + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", + "requires": { + "safe-buffer": "5.1.1" + } } } }, "base64url": { "version": "2.0.0", - "from": "base64url@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz" + "resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz", + "integrity": "sha1-6sFuA+oUOO/5Qj1puqNiYu0fcLs=" }, "basic-auth-connect": { "version": "1.0.0", - "from": "basic-auth-connect@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/basic-auth-connect/-/basic-auth-connect-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/basic-auth-connect/-/basic-auth-connect-1.0.0.tgz", + "integrity": "sha1-/bC0OWLKe0BFanwrtI/hc9otISI=" }, "bcrypt": { "version": "1.0.1", - "from": "bcrypt@1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-1.0.1.tgz", + "integrity": "sha1-RTt6oeBeA/zEi442iGUSOuP8MT8=", + "requires": { + "bindings": "1.2.1", + "nan": "2.3.5", + "node-pre-gyp": "0.6.30" + } }, "bcrypt-pbkdf": { "version": "1.0.1", - "from": "bcrypt-pbkdf@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "optional": true + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } }, "bcryptjs": { "version": "2.3.0", - "from": "bcryptjs@2.3.0", - "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.3.0.tgz" + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.3.0.tgz", + "integrity": "sha1-WCaQDP73q680JccuTUZN5Qm4wuw=" }, "bindings": { "version": "1.2.1", - "from": "bindings@1.2.1", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz" + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz", + "integrity": "sha1-FK1hE4EtLTfXLme0ystLtyZQXxE=" }, "bl": { "version": "0.6.0", - "from": "bl@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-0.6.0.tgz" + "resolved": "https://registry.npmjs.org/bl/-/bl-0.6.0.tgz", + "integrity": "sha1-MJECmZNylBM4RO40qkeaU0S0zSk=", + "requires": { + "readable-stream": "1.0.34" + } }, "block-stream": { "version": "0.0.9", - "from": "block-stream@*", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz" + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "requires": { + "inherits": "2.0.3" + } }, "bluebird": { "version": "3.5.0", - "from": "bluebird@>=3.3.4 <4.0.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz" + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", + "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=" }, "body-parser": { "version": "1.18.0", - "from": "body-parser@>=1.13.1 <2.0.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.0.tgz", + "integrity": "sha1-07Ik1Gf6LOjUNYnAJFBDJnwJNjQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "1.0.4", + "debug": "2.6.8", + "depd": "1.1.1", + "http-errors": "1.6.2", + "iconv-lite": "0.4.18", + "on-finished": "2.3.0", + "qs": "6.5.0", + "raw-body": "2.3.1", + "type-is": "1.6.15" + }, "dependencies": { "debug": { "version": "2.6.8", - "from": "debug@2.6.8", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz" + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "requires": { + "ms": "2.0.0" + } }, "iconv-lite": { "version": "0.4.18", - "from": "iconv-lite@0.4.18", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz" + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz", + "integrity": "sha1-I9hlaxaq5nQqwpcy6o8DNqR4nPI=" }, "qs": { "version": "6.5.0", - "from": "qs@6.5.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.0.tgz" + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.0.tgz", + "integrity": "sha1-jQSVTTZN7z78VbWgeT4eLIsebkk=" } } }, "boom": { "version": "2.10.1", - "from": "boom@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz" + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "requires": { + "hoek": "2.16.3" + } }, "brace-expansion": { "version": "1.1.8", - "from": "brace-expansion@>=1.1.7 <2.0.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz" + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } }, "broadway": { "version": "0.3.6", - "from": "broadway@>=0.3.2 <0.4.0", "resolved": "https://registry.npmjs.org/broadway/-/broadway-0.3.6.tgz", + "integrity": "sha1-fb7waLlUt5B5Jf1USWO1eKkCuno=", "dev": true, + "requires": { + "cliff": "0.1.9", + "eventemitter2": "0.4.14", + "nconf": "0.6.9", + "utile": "0.2.1", + "winston": "0.8.0" + }, "dependencies": { "async": { "version": "0.2.10", - "from": "async@>=0.2.0 <0.3.0", "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", "dev": true }, "cliff": { "version": "0.1.9", - "from": "cliff@0.1.9", "resolved": "https://registry.npmjs.org/cliff/-/cliff-0.1.9.tgz", - "dev": true + "integrity": "sha1-ohHgnGo947oa8n0EnTASUNGIErw=", + "dev": true, + "requires": { + "colors": "0.6.2", + "eyes": "0.1.8", + "winston": "0.8.0" + } }, "winston": { "version": "0.8.0", - "from": "winston@0.8.0", "resolved": "https://registry.npmjs.org/winston/-/winston-0.8.0.tgz", - "dev": true + "integrity": "sha1-YdCDD6aZcGISIGsKK1ymmpMENmg=", + "dev": true, + "requires": { + "async": "0.2.10", + "colors": "0.6.2", + "cycle": "1.0.3", + "eyes": "0.1.8", + "pkginfo": "0.3.1", + "stack-trace": "0.0.9" + } } } }, "browserslist": { "version": "1.7.7", - "from": "browserslist@>=1.7.6 <2.0.0", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", - "dev": true + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "dev": true, + "requires": { + "caniuse-db": "1.0.30000740", + "electron-to-chromium": "1.3.24" + } }, "bson": { "version": "1.0.4", - "from": "bson@>=1.0.4 <1.1.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.4.tgz" + "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.4.tgz", + "integrity": "sha1-k8ENOeqltYQVy8QFLz5T5WKwtyw=" }, "buffer": { "version": "4.9.1", - "from": "buffer@4.9.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "requires": { + "base64-js": "1.2.1", + "ieee754": "1.1.8", + "isarray": "1.0.0" + }, "dependencies": { "isarray": { "version": "1.0.0", - "from": "isarray@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" } } }, "buffer-crc32": { "version": "0.2.13", - "from": "buffer-crc32@>=0.2.1 <0.3.0", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz" + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" }, "buffer-equal-constant-time": { "version": "1.0.1", - "from": "buffer-equal-constant-time@1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" }, "buffer-shims": { "version": "1.0.0", - "from": "buffer-shims@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=" }, "bufferedstream": { "version": "1.6.0", - "from": "bufferedstream@1.6.0", - "resolved": "https://registry.npmjs.org/bufferedstream/-/bufferedstream-1.6.0.tgz" + "resolved": "https://registry.npmjs.org/bufferedstream/-/bufferedstream-1.6.0.tgz", + "integrity": "sha1-G1a+ZxMhYtn0aLyIbdLJFg3fKII=" }, "buildmail": { "version": "3.3.2", - "from": "buildmail@3.3.2", "resolved": "https://registry.npmjs.org/buildmail/-/buildmail-3.3.2.tgz", + "integrity": "sha1-fJ4pDUKoWAiUrzUoG8xt6D3+Ehw=", + "requires": { + "addressparser": "1.0.0", + "libbase64": "0.1.0", + "libmime": "2.0.0", + "libqp": "1.1.0", + "nodemailer-fetch": "1.2.1", + "nodemailer-shared": "1.0.3" + }, "dependencies": { "addressparser": { "version": "1.0.0", - "from": "addressparser@1.0.0", - "resolved": "https://registry.npmjs.org/addressparser/-/addressparser-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/addressparser/-/addressparser-1.0.0.tgz", + "integrity": "sha1-ighXVDABSXZoaqG3bgx0xadwKj4=" } } }, "bunyan": { "version": "0.22.1", - "from": "bunyan@0.22.1", - "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-0.22.1.tgz" + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-0.22.1.tgz", + "integrity": "sha1-Agw4O+1iWvXGyINN2MSsoN0Pdlw=", + "requires": { + "dtrace-provider": "0.2.8", + "mv": "0.0.5" + } }, "busboy": { "version": "0.2.14", - "from": "busboy@>=0.2.9 <0.3.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", + "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", + "requires": { + "dicer": "0.2.5", + "readable-stream": "1.1.14" + }, "dependencies": { "readable-stream": { "version": "1.1.14", - "from": "readable-stream@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz" + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } } } }, "bytes": { "version": "3.0.0", - "from": "bytes@3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz" + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" }, "camelcase": { "version": "1.2.1", - "from": "camelcase@>=1.0.2 <2.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz" + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" }, "camelize": { "version": "1.0.0", - "from": "camelize@1.0.0", - "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", + "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" }, "caniuse-db": { "version": "1.0.30000740", - "from": "caniuse-db@>=1.0.30000634 <2.0.0", "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000740.tgz", + "integrity": "sha1-A/yqoXbj7QdYlfctRsGhIUm76sk=", "dev": true }, "caseless": { "version": "0.12.0", - "from": "caseless@>=0.12.0 <0.13.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "center-align": { "version": "0.1.3", - "from": "center-align@>=0.1.1 <0.2.0", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz" + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "requires": { + "align-text": "0.1.4", + "lazy-cache": "1.0.4" + } }, "chai": { "version": "3.5.0", - "from": "chai@3.5.0", "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", - "dev": true + "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", + "dev": true, + "requires": { + "assertion-error": "1.0.2", + "deep-eql": "0.1.3", + "type-detect": "1.0.0" + } }, "chai-spies": { "version": "0.7.1", - "from": "chai-spies@latest", - "resolved": "https://registry.npmjs.org/chai-spies/-/chai-spies-0.7.1.tgz" + "resolved": "https://registry.npmjs.org/chai-spies/-/chai-spies-0.7.1.tgz", + "integrity": "sha1-ND2Z9RJEIS6LF+ZLk5lv97LCqbE=" }, "chalk": { "version": "1.1.3", - "from": "chalk@>=1.1.3 <2.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, "dependencies": { "supports-color": { "version": "2.0.0", - "from": "supports-color@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true } } }, "character-parser": { "version": "1.2.0", - "from": "character-parser@1.2.0", - "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-1.2.0.tgz" + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-1.2.0.tgz", + "integrity": "sha1-lBNNbl2HCjm+NZ99IkYJNRhN3vY=" }, "check-error": { "version": "1.0.2", - "from": "check-error@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz" + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=" }, "clean-css": { "version": "3.4.28", - "from": "clean-css@>=3.3.0 <4.0.0", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.28.tgz", + "integrity": "sha1-vxlF6C/ICPVWlebd6uwBQA79A/8=", + "requires": { + "commander": "2.8.1", + "source-map": "0.4.4" + }, "dependencies": { "commander": { "version": "2.8.1", - "from": "commander@>=2.8.0 <2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz" + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "requires": { + "graceful-readlink": "1.0.1" + } }, "source-map": { "version": "0.4.4", - "from": "source-map@>=0.4.0 <0.5.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz" + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "requires": { + "amdefine": "1.0.1" + } } } }, "cliff": { "version": "0.1.10", - "from": "cliff@>=0.1.9 <0.2.0", "resolved": "https://registry.npmjs.org/cliff/-/cliff-0.1.10.tgz", + "integrity": "sha1-U74z6p9ZvshWCe4wCsQgdgPlIBM=", "dev": true, + "requires": { + "colors": "1.0.3", + "eyes": "0.1.8", + "winston": "0.8.3" + }, "dependencies": { "colors": { "version": "1.0.3", - "from": "colors@>=1.0.3 <1.1.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "dev": true } } }, "cliui": { "version": "2.1.0", - "from": "cliui@>=2.1.0 <3.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz" + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "requires": { + "center-align": "0.1.3", + "right-align": "0.1.3", + "wordwrap": "0.0.2" + } }, "clone": { "version": "1.0.2", - "from": "clone@1.0.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz" + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz", + "integrity": "sha1-Jgt6meux7f4kdTgXX3gyQ8sZ0Uk=" }, "cluster-key-slot": { "version": "1.0.8", - "from": "cluster-key-slot@>=1.0.6 <2.0.0", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.0.8.tgz" + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.0.8.tgz", + "integrity": "sha1-dlRVYIWmUzCTKi6LWXb44tCz5BQ=" }, "co": { "version": "4.6.0", - "from": "co@>=4.6.0 <5.0.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz" + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" }, "code-point-at": { "version": "1.1.0", - "from": "code-point-at@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz" + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "coffee-script": { "version": "1.12.4", - "from": "coffee-script@1.12.4", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.4.tgz" + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.4.tgz", + "integrity": "sha1-/hvO2X/h+zknuZjytFYW4GWL4f8=" }, "colors": { "version": "0.6.2", - "from": "colors@>=0.6.2 <0.7.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz" + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", + "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=" }, "combined-stream": { "version": "1.0.5", - "from": "combined-stream@>=1.0.5 <1.1.0", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz" + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "requires": { + "delayed-stream": "1.0.0" + } }, "commander": { "version": "2.1.0", - "from": "commander@2.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.1.0.tgz" + "resolved": "https://registry.npmjs.org/commander/-/commander-2.1.0.tgz", + "integrity": "sha1-0SG7roYNmZKj1Re6lvVliOR8Z4E=" }, "concat-map": { "version": "0.0.1", - "from": "concat-map@0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", + "requires": { + "inherits": "2.0.3", + "readable-stream": "1.0.34", + "typedarray": "0.0.6" + } }, "connect": { "version": "3.6.2", - "from": "connect@3.6.2", "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.2.tgz", + "integrity": "sha1-aU6NIGgb/kkCgsiriGvpjwn0L+c=", + "requires": { + "debug": "2.6.7", + "finalhandler": "1.0.3", + "parseurl": "1.3.2", + "utils-merge": "1.0.0" + }, "dependencies": { "debug": { "version": "2.6.7", - "from": "debug@2.6.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.7.tgz" + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.7.tgz", + "integrity": "sha1-krrR9tBbu2u6Isyoi80OyJTChh4=", + "requires": { + "ms": "2.0.0" + } }, "escape-html": { "version": "1.0.3", - "from": "escape-html@>=1.0.3 <1.1.0", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, "finalhandler": { "version": "1.0.3", - "from": "finalhandler@1.0.3", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.3.tgz" + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.3.tgz", + "integrity": "sha1-70fneVDpmXgOhgIqVg4yF+DQzIk=", + "requires": { + "debug": "2.6.7", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "statuses": "1.3.1", + "unpipe": "1.0.0" + } } } }, "connect-redis": { "version": "3.3.0", - "from": "connect-redis@>=3.1.0 <4.0.0", "resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-3.3.0.tgz", + "integrity": "sha1-yVEMGlZ/9xDrJRDmp1CfqSsiMt8=", + "requires": { + "debug": "2.6.8", + "redis": "2.8.0" + }, "dependencies": { "debug": { "version": "2.6.8", - "from": "debug@>=2.2.0 <3.0.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz" + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "requires": { + "ms": "2.0.0" + } }, "redis": { "version": "2.8.0", - "from": "redis@>=2.1.0 <3.0.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz" + "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", + "integrity": "sha1-ICKI4/WMSfYHnZevehDhMDrhSwI=", + "requires": { + "double-ended-queue": "2.1.0-0", + "redis-commands": "1.3.1", + "redis-parser": "2.6.0" + } } } }, "console-control-strings": { "version": "1.1.0", - "from": "console-control-strings@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, "constantinople": { "version": "2.0.1", - "from": "constantinople@>=2.0.0 <2.1.0", - "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-2.0.1.tgz" + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-2.0.1.tgz", + "integrity": "sha1-WCn4VvMBqb2xB9k1932OuMzsTHk=", + "requires": { + "uglify-js": "2.4.24" + } }, "content-disposition": { "version": "0.5.0", - "from": "content-disposition@0.5.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.0.tgz" + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.0.tgz", + "integrity": "sha1-QoT+auBjCHRjnkToCkGMKTQTXp4=" }, "content-security-policy-builder": { "version": "1.1.0", - "from": "content-security-policy-builder@1.1.0", - "resolved": "https://registry.npmjs.org/content-security-policy-builder/-/content-security-policy-builder-1.1.0.tgz" + "resolved": "https://registry.npmjs.org/content-security-policy-builder/-/content-security-policy-builder-1.1.0.tgz", + "integrity": "sha1-2R8bB2I2wRmFDH3umSS/VeBXcrM=", + "requires": { + "dashify": "0.2.2" + } }, "content-type": { "version": "1.0.4", - "from": "content-type@>=1.0.2 <1.1.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz" + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=" }, "contentful": { "version": "3.8.1", - "from": "contentful@>=3.3.14 <4.0.0", "resolved": "https://registry.npmjs.org/contentful/-/contentful-3.8.1.tgz", + "integrity": "sha1-ulMsADKhZcIqJRzpZKfznUARwhs=", + "requires": { + "babel-runtime": "6.3.19", + "contentful-sdk-core": "2.5.0", + "json-stringify-safe": "5.0.1", + "lodash": "4.2.1" + }, "dependencies": { "lodash": { "version": "4.2.1", - "from": "lodash@>=4.2.0 <4.3.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.2.1.tgz" + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.2.1.tgz", + "integrity": "sha1-Fx/c+7ww1onFRM0YwFKfVt5sGqk=" } } }, "contentful-sdk-core": { "version": "2.5.0", - "from": "contentful-sdk-core@>=2.5.0 <2.6.0", - "resolved": "https://registry.npmjs.org/contentful-sdk-core/-/contentful-sdk-core-2.5.0.tgz" + "resolved": "https://registry.npmjs.org/contentful-sdk-core/-/contentful-sdk-core-2.5.0.tgz", + "integrity": "sha1-4crWt2YGVLsQlbOF/Vq8IlGZJz8=", + "requires": { + "babel-runtime": "6.3.19", + "follow-redirects": "0.0.7", + "lodash": "4.17.4", + "qs": "6.4.0" + } }, "cookie": { "version": "0.2.4", - "from": "cookie@>=0.2.3 <0.3.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.2.4.tgz" + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.2.4.tgz", + "integrity": "sha1-qMFVqnubLPLE0y68e5oKoojMxr0=" }, "cookie-parser": { "version": "1.3.5", - "from": "cookie-parser@1.3.5", "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.3.5.tgz", + "integrity": "sha1-nXVVcPtdF4kHcSJ6AjFNm+fPg1Y=", + "requires": { + "cookie": "0.1.3", + "cookie-signature": "1.0.6" + }, "dependencies": { "cookie": { "version": "0.1.3", - "from": "cookie@0.1.3", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.3.tgz" + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.3.tgz", + "integrity": "sha1-5zSlwUF/zkctWu+Cw4HKu2TRpDU=" } } }, "cookie-signature": { "version": "1.0.6", - "from": "cookie-signature@1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, "cookies": { "version": "0.7.1", - "from": "cookies@>=0.2.2", "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.7.1.tgz", - "dev": true + "integrity": "sha1-fIphX1SBxhq58WyDNzG8uPZjuZs=", + "dev": true, + "requires": { + "depd": "1.1.1", + "keygrip": "1.0.2" + } }, "core-js": { "version": "1.2.7", - "from": "core-js@>=1.2.0 <2.0.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz" + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" }, "core-util-is": { "version": "1.0.2", - "from": "core-util-is@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "crc": { "version": "3.4.4", - "from": "crc@3.4.4", - "resolved": "https://registry.npmjs.org/crc/-/crc-3.4.4.tgz" + "resolved": "https://registry.npmjs.org/crc/-/crc-3.4.4.tgz", + "integrity": "sha1-naHpgOO9RPxck79as9ozeNheRms=" }, "crc32-stream": { "version": "0.2.0", - "from": "crc32-stream@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-0.2.0.tgz" + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-0.2.0.tgz", + "integrity": "sha1-XIDUgMhoL5BLbxVTDbvguMBj274=", + "requires": { + "buffer-crc32": "0.2.13", + "readable-stream": "1.0.34" + } }, "cryptiles": { "version": "2.0.5", - "from": "cryptiles@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz" + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "requires": { + "boom": "2.10.1" + } }, "crypto-browserify": { "version": "1.0.9", - "from": "crypto-browserify@1.0.9", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-1.0.9.tgz" + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-1.0.9.tgz", + "integrity": "sha1-zFRJaF37hesRyYKKzHy4erW7/MA=" }, "csrf": { "version": "3.0.6", - "from": "csrf@>=3.0.3 <3.1.0", - "resolved": "https://registry.npmjs.org/csrf/-/csrf-3.0.6.tgz" + "resolved": "https://registry.npmjs.org/csrf/-/csrf-3.0.6.tgz", + "integrity": "sha1-thEg3c7q/JHnbtUxO7XAsmZ7cQo=", + "requires": { + "rndm": "1.2.0", + "tsscmp": "1.0.5", + "uid-safe": "2.1.4" + } }, "css": { "version": "1.0.8", - "from": "css@>=1.0.8 <1.1.0", - "resolved": "https://registry.npmjs.org/css/-/css-1.0.8.tgz" + "resolved": "https://registry.npmjs.org/css/-/css-1.0.8.tgz", + "integrity": "sha1-k4aBHKgrzMnuf7WnMrHioxfIo+c=", + "requires": { + "css-parse": "1.0.4", + "css-stringify": "1.0.5" + } }, "css-parse": { "version": "1.0.4", - "from": "css-parse@1.0.4", - "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.0.4.tgz" + "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.0.4.tgz", + "integrity": "sha1-OLBQP7+dqfVOnB29pg4UXHcRe90=" }, "css-stringify": { "version": "1.0.5", - "from": "css-stringify@1.0.5", - "resolved": "https://registry.npmjs.org/css-stringify/-/css-stringify-1.0.5.tgz" + "resolved": "https://registry.npmjs.org/css-stringify/-/css-stringify-1.0.5.tgz", + "integrity": "sha1-sNBClG2ylTu50pKQCmy19tASIDE=" }, "csurf": { "version": "1.9.0", - "from": "csurf@>=1.8.3 <2.0.0", "resolved": "https://registry.npmjs.org/csurf/-/csurf-1.9.0.tgz", + "integrity": "sha1-SdLGkl/87Ht95VlZfBU/pTM2QTM=", + "requires": { + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "csrf": "3.0.6", + "http-errors": "1.5.1" + }, "dependencies": { "cookie": { "version": "0.3.1", - "from": "cookie@0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz" + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" }, "http-errors": { "version": "1.5.1", - "from": "http-errors@>=1.5.0 <1.6.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.5.1.tgz" + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.5.1.tgz", + "integrity": "sha1-eIwNLB3iyBuebowBhDtrl+uSB1A=", + "requires": { + "inherits": "2.0.3", + "setprototypeof": "1.0.2", + "statuses": "1.3.1" + } }, "setprototypeof": { "version": "1.0.2", - "from": "setprototypeof@1.0.2", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.2.tgz" + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.2.tgz", + "integrity": "sha1-gaVSFB7BBLiOic44MQOtXGZWTQg=" } } }, "ctype": { "version": "0.5.3", - "from": "ctype@0.5.3", "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz", + "integrity": "sha1-gsGMJGH3QRTvFsE1IkrQuRRMoS8=", "dev": true, "optional": true }, "cycle": { "version": "1.0.3", - "from": "cycle@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", + "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=", "dev": true }, "dashdash": { "version": "1.14.1", - "from": "dashdash@>=1.12.0 <2.0.0", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "1.0.0" + }, "dependencies": { "assert-plus": { "version": "1.0.0", - "from": "assert-plus@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" } } }, "dasherize": { "version": "2.0.0", - "from": "dasherize@2.0.0", - "resolved": "https://registry.npmjs.org/dasherize/-/dasherize-2.0.0.tgz" + "resolved": "https://registry.npmjs.org/dasherize/-/dasherize-2.0.0.tgz", + "integrity": "sha1-bYCcnNDPe7iVLYD8hPoT1H3bEwg=" }, "dashify": { "version": "0.2.2", - "from": "dashify@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/dashify/-/dashify-0.2.2.tgz" + "resolved": "https://registry.npmjs.org/dashify/-/dashify-0.2.2.tgz", + "integrity": "sha1-agdBWgHJH69KMuONnfunH2HLIP4=" }, "dateformat": { "version": "1.0.4-1.2.3", - "from": "dateformat@1.0.4-1.2.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.4-1.2.3.tgz" + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.4-1.2.3.tgz", + "integrity": "sha1-TBKxAb3buIgMI1babkVEWaXri3U=" }, "debug": { "version": "1.0.5", - "from": "debug@>=1.0.2 <1.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-1.0.5.tgz" + "resolved": "https://registry.npmjs.org/debug/-/debug-1.0.5.tgz", + "integrity": "sha1-9yQSF0MPmd7EwrRz6rkiKOh0wqw=", + "requires": { + "ms": "2.0.0" + } }, "decamelize": { "version": "1.2.0", - "from": "decamelize@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, "deep-eql": { "version": "0.1.3", - "from": "deep-eql@>=0.1.3 <0.2.0", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", "dev": true, + "requires": { + "type-detect": "0.1.1" + }, "dependencies": { "type-detect": { "version": "0.1.1", - "from": "type-detect@0.1.1", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", + "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", "dev": true } } }, "deep-equal": { "version": "1.0.1", - "from": "deep-equal@>=1.0.1 <1.1.0", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=" }, "deep-extend": { "version": "0.4.2", - "from": "deep-extend@>=0.4.0 <0.5.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz" + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", + "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=" }, "define-properties": { "version": "1.1.2", - "from": "define-properties@>=1.1.2 <2.0.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz" + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", + "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", + "requires": { + "foreach": "2.0.5", + "object-keys": "1.0.11" + } }, "defined": { "version": "1.0.0", - "from": "defined@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" }, "deflate-crc32-stream": { "version": "0.1.2", - "from": "deflate-crc32-stream@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/deflate-crc32-stream/-/deflate-crc32-stream-0.1.2.tgz" + "resolved": "https://registry.npmjs.org/deflate-crc32-stream/-/deflate-crc32-stream-0.1.2.tgz", + "integrity": "sha1-l16g5zA7ddhSMhmKt7QFwtR7qtU=", + "requires": { + "buffer-crc32": "0.2.13" + } }, "delayed-stream": { "version": "1.0.0", - "from": "delayed-stream@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "delegates": { "version": "1.0.0", - "from": "delegates@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" }, "depd": { "version": "1.1.1", - "from": "depd@>=1.1.1 <1.2.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz" + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" }, "destroy": { "version": "1.0.3", - "from": "destroy@1.0.3", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.3.tgz" + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.3.tgz", + "integrity": "sha1-tDO0ck5x/YVR2YhRdIUcX8N34sk=" }, "dicer": { "version": "0.2.5", - "from": "dicer@0.2.5", "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", + "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", + "requires": { + "readable-stream": "1.1.14", + "streamsearch": "0.1.2" + }, "dependencies": { "readable-stream": { "version": "1.1.14", - "from": "readable-stream@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz" + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } } } }, "diff": { "version": "1.0.7", - "from": "diff@1.0.7", - "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz" + "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz", + "integrity": "sha1-JLuwAcSn1VIhaefKvbLCgU7ZHPQ=" }, "director": { "version": "1.2.7", - "from": "director@1.2.7", "resolved": "https://registry.npmjs.org/director/-/director-1.2.7.tgz", + "integrity": "sha1-v9N0EHX9f7GlsuE2WMX0vsd3NvM=", "dev": true }, "dns-prefetch-control": { "version": "0.1.0", - "from": "dns-prefetch-control@0.1.0", - "resolved": "https://registry.npmjs.org/dns-prefetch-control/-/dns-prefetch-control-0.1.0.tgz" + "resolved": "https://registry.npmjs.org/dns-prefetch-control/-/dns-prefetch-control-0.1.0.tgz", + "integrity": "sha1-YN20V3dOF48flBXwyrsOhbCzALI=" }, "doctypes": { "version": "1.1.0", - "from": "doctypes@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz" + "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", + "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=" }, "dom-serializer": { "version": "0.1.0", - "from": "dom-serializer@>=0.0.0 <1.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", "dev": true, + "requires": { + "domelementtype": "1.1.3", + "entities": "1.1.1" + }, "dependencies": { "domelementtype": { "version": "1.1.3", - "from": "domelementtype@>=1.1.1 <1.2.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", "dev": true } } }, "domelementtype": { "version": "1.3.0", - "from": "domelementtype@>=1.3.0 <2.0.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", + "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", "dev": true }, "domhandler": { "version": "2.4.1", - "from": "domhandler@>=2.3.0 <3.0.0", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.1.tgz", - "dev": true + "integrity": "sha1-iS5HAAqZvlW783dP/qBWHYh5wlk=", + "dev": true, + "requires": { + "domelementtype": "1.3.0" + } }, "domutils": { "version": "1.6.2", - "from": "domutils@>=1.5.1 <2.0.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.6.2.tgz", - "dev": true + "integrity": "sha1-GVjMC0yUJuntNn+xyOhUiRsPo/8=", + "dev": true, + "requires": { + "dom-serializer": "0.1.0", + "domelementtype": "1.3.0" + } }, "dont-sniff-mimetype": { "version": "1.0.0", - "from": "dont-sniff-mimetype@1.0.0", - "resolved": "https://registry.npmjs.org/dont-sniff-mimetype/-/dont-sniff-mimetype-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/dont-sniff-mimetype/-/dont-sniff-mimetype-1.0.0.tgz", + "integrity": "sha1-WTKJDcn04vGeXrAqIAJuXl78j1g=" }, "dottie": { "version": "1.1.1", - "from": "dottie@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/dottie/-/dottie-1.1.1.tgz" + "resolved": "https://registry.npmjs.org/dottie/-/dottie-1.1.1.tgz", + "integrity": "sha1-RcKj9IvWUo7u0memmoSOqspvqmo=" }, "double-ended-queue": { "version": "2.1.0-0", - "from": "double-ended-queue@>=2.1.0-0 <3.0.0", - "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz" + "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", + "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=" + }, + "dtrace-provider": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.2.8.tgz", + "integrity": "sha1-4kPxkhmqlfvw2PL/sH9b1k6U/iA=", + "optional": true }, "each-series": { "version": "1.0.0", - "from": "each-series@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/each-series/-/each-series-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/each-series/-/each-series-1.0.0.tgz", + "integrity": "sha1-+Ibmxm39sl7x/nNWQUbuXLR4r8s=" }, "ecc-jsbn": { "version": "0.1.1", - "from": "ecc-jsbn@>=0.1.1 <0.2.0", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "optional": true + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "optional": true, + "requires": { + "jsbn": "0.1.1" + } }, "ecdsa-sig-formatter": { "version": "1.0.9", - "from": "ecdsa-sig-formatter@1.0.9", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz" + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz", + "integrity": "sha1-S8kmJ07Dtau1AW5+HWCSGsJisqE=", + "requires": { + "base64url": "2.0.0", + "safe-buffer": "5.1.1" + } }, "ee-first": { "version": "1.1.1", - "from": "ee-first@1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "ejs": { "version": "0.8.8", - "from": "ejs@>=0.8.3 <0.9.0", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-0.8.8.tgz" + "resolved": "https://registry.npmjs.org/ejs/-/ejs-0.8.8.tgz", + "integrity": "sha1-/9xW3MNdApJt1QrRNDm7xUBh1Zg=" }, "electron-to-chromium": { "version": "1.3.24", - "from": "electron-to-chromium@>=1.2.7 <2.0.0", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.24.tgz", + "integrity": "sha1-m3uIuwXOufoBahd4M8wt3jiPIbY=", "dev": true }, "encodeurl": { "version": "1.0.1", - "from": "encodeurl@>=1.0.1 <1.1.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", + "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=" }, "encoding": { "version": "0.1.12", - "from": "encoding@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "requires": { + "iconv-lite": "0.4.19" + }, "dependencies": { "iconv-lite": { "version": "0.4.19", - "from": "iconv-lite@>=0.4.13 <0.5.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz" + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha1-90aPYBNfXl2tM5nAqBvpoWA6CCs=" } } }, "end-of-stream": { "version": "0.1.5", - "from": "end-of-stream@>=0.1.3 <0.2.0", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-0.1.5.tgz" + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-0.1.5.tgz", + "integrity": "sha1-jhdyBsPICDfYVjLouTWd/osvbq8=", + "requires": { + "once": "1.3.3" + } }, "entities": { "version": "1.1.1", - "from": "entities@>=1.1.1 <2.0.0", "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", + "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", "dev": true }, "es-abstract": { "version": "1.8.2", - "from": "es-abstract@>=1.5.0 <2.0.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.8.2.tgz" + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.8.2.tgz", + "integrity": "sha1-JRAyY9xN7L2mDgxzfKMjE1GAJ+4=", + "requires": { + "es-to-primitive": "1.1.1", + "function-bind": "1.1.1", + "has": "1.0.1", + "is-callable": "1.1.3", + "is-regex": "1.0.4" + } }, "es-to-primitive": { "version": "1.1.1", - "from": "es-to-primitive@>=1.1.1 <2.0.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz" + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", + "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", + "requires": { + "is-callable": "1.1.3", + "is-date-object": "1.0.1", + "is-symbol": "1.0.1" + } }, "es6-promise": { "version": "4.1.1", - "from": "es6-promise@>=4.0.5 <5.0.0", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.1.1.tgz", + "integrity": "sha1-iBHpCRXZoNujYnTwskLb2nj5ySo=", "dev": true }, "escape-html": { "version": "1.0.2", - "from": "escape-html@1.0.2", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.2.tgz" + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.2.tgz", + "integrity": "sha1-130y+pjjjC9BroXpJ44ODmuhAiw=" }, "escape-string-regexp": { "version": "1.0.5", - "from": "escape-string-regexp@^1.0.2", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "esprima": { "version": "1.0.4", - "from": "esprima@>=1.0.2 <1.1.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz" + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", + "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=" }, "etag": { "version": "1.7.0", - "from": "etag@>=1.7.0 <1.8.0", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.7.0.tgz" + "resolved": "https://registry.npmjs.org/etag/-/etag-1.7.0.tgz", + "integrity": "sha1-A9MLX2fdbmMtKUXTDWZScxo01dg=" }, "event-stream": { "version": "0.5.3", - "from": "event-stream@>=0.5.0 <0.6.0", "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-0.5.3.tgz", + "integrity": "sha1-t3uTCfcQet3+q2PwwOr9jbC9jBw=", "dev": true, + "requires": { + "optimist": "0.2.8" + }, "dependencies": { "optimist": { "version": "0.2.8", - "from": "optimist@>=0.2.0 <0.3.0", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.2.8.tgz", - "dev": true + "integrity": "sha1-6YGrfiaLRXlIWTtVZ0wJmoFcrDE=", + "dev": true, + "requires": { + "wordwrap": "0.0.2" + } } } }, "eventemitter2": { "version": "0.4.14", - "from": "eventemitter2@>=0.4.13 <0.5.0", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz" + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", + "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=" }, "eventemitter3": { "version": "1.2.0", - "from": "eventemitter3@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz" + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", + "integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=" }, "events": { "version": "1.1.1", - "from": "events@>=1.1.1 <2.0.0", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz" + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" }, "exit": { "version": "0.1.2", - "from": "exit@>=0.1.1 <0.2.0", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=" }, "expect-ct": { "version": "0.1.0", - "from": "expect-ct@0.1.0", - "resolved": "https://registry.npmjs.org/expect-ct/-/expect-ct-0.1.0.tgz" + "resolved": "https://registry.npmjs.org/expect-ct/-/expect-ct-0.1.0.tgz", + "integrity": "sha1-UnNWeN4YUwiQ2Ne5XwrGNkCVgJQ=" }, "express": { "version": "4.13.0", - "from": "express@4.13.0", "resolved": "https://registry.npmjs.org/express/-/express-4.13.0.tgz", + "integrity": "sha1-Bni9vHJxUXCz/MkXBS8EbLlomt0=", + "requires": { + "accepts": "1.2.13", + "array-flatten": "1.1.0", + "content-disposition": "0.5.0", + "content-type": "1.0.4", + "cookie": "0.1.3", + "cookie-signature": "1.0.6", + "debug": "2.2.0", + "depd": "1.0.1", + "escape-html": "1.0.2", + "etag": "1.7.0", + "finalhandler": "0.4.0", + "fresh": "0.3.0", + "merge-descriptors": "1.0.0", + "methods": "1.1.2", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "path-to-regexp": "0.1.6", + "proxy-addr": "1.0.10", + "qs": "2.4.2", + "range-parser": "1.0.3", + "send": "0.13.0", + "serve-static": "1.10.3", + "type-is": "1.6.15", + "utils-merge": "1.0.0", + "vary": "1.0.1" + }, "dependencies": { "cookie": { "version": "0.1.3", - "from": "cookie@0.1.3", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.3.tgz" + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.3.tgz", + "integrity": "sha1-5zSlwUF/zkctWu+Cw4HKu2TRpDU=" }, "debug": { "version": "2.2.0", - "from": "debug@>=2.2.0 <2.3.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz" + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "requires": { + "ms": "0.7.1" + } }, "depd": { "version": "1.0.1", - "from": "depd@>=1.0.1 <1.1.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/depd/-/depd-1.0.1.tgz", + "integrity": "sha1-gK7GTJ1tl+ZcwqnKqTwKpqv3Oqo=" }, "ms": { "version": "0.7.1", - "from": "ms@0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" }, "qs": { "version": "2.4.2", - "from": "qs@2.4.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-2.4.2.tgz" + "resolved": "https://registry.npmjs.org/qs/-/qs-2.4.2.tgz", + "integrity": "sha1-9854jld33wtQENp/fE5zujJHD1o=" } } }, "express-session": { "version": "1.15.5", - "from": "express-session@>=1.14.2 <2.0.0", "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.15.5.tgz", + "integrity": "sha1-9JoYInJjsxb2+FRNpf7iWlQCWew=", + "requires": { + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "crc": "3.4.4", + "debug": "2.6.8", + "depd": "1.1.1", + "on-headers": "1.0.1", + "parseurl": "1.3.2", + "uid-safe": "2.1.4", + "utils-merge": "1.0.0" + }, "dependencies": { "cookie": { "version": "0.3.1", - "from": "cookie@0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz" + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" }, "debug": { "version": "2.6.8", - "from": "debug@2.6.8", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz" + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "requires": { + "ms": "2.0.0" + } } } }, "extend": { "version": "3.0.1", - "from": "extend@>=3.0.0 <3.1.0", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz" + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" }, "extendible": { "version": "0.1.1", - "from": "extendible@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/extendible/-/extendible-0.1.1.tgz" + "resolved": "https://registry.npmjs.org/extendible/-/extendible-0.1.1.tgz", + "integrity": "sha1-4qN+2HEp+0+VM+io11BiMKU5yQU=" + }, + "extract-zip": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.5.tgz", + "integrity": "sha1-maBnNbbqIOqbcF13ms/8yHz/BEA=", + "requires": { + "concat-stream": "1.6.0", + "debug": "1.0.5", + "mkdirp": "0.5.1", + "yauzl": "2.4.1" + }, + "dependencies": { + "yauzl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", + "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", + "requires": { + "fd-slicer": "1.0.1" + } + } + } }, "extsprintf": { "version": "1.3.0", - "from": "extsprintf@1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz" + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "eyes": { "version": "0.1.8", - "from": "eyes@>=0.1.8 <0.2.0", "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=", "dev": true }, "failure": { "version": "1.1.1", - "from": "failure@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/failure/-/failure-1.1.1.tgz" + "resolved": "https://registry.npmjs.org/failure/-/failure-1.1.1.tgz", + "integrity": "sha1-qOg9OxYC0kaL/2rU2QceAQO4Goc=" }, "faye-websocket": { "version": "0.10.0", - "from": "faye-websocket@>=0.10.0 <0.11.0", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "dev": true + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "dev": true, + "requires": { + "websocket-driver": "0.7.0" + } + }, + "fd-slicer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", + "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", + "requires": { + "pend": "1.2.0" + } }, "file-utils": { "version": "0.1.5", - "from": "file-utils@>=0.1.5 <0.2.0", "resolved": "https://registry.npmjs.org/file-utils/-/file-utils-0.1.5.tgz", + "integrity": "sha1-3IFTyFU4fLTaywoXJVMfpESmtIw=", + "requires": { + "findup-sync": "0.1.3", + "glob": "3.2.11", + "iconv-lite": "0.2.11", + "isbinaryfile": "0.1.9", + "lodash": "2.1.0", + "minimatch": "0.2.14", + "rimraf": "2.2.6" + }, "dependencies": { "lodash": { "version": "2.1.0", - "from": "lodash@>=2.1.0 <2.2.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.1.0.tgz" + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.1.0.tgz", + "integrity": "sha1-Bjfqqjaooc/IZcOt+5Qhib+wmY0=" } } }, "finalhandler": { "version": "0.4.0", - "from": "finalhandler@0.4.0", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.4.0.tgz", + "integrity": "sha1-llpS2ejQXSuFdUhUH7ibU6JJfZs=", + "requires": { + "debug": "2.2.0", + "escape-html": "1.0.2", + "on-finished": "2.3.0", + "unpipe": "1.0.0" + }, "dependencies": { "debug": { "version": "2.2.0", - "from": "debug@>=2.2.0 <2.3.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz" + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "requires": { + "ms": "0.7.1" + } }, "ms": { "version": "0.7.1", - "from": "ms@0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" } } }, "findup-sync": { "version": "0.1.3", - "from": "findup-sync@>=0.1.2 <0.2.0", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", + "integrity": "sha1-fz56l7gjksZTvwZYm9hRkOk8NoM=", + "requires": { + "glob": "3.2.11", + "lodash": "2.4.2" + }, "dependencies": { "lodash": { "version": "2.4.2", - "from": "lodash@>=2.4.1 <2.5.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=" } } }, "flatiron": { "version": "0.4.3", - "from": "flatiron@>=0.4.2 <0.5.0", "resolved": "https://registry.npmjs.org/flatiron/-/flatiron-0.4.3.tgz", + "integrity": "sha1-JIz3mj2n19w3nioRySonGcu1QPY=", "dev": true, + "requires": { + "broadway": "0.3.6", + "director": "1.2.7", + "optimist": "0.6.0", + "prompt": "0.2.14" + }, "dependencies": { "optimist": { "version": "0.6.0", - "from": "optimist@0.6.0", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.0.tgz", - "dev": true + "integrity": "sha1-aUJIJvNAX3nxQub8PZrljU27kgA=", + "dev": true, + "requires": { + "minimist": "0.0.8", + "wordwrap": "0.0.2" + } } } }, "flexbuffer": { "version": "0.0.6", - "from": "flexbuffer@0.0.6", - "resolved": "https://registry.npmjs.org/flexbuffer/-/flexbuffer-0.0.6.tgz" + "resolved": "https://registry.npmjs.org/flexbuffer/-/flexbuffer-0.0.6.tgz", + "integrity": "sha1-A5/fI/iCPkQMOPMnfm/vEXQhWzA=" }, "follow-redirects": { "version": "0.0.7", - "from": "follow-redirects@0.0.7", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-0.0.7.tgz", + "integrity": "sha1-NLkLqyqRGqNHVx2pDyK9NuzYqRk=", + "requires": { + "debug": "2.6.8", + "stream-consume": "0.1.0" + }, "dependencies": { "debug": { "version": "2.6.8", - "from": "debug@>=2.2.0 <3.0.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz" + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "requires": { + "ms": "2.0.0" + } } } }, "for-each": { "version": "0.3.2", - "from": "for-each@>=0.3.2 <0.4.0", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.2.tgz" + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.2.tgz", + "integrity": "sha1-LEBFC5NI6X8oEyJZO6lnBLmr1NQ=", + "requires": { + "is-function": "1.0.1" + } }, "foreach": { "version": "2.0.5", - "from": "foreach@>=2.0.5 <3.0.0", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz" + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" }, "forever": { "version": "0.14.2", - "from": "forever@>=0.14.1 <0.15.0", "resolved": "https://registry.npmjs.org/forever/-/forever-0.14.2.tgz", - "dev": true + "integrity": "sha1-6Tsr2UxXBavBmxXlTDEz1puinGs=", + "dev": true, + "requires": { + "cliff": "0.1.10", + "colors": "0.6.2", + "flatiron": "0.4.3", + "forever-monitor": "1.5.2", + "nconf": "0.6.9", + "nssocket": "0.5.3", + "optimist": "0.6.1", + "timespan": "2.3.0", + "utile": "0.2.1", + "winston": "0.8.3" + } }, "forever-agent": { "version": "0.6.1", - "from": "forever-agent@>=0.6.1 <0.7.0", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, "forever-monitor": { "version": "1.5.2", - "from": "forever-monitor@>=1.5.1 <1.6.0", "resolved": "https://registry.npmjs.org/forever-monitor/-/forever-monitor-1.5.2.tgz", + "integrity": "sha1-J5OI36k7CFNj1rKKgj7wpq7rNdc=", "dev": true, + "requires": { + "broadway": "0.3.6", + "minimatch": "1.0.0", + "ps-tree": "0.0.3", + "utile": "0.2.1", + "watch": "0.13.0" + }, "dependencies": { "minimatch": { "version": "1.0.0", - "from": "minimatch@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-1.0.0.tgz", - "dev": true + "integrity": "sha1-4N0hILSeG3JM6NcUxSCCKpQ4V20=", + "dev": true, + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } } } }, "form-data": { "version": "2.1.4", - "from": "form-data@>=2.1.1 <2.2.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz" + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.17" + } }, "formatio": { "version": "1.1.1", - "from": "formatio@1.1.1", "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.1.1.tgz", - "dev": true + "integrity": "sha1-XtPM1jZVEJc4NGXZlhmRAOhhYek=", + "dev": true, + "requires": { + "samsam": "1.1.2" + } }, "forwarded": { "version": "0.1.1", - "from": "forwarded@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.1.tgz" + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.1.tgz", + "integrity": "sha1-ik4wxkCwU5U5mjVJxzAldygEiWE=" }, "frameguard": { "version": "3.0.0", - "from": "frameguard@3.0.0", - "resolved": "https://registry.npmjs.org/frameguard/-/frameguard-3.0.0.tgz" + "resolved": "https://registry.npmjs.org/frameguard/-/frameguard-3.0.0.tgz", + "integrity": "sha1-e8rUae57lukdEs6zlZx4I1qScuk=" }, "fresh": { "version": "0.3.0", - "from": "fresh@0.3.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz" + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz", + "integrity": "sha1-ZR+DjiJCTnVm3hYdg1jKoZn4PU8=" }, "fs-extra": { - "version": "0.9.1", - "from": "fs-extra@>=0.9.1 <0.10.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.9.1.tgz", - "dependencies": { - "glob": { - "version": "7.1.2", - "from": "glob@>=7.0.5 <8.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz" - }, - "minimatch": { - "version": "3.0.4", - "from": "minimatch@>=3.0.4 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" - }, - "ncp": { - "version": "0.5.1", - "from": "ncp@>=0.5.1 <0.6.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.5.1.tgz" - }, - "rimraf": { - "version": "2.6.2", - "from": "rimraf@>=2.2.8 <3.0.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz" - } + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.2.tgz", + "integrity": "sha1-+RcExT0bRh+JNFKwwwfZmXZHq2s=", + "requires": { + "graceful-fs": "4.1.11", + "jsonfile": "4.0.0", + "universalify": "0.1.1" } }, "fs.realpath": { "version": "1.0.0", - "from": "fs.realpath@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fstream": { "version": "1.0.11", - "from": "fstream@>=1.0.2 <2.0.0", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz" + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.2.6" + } }, "fstream-ignore": { "version": "1.0.5", - "from": "fstream-ignore@>=1.0.5 <1.1.0", "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", + "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=", + "requires": { + "fstream": "1.0.11", + "inherits": "2.0.3", + "minimatch": "3.0.4" + }, "dependencies": { "minimatch": { "version": "3.0.4", - "from": "minimatch@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "requires": { + "brace-expansion": "1.1.8" + } } } }, "function-bind": { "version": "1.1.1", - "from": "function-bind@>=1.0.2 <2.0.0", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=" }, "gauge": { "version": "2.7.4", - "from": "gauge@>=2.7.3 <2.8.0", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz" + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "1.1.2", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } }, "gaze": { "version": "1.1.2", - "from": "gaze@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.2.tgz", - "dev": true + "integrity": "sha1-hHIkZ3rbiHDWeSV+0ziP22HkAQU=", + "dev": true, + "requires": { + "globule": "1.2.0" + } }, "generic-pool": { "version": "2.4.2", - "from": "generic-pool@2.4.2", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.4.2.tgz" + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.4.2.tgz", + "integrity": "sha1-iGvFvwvrfblugby7oHiBjeWmJoM=" }, "get-func-name": { "version": "2.0.0", - "from": "get-func-name@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz" + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=" }, "getobject": { "version": "0.1.0", - "from": "getobject@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz" + "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz", + "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=" }, "getpass": { "version": "0.1.7", - "from": "getpass@>=0.1.1 <0.2.0", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "1.0.0" + }, "dependencies": { "assert-plus": { "version": "1.0.0", - "from": "assert-plus@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" } } }, "glob": { "version": "3.2.11", - "from": "glob@>=3.2.6 <3.3.0", "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", + "requires": { + "inherits": "2.0.3", + "minimatch": "0.3.0" + }, "dependencies": { "minimatch": { "version": "0.3.0", - "from": "minimatch@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz" + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } } } }, "globule": { "version": "1.2.0", - "from": "globule@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz", + "integrity": "sha1-HcScaCLdnoovoAuiopUAboZkvQk=", "dev": true, + "requires": { + "glob": "7.1.2", + "lodash": "4.17.4", + "minimatch": "3.0.4" + }, "dependencies": { "glob": { "version": "7.1.2", - "from": "glob@>=7.1.1 <7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "dev": true + "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } }, "minimatch": { "version": "3.0.4", - "from": "minimatch@>=3.0.2 <3.1.0", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "dev": true + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } } } }, "graceful-fs": { "version": "4.1.11", - "from": "graceful-fs@>=4.1.2 <5.0.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" }, "graceful-readlink": { "version": "1.0.1", - "from": "graceful-readlink@>=1.0.0", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" }, "growl": { "version": "1.7.0", - "from": "growl@>=1.7.0 <1.8.0", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.7.0.tgz" + "resolved": "https://registry.npmjs.org/growl/-/growl-1.7.0.tgz", + "integrity": "sha1-3i1mE20ALhErpw8/EMMc98NQsto=" }, "grunt": { "version": "0.4.5", - "from": "grunt@>=0.4.5 <0.5.0", "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz", + "integrity": "sha1-VpN81RlDJK3/bSB2MYMqnWuk5/A=", + "requires": { + "async": "0.1.22", + "coffee-script": "1.3.3", + "colors": "0.6.2", + "dateformat": "1.0.2-1.2.3", + "eventemitter2": "0.4.14", + "exit": "0.1.2", + "findup-sync": "0.1.3", + "getobject": "0.1.0", + "glob": "3.1.21", + "grunt-legacy-log": "0.1.3", + "grunt-legacy-util": "0.2.0", + "hooker": "0.2.3", + "iconv-lite": "0.2.11", + "js-yaml": "2.0.5", + "lodash": "0.9.2", + "minimatch": "0.2.14", + "nopt": "1.0.10", + "rimraf": "2.2.8", + "underscore.string": "2.2.1", + "which": "1.0.9" + }, "dependencies": { "async": { "version": "0.1.22", - "from": "async@>=0.1.22 <0.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz" + "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz", + "integrity": "sha1-D8GqoIig4+8Ovi2IMbqw3PiEUGE=" }, "coffee-script": { "version": "1.3.3", - "from": "coffee-script@>=1.3.3 <1.4.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz" + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz", + "integrity": "sha1-FQ1rTLUiiUNp7+1qIQHCC8f0pPQ=" }, "dateformat": { "version": "1.0.2-1.2.3", - "from": "dateformat@1.0.2-1.2.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz" + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz", + "integrity": "sha1-sCIMAt6YYXQztyhRz0fePfLNvuk=" }, "glob": { "version": "3.1.21", - "from": "glob@>=3.1.21 <3.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz" + "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", + "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=", + "requires": { + "graceful-fs": "1.2.3", + "inherits": "1.0.2", + "minimatch": "0.2.14" + } }, "graceful-fs": { "version": "1.2.3", - "from": "graceful-fs@>=1.2.0 <1.3.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz" + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz", + "integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=" }, "inherits": { "version": "1.0.2", - "from": "inherits@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz" + "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz", + "integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=" }, "lodash": { "version": "0.9.2", - "from": "lodash@>=0.9.2 <0.10.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz" + "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz", + "integrity": "sha1-jzSZxSRdNG1oLlsNO0B2fgnxqSw=" }, "nopt": { "version": "1.0.10", - "from": "nopt@>=1.0.10 <1.1.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz" + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "requires": { + "abbrev": "1.1.0" + } }, "rimraf": { "version": "2.2.8", - "from": "rimraf@>=2.2.8 <2.3.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", + "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=" } } }, "grunt-available-tasks": { "version": "0.4.1", - "from": "grunt-available-tasks@0.4.1", "resolved": "https://registry.npmjs.org/grunt-available-tasks/-/grunt-available-tasks-0.4.1.tgz", + "integrity": "sha1-fyAoBUn6OFZxLp68Uj3YjBE7hZQ=", "dev": true, + "requires": { + "lodash": "2.4.2", + "underscore.string": "2.3.3" + }, "dependencies": { "lodash": { "version": "2.4.2", - "from": "lodash@>=2.4.0 <2.5.0", "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=", "dev": true }, "underscore.string": { "version": "2.3.3", - "from": "underscore.string@>=2.3.3 <2.4.0", "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", + "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=", "dev": true } } }, "grunt-bunyan": { "version": "0.5.0", - "from": "grunt-bunyan@>=0.5.0 <0.6.0", "resolved": "https://registry.npmjs.org/grunt-bunyan/-/grunt-bunyan-0.5.0.tgz", + "integrity": "sha1-aCnXbgGZQ9owQTk2MaNuKsgpsWw=", + "requires": { + "lodash": "2.4.2" + }, "dependencies": { "lodash": { "version": "2.4.2", - "from": "lodash@>=2.4.1 <2.5.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=" } } }, "grunt-contrib-clean": { "version": "0.5.0", - "from": "grunt-contrib-clean@0.5.0", "resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-0.5.0.tgz", - "dev": true + "integrity": "sha1-9T397ghJsce0Dp67umn0jExgecU=", + "dev": true, + "requires": { + "rimraf": "2.2.6" + } }, "grunt-contrib-coffee": { "version": "0.10.0", - "from": "grunt-contrib-coffee@0.10.0", "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.10.0.tgz", + "integrity": "sha1-1UntlxHN1mkWOWwRisf9LRF4C9E=", "dev": true, + "requires": { + "chalk": "0.4.0", + "coffee-script": "1.7.1", + "lodash": "2.4.2" + }, "dependencies": { "ansi-styles": { "version": "1.0.0", - "from": "ansi-styles@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz", + "integrity": "sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg=", "dev": true }, "chalk": { "version": "0.4.0", - "from": "chalk@>=0.4.0 <0.5.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz", - "dev": true + "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=", + "dev": true, + "requires": { + "ansi-styles": "1.0.0", + "has-color": "0.1.7", + "strip-ansi": "0.1.1" + } }, "coffee-script": { "version": "1.7.1", - "from": "coffee-script@>=1.7.0 <1.8.0", "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz", - "dev": true + "integrity": "sha1-YplqhheAx15tUGnROCJyO3NAS/w=", + "dev": true, + "requires": { + "mkdirp": "0.3.5" + } }, "lodash": { "version": "2.4.2", - "from": "lodash@~2.4.1", "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=", "dev": true }, "mkdirp": { "version": "0.3.5", - "from": "mkdirp@>=0.3.5 <0.4.0", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=", "dev": true }, "strip-ansi": { "version": "0.1.1", - "from": "strip-ansi@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz", + "integrity": "sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE=", "dev": true } } }, "grunt-contrib-less": { "version": "0.9.0", - "from": "grunt-contrib-less@0.9.0", "resolved": "https://registry.npmjs.org/grunt-contrib-less/-/grunt-contrib-less-0.9.0.tgz", + "integrity": "sha1-rx5udlEcK8Mo740oQFhkPKWIdnU=", "dev": true, + "requires": { + "chalk": "0.4.0", + "grunt-lib-contrib": "0.6.1", + "less": "1.6.3" + }, "dependencies": { "ansi-styles": { "version": "1.0.0", - "from": "ansi-styles@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz", + "integrity": "sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg=", "dev": true }, "chalk": { "version": "0.4.0", - "from": "chalk@>=0.4.0 <0.5.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz", - "dev": true + "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=", + "dev": true, + "requires": { + "ansi-styles": "1.0.0", + "has-color": "0.1.7", + "strip-ansi": "0.1.1" + } }, "strip-ansi": { "version": "0.1.1", - "from": "strip-ansi@~0.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz", + "integrity": "sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE=", "dev": true } } }, "grunt-contrib-requirejs": { "version": "0.4.1", - "from": "grunt-contrib-requirejs@0.4.1", "resolved": "https://registry.npmjs.org/grunt-contrib-requirejs/-/grunt-contrib-requirejs-0.4.1.tgz", - "dev": true + "integrity": "sha1-hiuhZxQbio82r1RE/qsycruM9L0=", + "dev": true, + "requires": { + "requirejs": "2.1.22" + } }, "grunt-contrib-watch": { "version": "1.0.0", - "from": "grunt-contrib-watch@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-1.0.0.tgz", + "integrity": "sha1-hKGnodar0m7VaEE0lscxM+mQAY8=", "dev": true, + "requires": { + "async": "1.5.2", + "gaze": "1.1.2", + "lodash": "3.10.1", + "tiny-lr": "0.2.1" + }, "dependencies": { "async": { "version": "1.5.2", - "from": "async@>=1.5.0 <2.0.0", "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true }, "lodash": { "version": "3.10.1", - "from": "lodash@>=3.10.1 <4.0.0", "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", "dev": true } } }, "grunt-env": { "version": "0.4.4", - "from": "grunt-env@0.4.4", "resolved": "https://registry.npmjs.org/grunt-env/-/grunt-env-0.4.4.tgz", + "integrity": "sha1-OziEOo1zcXfdyfiTh5+2nOGgvC8=", "dev": true, + "requires": { + "ini": "1.3.4", + "lodash": "2.4.2" + }, "dependencies": { "lodash": { "version": "2.4.2", - "from": "lodash@~2.4.1", "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=", "dev": true } } }, "grunt-exec": { "version": "0.4.7", - "from": "grunt-exec@>=0.4.7 <0.5.0", "resolved": "https://registry.npmjs.org/grunt-exec/-/grunt-exec-0.4.7.tgz", + "integrity": "sha1-QAUf+k6wyWV+BTuV6I1ENSocLCU=", "dev": true }, "grunt-execute": { "version": "0.2.2", - "from": "grunt-execute@>=0.2.2 <0.3.0", - "resolved": "https://registry.npmjs.org/grunt-execute/-/grunt-execute-0.2.2.tgz" + "resolved": "https://registry.npmjs.org/grunt-execute/-/grunt-execute-0.2.2.tgz", + "integrity": "sha1-TpRf5XlZzA3neZCDtrQq7ZYWNQo=" }, "grunt-file-append": { "version": "0.0.6", - "from": "grunt-file-append@0.0.6", "resolved": "https://registry.npmjs.org/grunt-file-append/-/grunt-file-append-0.0.6.tgz", + "integrity": "sha1-2nH07qPtOylROCKIWaFFZ2NX7z8=", "dev": true }, "grunt-forever": { "version": "0.4.7", - "from": "grunt-forever@>=0.4.7 <0.5.0", "resolved": "https://registry.npmjs.org/grunt-forever/-/grunt-forever-0.4.7.tgz", - "dev": true + "integrity": "sha1-dHDb4a2hFFAhZKTCoAOHXfj+EzA=", + "dev": true, + "requires": { + "forever": "0.14.2" + } }, "grunt-git-rev-parse": { "version": "0.1.5", - "from": "grunt-git-rev-parse@>=0.1.4 <0.2.0", "resolved": "https://registry.npmjs.org/grunt-git-rev-parse/-/grunt-git-rev-parse-0.1.5.tgz", + "integrity": "sha1-ntSngs7jCa9DHiKXGX6vyTOQa80=", "dev": true }, "grunt-legacy-log": { "version": "0.1.3", - "from": "grunt-legacy-log@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.3.tgz", + "integrity": "sha1-7ClCboAwIa9ZAp+H0vnNczWgVTE=", + "requires": { + "colors": "0.6.2", + "grunt-legacy-log-utils": "0.1.1", + "hooker": "0.2.3", + "lodash": "2.4.2", + "underscore.string": "2.3.3" + }, "dependencies": { "lodash": { "version": "2.4.2", - "from": "lodash@>=2.4.1 <2.5.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=" }, "underscore.string": { "version": "2.3.3", - "from": "underscore.string@>=2.3.3 <2.4.0", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz" + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", + "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=" } } }, "grunt-legacy-log-utils": { "version": "0.1.1", - "from": "grunt-legacy-log-utils@>=0.1.1 <0.2.0", "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-0.1.1.tgz", + "integrity": "sha1-wHBrndkGThFvNvI/5OawSGcsD34=", + "requires": { + "colors": "0.6.2", + "lodash": "2.4.2", + "underscore.string": "2.3.3" + }, "dependencies": { "lodash": { "version": "2.4.2", - "from": "lodash@>=2.4.1 <2.5.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=" }, "underscore.string": { "version": "2.3.3", - "from": "underscore.string@>=2.3.3 <2.4.0", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz" + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", + "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=" } } }, "grunt-legacy-util": { "version": "0.2.0", - "from": "grunt-legacy-util@>=0.2.0 <0.3.0", "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz", + "integrity": "sha1-kzJIhNv343qf98Am3/RR2UqeVUs=", + "requires": { + "async": "0.1.22", + "exit": "0.1.2", + "getobject": "0.1.0", + "hooker": "0.2.3", + "lodash": "0.9.2", + "underscore.string": "2.2.1", + "which": "1.0.9" + }, "dependencies": { "async": { "version": "0.1.22", - "from": "async@>=0.1.22 <0.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz" + "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz", + "integrity": "sha1-D8GqoIig4+8Ovi2IMbqw3PiEUGE=" }, "lodash": { "version": "0.9.2", - "from": "lodash@>=0.9.2 <0.10.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz" + "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz", + "integrity": "sha1-jzSZxSRdNG1oLlsNO0B2fgnxqSw=" } } }, "grunt-lib-contrib": { "version": "0.6.1", - "from": "grunt-lib-contrib@>=0.6.1 <0.7.0", "resolved": "https://registry.npmjs.org/grunt-lib-contrib/-/grunt-lib-contrib-0.6.1.tgz", - "dev": true + "integrity": "sha1-P1att9oG6BR5XuJBWw6+X7iQPrs=", + "dev": true, + "requires": { + "zlib-browserify": "0.0.1" + } }, "grunt-mocha-test": { "version": "0.9.0", - "from": "grunt-mocha-test@0.9.0", "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.9.0.tgz", - "dev": true + "integrity": "sha1-BNFfMl8OKJcrLMZB9OMEwGxFwQs=", + "dev": true, + "requires": { + "hooker": "0.2.3", + "mocha": "1.17.1" + } }, "grunt-newer": { "version": "1.3.0", - "from": "grunt-newer@>=1.2.0 <2.0.0", "resolved": "https://registry.npmjs.org/grunt-newer/-/grunt-newer-1.3.0.tgz", + "integrity": "sha1-g8y3od2ny9irI7BZAk6+YUrS80I=", "dev": true, + "requires": { + "async": "1.5.2", + "rimraf": "2.6.2" + }, "dependencies": { "async": { "version": "1.5.2", - "from": "async@^1.5.2", "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true }, "glob": { "version": "7.1.2", - "from": "glob@^7.0.5", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "dev": true + "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } }, "minimatch": { "version": "3.0.4", - "from": "minimatch@^3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "dev": true + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } }, "rimraf": { "version": "2.6.2", - "from": "rimraf@>=2.5.2 <3.0.0", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "dev": true + "integrity": "sha1-LtgVDSShbqhlHm1u8PR8QVjOejY=", + "dev": true, + "requires": { + "glob": "7.1.2" + } } } }, "grunt-parallel": { "version": "0.5.1", - "from": "grunt-parallel@>=0.5.1 <0.6.0", "resolved": "https://registry.npmjs.org/grunt-parallel/-/grunt-parallel-0.5.1.tgz", + "integrity": "sha1-X3ozdvYYVCQSjEsqLPLi0Hw39dk=", "dev": true, + "requires": { + "lpad": "0.1.0", + "q": "0.8.12" + }, "dependencies": { "q": { "version": "0.8.12", - "from": "q@>=0.8.12 <0.9.0", "resolved": "https://registry.npmjs.org/q/-/q-0.8.12.tgz", + "integrity": "sha1-kWKpHhGBnEvNp9oVz1/vqtB3iCM=", "dev": true } } }, "grunt-postcss": { "version": "0.8.0", - "from": "grunt-postcss@>=0.8.0 <0.9.0", "resolved": "https://registry.npmjs.org/grunt-postcss/-/grunt-postcss-0.8.0.tgz", + "integrity": "sha1-jzCor2B5A84MRfAfC+QsYOMc6w4=", "dev": true, + "requires": { + "chalk": "1.1.3", + "diff": "2.2.3", + "postcss": "5.2.17" + }, "dependencies": { "diff": { "version": "2.2.3", - "from": "diff@>=2.0.2 <3.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-2.2.3.tgz", + "integrity": "sha1-YOr9DSjukG5Oj/ClLBIpUhAzv5k=", "dev": true } } }, "grunt-sed": { "version": "0.1.1", - "from": "grunt-sed@>=0.1.1 <0.2.0", "resolved": "https://registry.npmjs.org/grunt-sed/-/grunt-sed-0.1.1.tgz", - "dev": true + "integrity": "sha1-JhPUhpCTGbP49L112vtGpkLsP4I=", + "dev": true, + "requires": { + "replace": "0.2.10" + } }, "grunt-shell": { "version": "2.1.0", - "from": "grunt-shell@>=2.1.0 <3.0.0", "resolved": "https://registry.npmjs.org/grunt-shell/-/grunt-shell-2.1.0.tgz", - "dev": true + "integrity": "sha1-Q595FZ7RHmSmUaacyKPQK+v17MI=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "npm-run-path": "2.0.2" + } }, "hang": { "version": "1.0.0", - "from": "hang@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/hang/-/hang-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/hang/-/hang-1.0.0.tgz", + "integrity": "sha1-ZwUIeYRENeAq4ECcT0VTxkOOHXE=" }, "har-schema": { "version": "1.0.5", - "from": "har-schema@>=1.0.5 <2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz" + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=" }, "har-validator": { "version": "4.2.1", - "from": "har-validator@>=4.2.1 <4.3.0", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz" + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "requires": { + "ajv": "4.11.8", + "har-schema": "1.0.5" + } }, "has": { "version": "1.0.1", - "from": "has@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", + "requires": { + "function-bind": "1.1.1" + } }, "has-ansi": { "version": "2.0.0", - "from": "has-ansi@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "dev": true + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } }, "has-color": { "version": "0.1.7", - "from": "has-color@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz", + "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=", "dev": true }, "has-flag": { "version": "1.0.0", - "from": "has-flag@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", "dev": true }, "has-unicode": { "version": "2.0.1", - "from": "has-unicode@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" }, "hawk": { "version": "3.1.3", - "from": "hawk@>=3.1.3 <3.2.0", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz" + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } }, "heapdump": { "version": "0.3.9", - "from": "heapdump@>=0.3.7 <0.4.0", - "resolved": "https://registry.npmjs.org/heapdump/-/heapdump-0.3.9.tgz" + "resolved": "https://registry.npmjs.org/heapdump/-/heapdump-0.3.9.tgz", + "integrity": "sha1-A8dOsN9dZ74Jgug0KbqcnSs7f3g=" }, "helmet": { "version": "3.8.1", - "from": "helmet@>=3.8.1 <4.0.0", - "resolved": "https://registry.npmjs.org/helmet/-/helmet-3.8.1.tgz" + "resolved": "https://registry.npmjs.org/helmet/-/helmet-3.8.1.tgz", + "integrity": "sha1-vvK2j/uqGXWehYwZzKfbITu1iy0=", + "requires": { + "connect": "3.6.2", + "dns-prefetch-control": "0.1.0", + "dont-sniff-mimetype": "1.0.0", + "expect-ct": "0.1.0", + "frameguard": "3.0.0", + "helmet-csp": "2.5.1", + "hide-powered-by": "1.0.0", + "hpkp": "2.0.0", + "hsts": "2.1.0", + "ienoopen": "1.0.0", + "nocache": "2.0.0", + "referrer-policy": "1.1.0", + "x-xss-protection": "1.0.0" + } }, "helmet-csp": { "version": "2.5.1", - "from": "helmet-csp@2.5.1", - "resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.5.1.tgz" + "resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.5.1.tgz", + "integrity": "sha1-Xz3uyPki+n4HTbw5h8FopQVzw20=", + "requires": { + "camelize": "1.0.0", + "content-security-policy-builder": "1.1.0", + "dasherize": "2.0.0", + "lodash.reduce": "4.6.0", + "platform": "1.3.4" + } }, "hide-powered-by": { "version": "1.0.0", - "from": "hide-powered-by@1.0.0", - "resolved": "https://registry.npmjs.org/hide-powered-by/-/hide-powered-by-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/hide-powered-by/-/hide-powered-by-1.0.0.tgz", + "integrity": "sha1-SoWtZYgfYoV/xwr3F0oRhNzM4ys=" }, "hoek": { "version": "2.16.3", - "from": "hoek@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" }, "hooker": { "version": "0.2.3", - "from": "hooker@>=0.2.3 <0.3.0", - "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", + "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=" }, "hooks-fixed": { "version": "2.0.0", - "from": "hooks-fixed@2.0.0", - "resolved": "https://registry.npmjs.org/hooks-fixed/-/hooks-fixed-2.0.0.tgz" + "resolved": "https://registry.npmjs.org/hooks-fixed/-/hooks-fixed-2.0.0.tgz", + "integrity": "sha1-oB2JTVKsf2WZu7H2PfycQR33DLo=" }, "hpkp": { "version": "2.0.0", - "from": "hpkp@2.0.0", - "resolved": "https://registry.npmjs.org/hpkp/-/hpkp-2.0.0.tgz" + "resolved": "https://registry.npmjs.org/hpkp/-/hpkp-2.0.0.tgz", + "integrity": "sha1-EOFCJk52IVpdMMROxD3mTe5tFnI=" }, "hsts": { "version": "2.1.0", - "from": "hsts@2.1.0", - "resolved": "https://registry.npmjs.org/hsts/-/hsts-2.1.0.tgz" + "resolved": "https://registry.npmjs.org/hsts/-/hsts-2.1.0.tgz", + "integrity": "sha1-y9bJGKI4X+4d1WgL+ys6GUwBIcw=" }, "htmlparser2": { "version": "3.9.2", - "from": "htmlparser2@>=3.9.0 <4.0.0", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", + "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=", "dev": true, + "requires": { + "domelementtype": "1.3.0", + "domhandler": "2.4.1", + "domutils": "1.6.2", + "entities": "1.1.1", + "inherits": "2.0.3", + "readable-stream": "2.3.3" + }, "dependencies": { "isarray": { "version": "1.0.0", - "from": "isarray@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, "readable-stream": { "version": "2.3.3", - "from": "readable-stream@>=2.0.2 <3.0.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "dev": true + "integrity": "sha1-No8lEtefnUb9/HE0mueHi7weuVw=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } }, "string_decoder": { "version": "1.0.3", - "from": "string_decoder@>=1.0.3 <1.1.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "dev": true + "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } } } }, "http-errors": { "version": "1.6.2", - "from": "http-errors@>=1.6.2 <1.7.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz" + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "requires": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": "1.3.1" + } }, "http-parser-js": { "version": "0.4.8", - "from": "http-parser-js@>=0.4.0", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.8.tgz", + "integrity": "sha1-dj91xLdxoLtEZTsHBwv/bKe8VWE=", "dev": true }, "http-proxy": { "version": "1.16.2", - "from": "http-proxy@>=1.8.1 <2.0.0", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz" + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", + "integrity": "sha1-Bt/ykpUr9k2+hHH6nfcwZtTzd0I=", + "requires": { + "eventemitter3": "1.2.0", + "requires-port": "1.0.0" + } }, "http-signature": { "version": "1.1.1", - "from": "http-signature@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz" + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" + } }, "i": { "version": "0.3.5", - "from": "i@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/i/-/i-0.3.5.tgz", + "integrity": "sha1-HSuFQVjsgWkRPGy39raAHpniEdU=", "dev": true }, "i18next": { "version": "1.7.10", - "from": "i18next@>=1.7.1 <1.8.0", "resolved": "https://registry.npmjs.org/i18next/-/i18next-1.7.10.tgz", - "dev": true + "integrity": "sha1-K8HOLMG8LpP6c7WINZ2gIZczaHU=", + "dev": true, + "requires": { + "cookies": "0.7.1", + "json5": "0.2.0" + } }, "iconv-lite": { "version": "0.2.11", - "from": "iconv-lite@>=0.2.11 <0.3.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz" + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz", + "integrity": "sha1-HOYKOleGSiktEyH/RgnKS7llrcg=" }, "ieee754": { "version": "1.1.8", - "from": "ieee754@>=1.1.4 <2.0.0", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz" + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" }, "ienoopen": { "version": "1.0.0", - "from": "ienoopen@1.0.0", - "resolved": "https://registry.npmjs.org/ienoopen/-/ienoopen-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/ienoopen/-/ienoopen-1.0.0.tgz", + "integrity": "sha1-NGpCj0dKrI9QzzeE6i0PFvYr2ms=" }, "inflection": { "version": "1.12.0", - "from": "inflection@>=1.6.0 <2.0.0", - "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz" + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz", + "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY=" }, "inflight": { "version": "1.0.6", - "from": "inflight@>=1.0.4 <2.0.0", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "1.3.3", + "wrappy": "1.0.2" + } }, "inherits": { "version": "2.0.3", - "from": "inherits@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ini": { "version": "1.3.4", - "from": "ini@>=1.3.0 <1.4.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz" + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", + "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=" }, "ioredis": { "version": "2.5.0", - "from": "ioredis@>=2.4.0 <3.0.0", "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-2.5.0.tgz", + "integrity": "sha1-+2/fChp+CXRhTGe25eETCKjPlbk=", + "requires": { + "bluebird": "3.5.0", + "cluster-key-slot": "1.0.8", + "debug": "2.6.8", + "double-ended-queue": "2.1.0-0", + "flexbuffer": "0.0.6", + "lodash": "4.17.4", + "redis-commands": "1.3.1", + "redis-parser": "1.3.0" + }, "dependencies": { "debug": { "version": "2.6.8", - "from": "debug@>=2.2.0 <3.0.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz" + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "requires": { + "ms": "2.0.0" + } }, "redis-parser": { "version": "1.3.0", - "from": "redis-parser@>=1.3.0 <2.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-1.3.0.tgz" + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-1.3.0.tgz", + "integrity": "sha1-gG6+e7+3005NfB6e8oLvz60EEmo=" } } }, "ipaddr.js": { "version": "1.0.5", - "from": "ipaddr.js@1.0.5", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.0.5.tgz" + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.0.5.tgz", + "integrity": "sha1-X6eM8wG4JceKvDBC2BJyMEnqI8c=" }, "is-buffer": { "version": "1.1.5", - "from": "is-buffer@>=1.1.5 <2.0.0", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz" + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", + "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=" }, "is-callable": { "version": "1.1.3", - "from": "is-callable@>=1.1.3 <2.0.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz" + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", + "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=" }, "is-date-object": { "version": "1.0.1", - "from": "is-date-object@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" }, "is-expression": { "version": "2.1.0", - "from": "is-expression@>=2.0.1 <3.0.0", - "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-2.1.0.tgz" + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-2.1.0.tgz", + "integrity": "sha1-kb6dR968/vB3l36XIr5tz7RGXvA=", + "requires": { + "acorn": "3.3.0", + "object-assign": "4.1.1" + } }, "is-fullwidth-code-point": { "version": "1.0.0", - "from": "is-fullwidth-code-point@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "1.0.1" + } }, "is-function": { "version": "1.0.1", - "from": "is-function@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz", + "integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU=" }, "is-promise": { "version": "1.0.1", - "from": "is-promise@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-1.0.1.tgz", + "integrity": "sha1-MVc3YcBX4zwukaq56W2gjO++duU=" }, "is-regex": { "version": "1.0.4", - "from": "is-regex@>=1.0.3 <2.0.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz" + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "requires": { + "has": "1.0.1" + } }, "is-symbol": { "version": "1.0.1", - "from": "is-symbol@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", + "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=" }, "is-typedarray": { "version": "1.0.0", - "from": "is-typedarray@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, "isarray": { "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" }, "isbinaryfile": { "version": "0.1.9", - "from": "isbinaryfile@>=0.1.9 <0.2.0", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-0.1.9.tgz" + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-0.1.9.tgz", + "integrity": "sha1-Fe7ONcSrcI2JJNqZ+4dPK1zAtsQ=" }, "isstream": { "version": "0.1.2", - "from": "isstream@>=0.1.2 <0.2.0", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, "jade": { "version": "1.3.1", - "from": "jade@>=1.3.1 <1.4.0", "resolved": "https://registry.npmjs.org/jade/-/jade-1.3.1.tgz", + "integrity": "sha1-dIPYSLhxTcUKQNqYsECXkLN0IWs=", + "requires": { + "character-parser": "1.2.0", + "commander": "2.1.0", + "constantinople": "2.0.1", + "mkdirp": "0.3.5", + "monocle": "1.1.51", + "transformers": "2.1.0", + "with": "3.0.1" + }, "dependencies": { "mkdirp": { "version": "0.3.5", - "from": "mkdirp@>=0.3.5 <0.4.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=" } } }, "jmespath": { "version": "0.15.0", - "from": "jmespath@0.15.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz" + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", + "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" }, "js-base64": { "version": "2.3.2", - "from": "js-base64@>=2.1.9 <3.0.0", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.3.2.tgz", + "integrity": "sha1-p5qSNmY3K1gPjif1GEXG9+j7+68=", "dev": true }, "js-stringify": { "version": "1.0.2", - "from": "js-stringify@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz" + "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", + "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=" }, "js-yaml": { "version": "2.0.5", - "from": "js-yaml@>=2.0.5 <2.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz" + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz", + "integrity": "sha1-olrmUJmZ6X3yeMZxnaEb0Gh3Q6g=", + "requires": { + "argparse": "0.1.16", + "esprima": "1.0.4" + } }, "jsbn": { "version": "0.1.1", - "from": "jsbn@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "optional": true }, "json-schema": { "version": "0.2.3", - "from": "json-schema@0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz" + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" }, "json-stable-stringify": { "version": "1.0.1", - "from": "json-stable-stringify@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "requires": { + "jsonify": "0.0.0" + } }, "json-stringify-safe": { "version": "5.0.1", - "from": "json-stringify-safe@>=5.0.1 <5.1.0", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, "json5": { "version": "0.2.0", - "from": "json5@>=0.2.0 <0.3.0", "resolved": "https://registry.npmjs.org/json5/-/json5-0.2.0.tgz", + "integrity": "sha1-ttcDXHDEVw+IPH7cdZ3jrgPbM0M=", "dev": true }, "jsonfile": { - "version": "1.1.1", - "from": "jsonfile@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-1.1.1.tgz" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "4.1.11" + } }, "jsonify": { "version": "0.0.0", - "from": "jsonify@>=0.0.0 <0.1.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" }, "jsonwebtoken": { "version": "8.0.1", - "from": "jsonwebtoken@latest", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.0.1.tgz" + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.0.1.tgz", + "integrity": "sha1-UNrvjQqMfeLNBrwQE7dbBMzz8M8=", + "requires": { + "jws": "3.1.4", + "lodash.includes": "4.3.0", + "lodash.isboolean": "3.0.3", + "lodash.isinteger": "4.0.4", + "lodash.isnumber": "3.0.3", + "lodash.isplainobject": "4.0.6", + "lodash.isstring": "4.0.1", + "lodash.once": "4.1.1", + "ms": "2.0.0", + "xtend": "4.0.1" + } }, "jsprim": { "version": "1.4.1", - "from": "jsprim@>=1.2.2 <2.0.0", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + }, "dependencies": { "assert-plus": { "version": "1.0.0", - "from": "assert-plus@1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" } } }, "jstransformer": { "version": "1.0.0", - "from": "jstransformer@1.0.0", "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", + "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=", + "requires": { + "is-promise": "2.1.0", + "promise": "7.3.1" + }, "dependencies": { "is-promise": { "version": "2.1.0", - "from": "is-promise@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz" + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" }, "promise": { "version": "7.3.1", - "from": "promise@>=7.0.1 <8.0.0", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz" + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha1-BktyYCsY+Q8pGSuLG8QY/9Hr078=", + "requires": { + "asap": "2.0.6" + } } } }, "just-extend": { "version": "1.1.22", - "from": "just-extend@>=1.1.22 <2.0.0", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-1.1.22.tgz" + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-1.1.22.tgz", + "integrity": "sha1-MzCvdWyralQnAMZLLk5KoGLVL/8=" }, "jwa": { "version": "1.1.5", - "from": "jwa@>=1.1.4 <2.0.0", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.5.tgz" + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.5.tgz", + "integrity": "sha1-oFUs4CIHQs1S4VN3SjKQXDDnVuU=", + "requires": { + "base64url": "2.0.0", + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.9", + "safe-buffer": "5.1.1" + } }, "jws": { "version": "3.1.4", - "from": "jws@>=3.1.4 <4.0.0", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.4.tgz" + "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.4.tgz", + "integrity": "sha1-+ei5M46KhHJ31kRLFGT2GIDgUKI=", + "requires": { + "base64url": "2.0.0", + "jwa": "1.1.5", + "safe-buffer": "5.1.1" + } }, "kareem": { "version": "1.5.0", - "from": "kareem@1.5.0", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-1.5.0.tgz" + "resolved": "https://registry.npmjs.org/kareem/-/kareem-1.5.0.tgz", + "integrity": "sha1-4+QQHZ3P3imXadr0tNtk2JXRdEg=" }, "keygrip": { "version": "1.0.2", - "from": "keygrip@>=1.0.2 <1.1.0", "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.0.2.tgz", + "integrity": "sha1-rTKXxVcGneqLz+ek+kkbdcXd65E=", "dev": true }, "kind-of": { "version": "3.2.2", - "from": "kind-of@>=3.0.2 <4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz" + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.5" + } }, "lazy": { "version": "1.0.11", - "from": "lazy@>=1.0.11 <1.1.0", "resolved": "https://registry.npmjs.org/lazy/-/lazy-1.0.11.tgz", + "integrity": "sha1-2qBoIGKCVCwIgojpdcKXwa53tpA=", "dev": true }, "lazy-cache": { "version": "1.0.4", - "from": "lazy-cache@>=1.0.3 <2.0.0", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz" + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" }, "lazystream": { "version": "0.1.0", - "from": "lazystream@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-0.1.0.tgz" + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-0.1.0.tgz", + "integrity": "sha1-GyXWPHcqTCDwpe0KnXf0hLbhaSA=", + "requires": { + "readable-stream": "1.0.34" + } }, "ldap-filter": { "version": "0.2.2", - "from": "ldap-filter@0.2.2", "resolved": "https://registry.npmjs.org/ldap-filter/-/ldap-filter-0.2.2.tgz", + "integrity": "sha1-8rhCvguG2jNSeYUFsx68rlkNd9A=", + "requires": { + "assert-plus": "0.1.5" + }, "dependencies": { "assert-plus": { "version": "0.1.5", - "from": "assert-plus@0.1.5", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz" + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", + "integrity": "sha1-7nQAlBMALYTOxyGcasgRgS5yMWA=" } } }, "ldapauth-fork": { "version": "2.5.5", - "from": "ldapauth-fork@>=2.5.0 <2.6.0", "resolved": "https://registry.npmjs.org/ldapauth-fork/-/ldapauth-fork-2.5.5.tgz", + "integrity": "sha1-qsK0cCLloj1nYLHVGKlpaVju5ns=", + "requires": { + "bcryptjs": "2.3.0", + "ldapjs": "1.0.1", + "lru-cache": "3.2.0" + }, "dependencies": { "assert-plus": { "version": "1.0.0", - "from": "assert-plus@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, "bunyan": { "version": "1.8.12", - "from": "bunyan@>=1.8.3 <2.0.0", "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz", + "integrity": "sha1-8VDw9nSKvdcq6uhPBEA74u8RN5c=", + "requires": { + "dtrace-provider": "0.8.5", + "moment": "2.18.1", + "mv": "2.1.1", + "safe-json-stringify": "1.0.4" + }, "dependencies": { "dtrace-provider": { "version": "0.8.5", - "from": "dtrace-provider@>=0.8.0 <0.9.0", "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.5.tgz", - "optional": true + "integrity": "sha1-mOu6Ihr6xG4cOf02hY2Pk2dSS5I=", + "optional": true, + "requires": { + "nan": "2.3.5" + } } } }, "dtrace-provider": { "version": "0.7.1", - "from": "dtrace-provider@>=0.7.0 <0.8.0", "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.7.1.tgz", - "optional": true + "integrity": "sha1-wGswjy8Q1dWDiuycVx5dWI3HHQQ=", + "optional": true, + "requires": { + "nan": "2.3.5" + } }, "extsprintf": { "version": "1.2.0", - "from": "extsprintf@1.2.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.2.0.tgz" + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.2.0.tgz", + "integrity": "sha1-WtlGwi9bMrp/jNdCZxHG6KP8JSk=" }, "glob": { "version": "6.0.4", - "from": "glob@>=6.0.1 <7.0.0", "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "optional": true + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "optional": true, + "requires": { + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } }, "ldapjs": { "version": "1.0.1", - "from": "ldapjs@>=1.0.1 <1.1.0", - "resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-1.0.1.tgz", + "integrity": "sha1-NSuBKudLCo6WVJpLiWBg7uG5pUY=", + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "backoff": "2.5.0", + "bunyan": "1.8.12", + "dashdash": "1.14.1", + "dtrace-provider": "0.7.1", + "ldap-filter": "0.2.2", + "once": "1.4.0", + "vasync": "1.6.4", + "verror": "1.10.0" + } }, "lru-cache": { "version": "3.2.0", - "from": "lru-cache@3.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-3.2.0.tgz" + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-3.2.0.tgz", + "integrity": "sha1-cXibO39Tmb7IVl3aOKow0qCX7+4=", + "requires": { + "pseudomap": "1.0.2" + } }, "minimatch": { "version": "3.0.4", - "from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "optional": true + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "optional": true, + "requires": { + "brace-expansion": "1.1.8" + } }, "mv": { "version": "2.1.1", - "from": "mv@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", - "optional": true + "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", + "optional": true, + "requires": { + "mkdirp": "0.5.1", + "ncp": "2.0.0", + "rimraf": "2.4.5" + } }, "once": { "version": "1.4.0", - "from": "once@>=1.4.0 <2.0.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1.0.2" + } }, "rimraf": { "version": "2.4.5", - "from": "rimraf@>=2.4.0 <2.5.0", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", - "optional": true + "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "optional": true, + "requires": { + "glob": "6.0.4" + } }, "vasync": { "version": "1.6.4", - "from": "vasync@>=1.6.4 <2.0.0", "resolved": "https://registry.npmjs.org/vasync/-/vasync-1.6.4.tgz", + "integrity": "sha1-3+k2Fq0OeugBszKp2Iv8XNyOHR8=", + "requires": { + "verror": "1.6.0" + }, "dependencies": { "verror": { "version": "1.6.0", - "from": "verror@1.6.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.6.0.tgz" + "resolved": "https://registry.npmjs.org/verror/-/verror-1.6.0.tgz", + "integrity": "sha1-fROyex+swuLakEBetepuW90lLqU=", + "requires": { + "extsprintf": "1.2.0" + } } } } @@ -2307,57 +3246,78 @@ }, "ldapjs": { "version": "0.7.1", - "from": "ldapjs@>=0.7.1 <0.8.0", "resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-0.7.1.tgz", + "integrity": "sha1-aEeYpodkC6sa+9gCz1MvMEkt+1Y=", + "requires": { + "asn1": "0.2.1", + "assert-plus": "0.1.5", + "bunyan": "0.22.1", + "dtrace-provider": "0.2.8", + "nopt": "2.1.1", + "pooling": "0.4.6" + }, "dependencies": { "asn1": { "version": "0.2.1", - "from": "asn1@0.2.1", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.1.tgz" + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.1.tgz", + "integrity": "sha1-7Mc/ddMeo8btnUdCjbNf7Meyxtw=" }, "assert-plus": { "version": "0.1.5", - "from": "assert-plus@0.1.5", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz" + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", + "integrity": "sha1-7nQAlBMALYTOxyGcasgRgS5yMWA=" }, "nopt": { "version": "2.1.1", - "from": "nopt@2.1.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.1.1.tgz" + "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.1.1.tgz", + "integrity": "sha1-ket8SwF+fACtytH9bWOUTQ/bdcE=", + "requires": { + "abbrev": "1.1.0" + } } } }, "less": { "version": "1.6.3", - "from": "less@>=1.6.0 <1.7.0", "resolved": "https://registry.npmjs.org/less/-/less-1.6.3.tgz", + "integrity": "sha1-cc6J7DC3dLNWfyVMZ5WPLywZO94=", "dev": true, + "requires": { + "clean-css": "2.0.8", + "mime": "1.2.11", + "mkdirp": "0.3.5", + "request": "2.81.0", + "source-map": "0.1.34" + }, "dependencies": { "clean-css": { "version": "2.0.8", - "from": "clean-css@>=2.0.0 <2.1.0", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-2.0.8.tgz", + "integrity": "sha1-6TfN/cxXgaAIF67EB56Fs+wVeiA=", "dev": true, - "optional": true + "optional": true, + "requires": { + "commander": "2.0.0" + } }, "commander": { "version": "2.0.0", - "from": "commander@>=2.0.0 <2.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz", + "integrity": "sha1-0bhvkB+LZL2UG96tr5JFMDk76Sg=", "dev": true, "optional": true }, "mime": { "version": "1.2.11", - "from": "mime@>=1.2.0 <1.3.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", + "integrity": "sha1-WCA+7Ybjpe8XrtK32evUfwpg3RA=", "dev": true, "optional": true }, "mkdirp": { "version": "0.3.5", - "from": "mkdirp@~0.3.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=", "dev": true, "optional": true } @@ -2365,1063 +3325,1529 @@ }, "libbase64": { "version": "0.1.0", - "from": "libbase64@0.1.0", - "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-0.1.0.tgz" + "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-0.1.0.tgz", + "integrity": "sha1-YjUag5VjrF/1vSbxL2Dpgwu3UeY=" }, "libmime": { "version": "2.0.0", - "from": "libmime@2.0.0", "resolved": "https://registry.npmjs.org/libmime/-/libmime-2.0.0.tgz", + "integrity": "sha1-oXpo3E+igwu2SZ6x2jUJlnbIxyQ=", + "requires": { + "iconv-lite": "0.4.13", + "libbase64": "0.1.0", + "libqp": "1.1.0" + }, "dependencies": { "iconv-lite": { "version": "0.4.13", - "from": "iconv-lite@0.4.13", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz" + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", + "integrity": "sha1-H4irpKsLFQjoMSrMOTRfNumS4vI=" } } }, "libqp": { "version": "1.1.0", - "from": "libqp@1.1.0", - "resolved": "https://registry.npmjs.org/libqp/-/libqp-1.1.0.tgz" + "resolved": "https://registry.npmjs.org/libqp/-/libqp-1.1.0.tgz", + "integrity": "sha1-9ebgatdLeU+1tbZpiL9yjvHe2+g=" }, "livereload-js": { "version": "2.2.2", - "from": "livereload-js@>=2.2.0 <3.0.0", "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.2.2.tgz", + "integrity": "sha1-bIclfmSKtHW8JOoldFftzB+NC8I=", "dev": true }, "loads": { "version": "0.0.4", - "from": "loads@>=0.0.0 <0.1.0", - "resolved": "https://registry.npmjs.org/loads/-/loads-0.0.4.tgz" + "resolved": "https://registry.npmjs.org/loads/-/loads-0.0.4.tgz", + "integrity": "sha1-l/MBY5fnDd/0gLgexjjO6iKgqZw=", + "requires": { + "failure": "1.1.1", + "one-time": "0.0.4", + "xhr-response": "1.0.1", + "xhr-status": "1.0.0" + } }, "lodash": { "version": "4.17.4", - "from": "lodash@>=4.13.1 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" }, "lodash.includes": { "version": "4.3.0", - "from": "lodash.includes@>=4.3.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz" + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" }, "lodash.isboolean": { "version": "3.0.3", - "from": "lodash.isboolean@>=3.0.3 <4.0.0", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz" + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" }, "lodash.isinteger": { "version": "4.0.4", - "from": "lodash.isinteger@>=4.0.4 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz" + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" }, "lodash.isnumber": { "version": "3.0.3", - "from": "lodash.isnumber@>=3.0.3 <4.0.0", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz" + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" }, "lodash.isplainobject": { "version": "4.0.6", - "from": "lodash.isplainobject@>=4.0.6 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz" + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" }, "lodash.isstring": { "version": "4.0.1", - "from": "lodash.isstring@>=4.0.1 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz" + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" }, "lodash.once": { "version": "4.1.1", - "from": "lodash.once@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz" + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" }, "lodash.reduce": { "version": "4.6.0", - "from": "lodash.reduce@4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz" + "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", + "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=" }, "logger-sharelatex": { - "version": "1.5.6", - "from": "git+https://github.com/sharelatex/logger-sharelatex.git#master", - "resolved": "git+https://github.com/sharelatex/logger-sharelatex.git#b2956ec56b582b9f4fc8fdda8dc00c06e77c5537", + "version": "git+https://github.com/sharelatex/logger-sharelatex.git#b2956ec56b582b9f4fc8fdda8dc00c06e77c5537", + "requires": { + "bunyan": "1.5.1", + "chai": "4.1.2", + "coffee-script": "1.12.4", + "grunt": "0.4.5", + "grunt-bunyan": "0.5.0", + "grunt-contrib-clean": "0.6.0", + "grunt-contrib-coffee": "0.11.1", + "grunt-execute": "0.2.2", + "grunt-mocha-test": "0.11.0", + "raven": "1.2.1", + "sandboxed-module": "2.0.3", + "sinon": "3.2.1", + "timekeeper": "1.0.0" + }, "dependencies": { "ansi-regex": { "version": "0.2.1", - "from": "ansi-regex@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", + "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=" }, "ansi-styles": { "version": "1.1.0", - "from": "ansi-styles@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz" + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz", + "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=" }, "bunyan": { "version": "1.5.1", - "from": "bunyan@1.5.1", - "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.5.1.tgz" + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.5.1.tgz", + "integrity": "sha1-X259RMQ7lS9WsPQTCeOrEjkbTi0=", + "requires": { + "dtrace-provider": "0.6.0", + "mv": "2.1.1", + "safe-json-stringify": "1.0.4" + } }, "chai": { "version": "4.1.2", - "from": "chai@latest", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz" + "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", + "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", + "requires": { + "assertion-error": "1.0.2", + "check-error": "1.0.2", + "deep-eql": "3.0.1", + "get-func-name": "2.0.0", + "pathval": "1.1.0", + "type-detect": "4.0.3" + } }, "chalk": { "version": "0.5.1", - "from": "chalk@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz" + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", + "requires": { + "ansi-styles": "1.1.0", + "escape-string-regexp": "1.0.5", + "has-ansi": "0.1.0", + "strip-ansi": "0.3.0", + "supports-color": "0.2.0" + } }, "commander": { "version": "2.0.0", - "from": "commander@2.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz" + "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz", + "integrity": "sha1-0bhvkB+LZL2UG96tr5JFMDk76Sg=" }, "deep-eql": { "version": "3.0.1", - "from": "deep-eql@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz" + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha1-38lARACtHI/gI+faHfHBR8S0RN8=", + "requires": { + "type-detect": "4.0.3" + } }, "dtrace-provider": { "version": "0.6.0", - "from": "dtrace-provider@>=0.6.0 <0.7.0", "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz", - "optional": true + "integrity": "sha1-CweNVReTfYcxAUUtkUZzdVe3XlE=", + "optional": true, + "requires": { + "nan": "2.3.5" + } }, "formatio": { "version": "1.2.0", - "from": "formatio@1.2.0", - "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.2.0.tgz" + "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.2.0.tgz", + "integrity": "sha1-87IWfZBoxGmKjVH092CjmlTYGOs=", + "requires": { + "samsam": "1.2.1" + } }, "glob": { "version": "6.0.4", - "from": "glob@>=6.0.1 <7.0.0", "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "optional": true + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "requires": { + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } }, "graceful-fs": { "version": "2.0.3", - "from": "graceful-fs@>=2.0.0 <2.1.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz" + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz", + "integrity": "sha1-fNLNsiiko/Nule+mzBQt59GhNtA=" }, "grunt-contrib-clean": { "version": "0.6.0", - "from": "grunt-contrib-clean@>=0.6.0 <0.7.0", "resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-0.6.0.tgz", + "integrity": "sha1-9TLbpLghJnTHwBPhRr2mY4uQSPY=", + "requires": { + "rimraf": "2.2.8" + }, "dependencies": { "rimraf": { "version": "2.2.8", - "from": "rimraf@>=2.2.1 <2.3.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", + "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=" } } }, "grunt-contrib-coffee": { "version": "0.11.1", - "from": "grunt-contrib-coffee@>=0.11.0 <0.12.0", "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.11.1.tgz", + "integrity": "sha1-+v48nuikQryNF9WlwZ/I5i2fP0U=", + "requires": { + "chalk": "0.5.1", + "coffee-script": "1.7.1", + "lodash": "2.4.2" + }, "dependencies": { "coffee-script": { "version": "1.7.1", - "from": "coffee-script@>=1.7.0 <1.8.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz" + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz", + "integrity": "sha1-YplqhheAx15tUGnROCJyO3NAS/w=", + "requires": { + "mkdirp": "0.3.5" + } }, "mkdirp": { "version": "0.3.5", - "from": "mkdirp@>=0.3.5 <0.4.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=" } } }, "grunt-mocha-test": { "version": "0.11.0", - "from": "grunt-mocha-test@>=0.11.0 <0.12.0", - "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.11.0.tgz" - }, - "has-ansi": { - "version": "0.1.0", - "from": "has-ansi@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz" - }, - "jade": { - "version": "0.26.3", - "from": "jade@0.26.3", - "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.11.0.tgz", + "integrity": "sha1-deQboQdZDkrL0phgklwBLkQ8oyI=", + "requires": { + "fs-extra": "0.9.1", + "hooker": "0.2.3", + "mocha": "1.20.1" + }, "dependencies": { - "commander": { - "version": "0.6.1", - "from": "commander@0.6.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz" + "fs-extra": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.9.1.tgz", + "integrity": "sha1-h9v8ATg6jdzn2dVJbzYIVkiJ8VY=", + "requires": { + "jsonfile": "1.1.1", + "mkdirp": "0.5.1", + "ncp": "0.5.1", + "rimraf": "2.4.5" + } }, - "mkdirp": { - "version": "0.3.0", - "from": "mkdirp@0.3.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz" + "ncp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.5.1.tgz", + "integrity": "sha1-dDmFMW49tFkoG1hxaehFc1oFQ58=" } } }, + "has-ansi": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", + "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=", + "requires": { + "ansi-regex": "0.2.1" + } + }, + "jade": { + "version": "0.26.3", + "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", + "requires": { + "commander": "0.6.1", + "mkdirp": "0.3.0" + }, + "dependencies": { + "commander": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", + "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=" + }, + "mkdirp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=" + } + } + }, + "jsonfile": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-1.1.1.tgz", + "integrity": "sha1-2k/WrXfxolUgPqY8e8Mtwx72RDM=" + }, "lodash": { "version": "2.4.2", - "from": "lodash@>=2.4.1 <2.5.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=" }, "lolex": { "version": "2.1.2", - "from": "lolex@>=2.1.2 <3.0.0", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.1.2.tgz" + "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.1.2.tgz", + "integrity": "sha1-JpS5U8nqTQE+W4v7qJHJkQJbJik=" }, "minimatch": { "version": "3.0.4", - "from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "optional": true + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "requires": { + "brace-expansion": "1.1.8" + } }, "mocha": { "version": "1.20.1", - "from": "mocha@>=1.20.0 <1.21.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.20.1.tgz", + "integrity": "sha1-80ODLZ/gx9l8ZPxwRI9RNt+f7Vs=", + "requires": { + "commander": "2.0.0", + "debug": "1.0.5", + "diff": "1.0.7", + "glob": "3.2.3", + "growl": "1.7.0", + "jade": "0.26.3", + "mkdirp": "0.3.5" + }, "dependencies": { "glob": { "version": "3.2.3", - "from": "glob@3.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz" + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", + "integrity": "sha1-4xPusknHr/qlxHUoaw4RW1mDlGc=", + "requires": { + "graceful-fs": "2.0.3", + "inherits": "2.0.3", + "minimatch": "0.2.14" + } }, "minimatch": { "version": "0.2.14", - "from": "minimatch@>=0.2.11 <0.3.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz" + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } }, "mkdirp": { "version": "0.3.5", - "from": "mkdirp@0.3.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=" } } }, "mv": { "version": "2.1.1", - "from": "mv@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", - "optional": true + "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", + "optional": true, + "requires": { + "mkdirp": "0.5.1", + "ncp": "2.0.0", + "rimraf": "2.4.5" + } }, "path-to-regexp": { "version": "1.7.0", - "from": "path-to-regexp@>=1.7.0 <2.0.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz" + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", + "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "requires": { + "isarray": "0.0.1" + } }, "rimraf": { "version": "2.4.5", - "from": "rimraf@>=2.4.0 <2.5.0", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", - "optional": true + "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "requires": { + "glob": "6.0.4" + } }, "samsam": { "version": "1.2.1", - "from": "samsam@>=1.1.3 <2.0.0", - "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.2.1.tgz" + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.2.1.tgz", + "integrity": "sha1-7dOQk6MYQ3DLhZJDsr3yVefY6mc=" }, "sandboxed-module": { "version": "2.0.3", - "from": "sandboxed-module@latest", - "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-2.0.3.tgz" + "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-2.0.3.tgz", + "integrity": "sha1-x+VFkzm7y6KMUwPusz9ug4e/upY=", + "requires": { + "require-like": "0.1.2", + "stack-trace": "0.0.9" + } }, "sinon": { "version": "3.2.1", - "from": "sinon@latest", "resolved": "https://registry.npmjs.org/sinon/-/sinon-3.2.1.tgz", + "integrity": "sha1-2K2r2QBzD9SXeIoCcEnGSwi+kcI=", + "requires": { + "diff": "3.3.1", + "formatio": "1.2.0", + "lolex": "2.1.2", + "native-promise-only": "0.8.1", + "nise": "1.0.1", + "path-to-regexp": "1.7.0", + "samsam": "1.2.1", + "text-encoding": "0.6.4", + "type-detect": "4.0.3" + }, "dependencies": { "diff": { "version": "3.3.1", - "from": "diff@>=3.1.0 <4.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz" + "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", + "integrity": "sha1-qoVnpu7QPFMfyJ0/cRzQ5SWd7HU=" } } }, "strip-ansi": { "version": "0.3.0", - "from": "strip-ansi@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz" + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", + "requires": { + "ansi-regex": "0.2.1" + } }, "supports-color": { "version": "0.2.0", - "from": "supports-color@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz" + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", + "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=" }, "timekeeper": { "version": "1.0.0", - "from": "timekeeper@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-1.0.0.tgz", + "integrity": "sha1-Lziu4elLEd1m2FgP8aqdzGoroNg=" }, "type-detect": { "version": "4.0.3", - "from": "type-detect@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.3.tgz" + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.3.tgz", + "integrity": "sha1-Dj8mcLRAmbC0bChNE2p+9Jx0wuo=" } } }, "lolex": { "version": "1.3.2", - "from": "lolex@1.3.2", "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.3.2.tgz", + "integrity": "sha1-fD2mL/yzDw9agKJWbKJORdigHzE=", "dev": true }, "longest": { "version": "1.0.1", - "from": "longest@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" }, "lpad": { "version": "0.1.0", - "from": "lpad@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/lpad/-/lpad-0.1.0.tgz", + "integrity": "sha1-5MYMKROTIcWXDeSTtJauDXdM0qc=", "dev": true }, "lru-cache": { "version": "2.7.3", - "from": "lru-cache@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" }, "lsmod": { "version": "1.0.0", - "from": "lsmod@1.0.0", - "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-1.0.0.tgz", + "integrity": "sha1-mgD3bco26yP6BTUK/htYXUKZ5ks=" }, "lynx": { "version": "0.1.1", - "from": "lynx@0.1.1", - "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz" + "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz", + "integrity": "sha1-Mxjc7xaQi4KG6Bisz9sxzXQkj50=", + "requires": { + "mersenne": "0.0.4", + "statsd-parser": "0.0.4" + } }, "mailcomposer": { "version": "3.3.2", - "from": "mailcomposer@3.3.2", - "resolved": "https://registry.npmjs.org/mailcomposer/-/mailcomposer-3.3.2.tgz" + "resolved": "https://registry.npmjs.org/mailcomposer/-/mailcomposer-3.3.2.tgz", + "integrity": "sha1-IFUjzIcu39UOyAp1F1SeCxuug00=", + "requires": { + "buildmail": "3.3.2", + "libmime": "2.0.0" + } }, "marked": { "version": "0.3.6", - "from": "marked@>=0.3.5 <0.4.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.6.tgz" + "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.6.tgz", + "integrity": "sha1-ssbGGPzOzk74bE/Gy4p8v1rtqNc=" }, "media-typer": { "version": "0.3.0", - "from": "media-typer@0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, "merge-descriptors": { "version": "1.0.0", - "from": "merge-descriptors@1.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.0.tgz", + "integrity": "sha1-IWnPdTjhsMyH+4jhUC2EdLv3mGQ=" }, "mersenne": { "version": "0.0.4", - "from": "mersenne@>=0.0.3 <0.1.0", - "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.4.tgz" + "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.4.tgz", + "integrity": "sha1-QB/ex+whzbngPNPTAhOY2iGycIU=" }, "method-override": { "version": "2.3.9", - "from": "method-override@>=2.3.3 <3.0.0", "resolved": "https://registry.npmjs.org/method-override/-/method-override-2.3.9.tgz", + "integrity": "sha1-vRUfLONM8Bp2ykAKuVwBKxAtj3E=", + "requires": { + "debug": "2.6.8", + "methods": "1.1.2", + "parseurl": "1.3.2", + "vary": "1.1.1" + }, "dependencies": { "debug": { "version": "2.6.8", - "from": "debug@2.6.8", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz" + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "requires": { + "ms": "2.0.0" + } }, "vary": { "version": "1.1.1", - "from": "vary@>=1.1.1 <1.2.0", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.1.tgz" + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.1.tgz", + "integrity": "sha1-Z1Neu2lMHVIldFeYRmUyP1h+jTc=" } } }, "methods": { "version": "1.1.2", - "from": "methods@>=1.1.1 <1.2.0", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, "metrics-sharelatex": { - "version": "1.7.1", - "from": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.7.1", - "resolved": "git+https://github.com/sharelatex/metrics-sharelatex.git#166961924c599b1f9468f2e17846fa2a9d12372d", + "version": "git+https://github.com/sharelatex/metrics-sharelatex.git#166961924c599b1f9468f2e17846fa2a9d12372d", + "requires": { + "coffee-script": "1.6.0", + "lynx": "0.1.1", + "underscore": "1.6.0" + }, "dependencies": { "coffee-script": { "version": "1.6.0", - "from": "coffee-script@1.6.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz" + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz", + "integrity": "sha1-gIs5bhEPU9AhoZpO8fZb4OjjX6M=" } } }, "microtime-nodejs": { "version": "1.0.0", - "from": "microtime-nodejs@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/microtime-nodejs/-/microtime-nodejs-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/microtime-nodejs/-/microtime-nodejs-1.0.0.tgz", + "integrity": "sha1-iFlASvLipGKhXJzWvyxORo2r2+g=" }, "mime": { "version": "1.3.4", - "from": "mime@1.3.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz" + "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", + "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=" }, "mime-db": { "version": "1.30.0", - "from": "mime-db@>=1.30.0 <1.31.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz" + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", + "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" }, "mime-types": { "version": "2.1.17", - "from": "mime-types@>=2.1.7 <2.2.0", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz" + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", + "requires": { + "mime-db": "1.30.0" + } }, "mimelib": { "version": "0.2.14", - "from": "mimelib@0.2.14", - "resolved": "https://registry.npmjs.org/mimelib/-/mimelib-0.2.14.tgz" + "resolved": "https://registry.npmjs.org/mimelib/-/mimelib-0.2.14.tgz", + "integrity": "sha1-KhqnJL0ZC4W9Um5jF6thBu39aDE=", + "requires": { + "addressparser": "0.2.1", + "encoding": "0.1.12" + } }, "minimatch": { "version": "0.2.14", - "from": "minimatch@>=0.2.12 <0.3.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz" + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } }, "minimist": { "version": "0.0.8", - "from": "minimist@0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "mkdirp": { "version": "0.5.1", - "from": "mkdirp@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz" + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } }, "mocha": { "version": "1.17.1", - "from": "mocha@1.17.1", "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.17.1.tgz", + "integrity": "sha1-f3Zx1oUm0HS3uuZgyQmfh+DqHMs=", + "requires": { + "commander": "2.0.0", + "debug": "1.0.5", + "diff": "1.0.7", + "glob": "3.2.3", + "growl": "1.7.0", + "jade": "0.26.3", + "mkdirp": "0.3.5" + }, "dependencies": { "commander": { "version": "2.0.0", - "from": "commander@2.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz" + "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz", + "integrity": "sha1-0bhvkB+LZL2UG96tr5JFMDk76Sg=" }, "glob": { "version": "3.2.3", - "from": "glob@3.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz" + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", + "integrity": "sha1-4xPusknHr/qlxHUoaw4RW1mDlGc=", + "requires": { + "graceful-fs": "2.0.3", + "inherits": "2.0.3", + "minimatch": "0.2.14" + } }, "graceful-fs": { "version": "2.0.3", - "from": "graceful-fs@>=2.0.0 <2.1.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz" + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz", + "integrity": "sha1-fNLNsiiko/Nule+mzBQt59GhNtA=" }, "jade": { "version": "0.26.3", - "from": "jade@0.26.3", "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", + "requires": { + "commander": "0.6.1", + "mkdirp": "0.3.0" + }, "dependencies": { "commander": { "version": "0.6.1", - "from": "commander@0.6.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz" + "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", + "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=" }, "mkdirp": { "version": "0.3.0", - "from": "mkdirp@0.3.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz" + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=" } } }, "mkdirp": { "version": "0.3.5", - "from": "mkdirp@0.3.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=" } } }, "moment": { "version": "2.18.1", - "from": "moment@>=2.10.6 <3.0.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz" + "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz", + "integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8=" }, "moment-timezone": { "version": "0.5.13", - "from": "moment-timezone@>=0.5.4 <0.6.0", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.13.tgz" + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.13.tgz", + "integrity": "sha1-mc5cfYJyYusPH3AgRBd/YHRde5A=", + "requires": { + "moment": "2.18.1" + } }, "mongodb": { "version": "2.2.31", - "from": "mongodb@>=2.0.45 <3.0.0", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.31.tgz", + "integrity": "sha1-GUBEXGYeGSF7s7+CRdmFSq71SNs=", + "requires": { + "es6-promise": "3.2.1", + "mongodb-core": "2.1.15", + "readable-stream": "2.2.7" + }, "dependencies": { "es6-promise": { "version": "3.2.1", - "from": "es6-promise@3.2.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz" + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", + "integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q=" }, "isarray": { "version": "1.0.0", - "from": "isarray@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "readable-stream": { "version": "2.2.7", - "from": "readable-stream@2.2.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz" + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz", + "integrity": "sha1-BwV6y+JGeyIELTb5jFrVBwVOlbE=", + "requires": { + "buffer-shims": "1.0.0", + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } }, "string_decoder": { "version": "1.0.3", - "from": "string_decoder@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz" + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", + "requires": { + "safe-buffer": "5.1.1" + } } } }, "mongodb-core": { "version": "2.1.15", - "from": "mongodb-core@2.1.15", - "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.15.tgz" + "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.15.tgz", + "integrity": "sha1-hB9TuH//9MdFgYnDXIroJ+EWl2Q=", + "requires": { + "bson": "1.0.4", + "require_optional": "1.0.1" + } }, "mongojs": { "version": "2.4.0", - "from": "mongojs@2.4.0", "resolved": "https://registry.npmjs.org/mongojs/-/mongojs-2.4.0.tgz", + "integrity": "sha1-8of7/UV/7fWItakBHmhRPZ3TK/s=", + "requires": { + "each-series": "1.0.0", + "mongodb": "2.2.31", + "once": "1.3.3", + "parse-mongo-url": "1.1.1", + "readable-stream": "2.3.3", + "thunky": "0.1.0", + "to-mongodb-core": "2.0.0", + "xtend": "4.0.1" + }, "dependencies": { "isarray": { "version": "1.0.0", - "from": "isarray@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "readable-stream": { "version": "2.3.3", - "from": "readable-stream@>=2.0.2 <3.0.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz" + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha1-No8lEtefnUb9/HE0mueHi7weuVw=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } }, "string_decoder": { "version": "1.0.3", - "from": "string_decoder@>=1.0.3 <1.1.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz" + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", + "requires": { + "safe-buffer": "5.1.1" + } } } }, "mongoose": { "version": "4.11.4", - "from": "mongoose@4.11.4", "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-4.11.4.tgz", + "integrity": "sha1-PXQREQXui2GescHXkOe/xOjLp7s=", + "requires": { + "async": "2.1.4", + "bson": "1.0.4", + "hooks-fixed": "2.0.0", + "kareem": "1.5.0", + "mongodb": "2.2.27", + "mpath": "0.3.0", + "mpromise": "0.5.5", + "mquery": "2.3.1", + "ms": "2.0.0", + "muri": "1.2.2", + "regexp-clone": "0.0.1", + "sliced": "1.0.1" + }, "dependencies": { "async": { "version": "2.1.4", - "from": "async@2.1.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.1.4.tgz" + "resolved": "https://registry.npmjs.org/async/-/async-2.1.4.tgz", + "integrity": "sha1-LSFgx3iAMuTdbL4lAvH5osj2zeQ=", + "requires": { + "lodash": "4.17.4" + } }, "es6-promise": { "version": "3.2.1", - "from": "es6-promise@3.2.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz" + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", + "integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q=" }, "isarray": { "version": "1.0.0", - "from": "isarray@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "mongodb": { "version": "2.2.27", - "from": "mongodb@2.2.27", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.27.tgz" + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.27.tgz", + "integrity": "sha1-NBIgNNtm2YO89qta2yaiSnD+9uY=", + "requires": { + "es6-promise": "3.2.1", + "mongodb-core": "2.1.11", + "readable-stream": "2.2.7" + } }, "mongodb-core": { "version": "2.1.11", - "from": "mongodb-core@2.1.11", - "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.11.tgz" + "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.11.tgz", + "integrity": "sha1-HDh3bOsXSZepnCiGDu2QKNqbPho=", + "requires": { + "bson": "1.0.4", + "require_optional": "1.0.1" + } }, "readable-stream": { "version": "2.2.7", - "from": "readable-stream@2.2.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz" + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz", + "integrity": "sha1-BwV6y+JGeyIELTb5jFrVBwVOlbE=", + "requires": { + "buffer-shims": "1.0.0", + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } }, "string_decoder": { "version": "1.0.3", - "from": "string_decoder@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz" + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", + "requires": { + "safe-buffer": "5.1.1" + } } } }, "monocle": { "version": "1.1.51", - "from": "monocle@1.1.51", - "resolved": "https://registry.npmjs.org/monocle/-/monocle-1.1.51.tgz" + "resolved": "https://registry.npmjs.org/monocle/-/monocle-1.1.51.tgz", + "integrity": "sha1-Iu0W4RLpsFZ2nFzKySDjdSSdicA=", + "requires": { + "readdirp": "0.2.5" + } }, "mpath": { "version": "0.3.0", - "from": "mpath@0.3.0", - "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.3.0.tgz" + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.3.0.tgz", + "integrity": "sha1-elj3iem1/TyUUgY0FXlg8mvV70Q=" }, "mpromise": { "version": "0.5.5", - "from": "mpromise@0.5.5", - "resolved": "https://registry.npmjs.org/mpromise/-/mpromise-0.5.5.tgz" + "resolved": "https://registry.npmjs.org/mpromise/-/mpromise-0.5.5.tgz", + "integrity": "sha1-9bJCWddjrMIlewoMjG2Gb9UXMuY=" }, "mquery": { "version": "2.3.1", - "from": "mquery@2.3.1", "resolved": "https://registry.npmjs.org/mquery/-/mquery-2.3.1.tgz", + "integrity": "sha1-mrNnSXFIAP8LtTpoHOS8TV8HyHs=", + "requires": { + "bluebird": "2.10.2", + "debug": "2.6.8", + "regexp-clone": "0.0.1", + "sliced": "0.0.5" + }, "dependencies": { "bluebird": { "version": "2.10.2", - "from": "bluebird@2.10.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.10.2.tgz" + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.10.2.tgz", + "integrity": "sha1-AkpVFylTCIV/FPkfEQb8O1VfRGs=" }, "debug": { "version": "2.6.8", - "from": "debug@2.6.8", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz" + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "requires": { + "ms": "2.0.0" + } }, "sliced": { "version": "0.0.5", - "from": "sliced@0.0.5", - "resolved": "https://registry.npmjs.org/sliced/-/sliced-0.0.5.tgz" + "resolved": "https://registry.npmjs.org/sliced/-/sliced-0.0.5.tgz", + "integrity": "sha1-XtwETKTrb3gW1Qui/GPiXY/kcH8=" } } }, "ms": { "version": "2.0.0", - "from": "ms@2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "multer": { "version": "0.1.8", - "from": "multer@>=0.1.8 <0.2.0", "resolved": "https://registry.npmjs.org/multer/-/multer-0.1.8.tgz", + "integrity": "sha1-VRuKYBUJNwG8rMlkkWsa4GV483s=", + "requires": { + "busboy": "0.2.14", + "mkdirp": "0.3.5", + "qs": "1.2.2", + "type-is": "1.5.7" + }, "dependencies": { "mime-db": { "version": "1.12.0", - "from": "mime-db@>=1.12.0 <1.13.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.12.0.tgz" + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.12.0.tgz", + "integrity": "sha1-PQxjGA9FjrENMlqqN9fFiuMS6dc=" }, "mime-types": { "version": "2.0.14", - "from": "mime-types@>=2.0.9 <2.1.0", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.14.tgz" + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.14.tgz", + "integrity": "sha1-MQ4VnbI+B3+Lsit0jav6SVcUCqY=", + "requires": { + "mime-db": "1.12.0" + } }, "mkdirp": { "version": "0.3.5", - "from": "mkdirp@>=0.3.5 <0.4.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=" }, "qs": { "version": "1.2.2", - "from": "qs@>=1.2.2 <1.3.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-1.2.2.tgz" + "resolved": "https://registry.npmjs.org/qs/-/qs-1.2.2.tgz", + "integrity": "sha1-GbV/8k3CqZzh+L32r82ln472H4g=" }, "type-is": { "version": "1.5.7", - "from": "type-is@>=1.5.2 <1.6.0", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.5.7.tgz" + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.5.7.tgz", + "integrity": "sha1-uTaKWTzG730GReeLL0xky+zQXpA=", + "requires": { + "media-typer": "0.3.0", + "mime-types": "2.0.14" + } } } }, "muri": { "version": "1.2.2", - "from": "muri@1.2.2", - "resolved": "https://registry.npmjs.org/muri/-/muri-1.2.2.tgz" + "resolved": "https://registry.npmjs.org/muri/-/muri-1.2.2.tgz", + "integrity": "sha1-YxmBMmUNsIoEzHnM0A3Tia/SYxw=" }, "mute-stream": { "version": "0.0.7", - "from": "mute-stream@>=0.0.4 <0.1.0", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, "mv": { "version": "0.0.5", - "from": "mv@0.0.5", "resolved": "https://registry.npmjs.org/mv/-/mv-0.0.5.tgz", + "integrity": "sha1-FerHWUeYhN8RMdbeVrziC2VPU5E=", "optional": true }, "nan": { "version": "2.3.5", - "from": "nan@2.3.5", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.3.5.tgz" + "resolved": "https://registry.npmjs.org/nan/-/nan-2.3.5.tgz", + "integrity": "sha1-gioNwmYpDOTNOhIoLKPn42Rmigg=" }, "native-promise-only": { "version": "0.8.1", - "from": "native-promise-only@>=0.8.1 <0.9.0", - "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz" + "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", + "integrity": "sha1-IKMYwwy0X3H+et+/eyHJnBRy7xE=" }, "nconf": { "version": "0.6.9", - "from": "nconf@>=0.6.9 <0.7.0", "resolved": "https://registry.npmjs.org/nconf/-/nconf-0.6.9.tgz", + "integrity": "sha1-lXDvFe1vmuays8jV5xtm0xk81mE=", "dev": true, + "requires": { + "async": "0.2.9", + "ini": "1.3.4", + "optimist": "0.6.0" + }, "dependencies": { "async": { "version": "0.2.9", - "from": "async@0.2.9", "resolved": "https://registry.npmjs.org/async/-/async-0.2.9.tgz", + "integrity": "sha1-32MGD789Myhqdqr21Vophtn/hhk=", "dev": true }, "optimist": { "version": "0.6.0", - "from": "optimist@0.6.0", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.0.tgz", - "dev": true + "integrity": "sha1-aUJIJvNAX3nxQub8PZrljU27kgA=", + "dev": true, + "requires": { + "minimist": "0.0.8", + "wordwrap": "0.0.2" + } } } }, "ncp": { "version": "2.0.0", - "from": "ncp@>=2.0.0 <2.1.0", "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", "optional": true }, "negotiator": { "version": "0.5.3", - "from": "negotiator@0.5.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.5.3.tgz" + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.5.3.tgz", + "integrity": "sha1-Jp1cR2gQ7JLtvntsLygxY4T5p+g=" }, "nise": { "version": "1.0.1", - "from": "nise@>=1.0.1 <2.0.0", "resolved": "https://registry.npmjs.org/nise/-/nise-1.0.1.tgz", + "integrity": "sha1-DakrEKhU6XwPSW9sKEWjASgLPu8=", + "requires": { + "formatio": "1.2.0", + "just-extend": "1.1.22", + "lolex": "1.6.0", + "path-to-regexp": "1.7.0" + }, "dependencies": { "formatio": { "version": "1.2.0", - "from": "formatio@^1.2.0", - "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.2.0.tgz" + "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.2.0.tgz", + "integrity": "sha1-87IWfZBoxGmKjVH092CjmlTYGOs=", + "requires": { + "samsam": "1.1.2" + } }, "lolex": { "version": "1.6.0", - "from": "lolex@>=1.6.0 <2.0.0", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.6.0.tgz" + "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.6.0.tgz", + "integrity": "sha1-OpoCg0UqR9dDnnJzG54H1zhuSfY=" }, "path-to-regexp": { "version": "1.7.0", - "from": "path-to-regexp@^1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz" + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", + "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "requires": { + "isarray": "0.0.1" + } } } }, "nocache": { "version": "2.0.0", - "from": "nocache@2.0.0", - "resolved": "https://registry.npmjs.org/nocache/-/nocache-2.0.0.tgz" + "resolved": "https://registry.npmjs.org/nocache/-/nocache-2.0.0.tgz", + "integrity": "sha1-ICtIAhoMTL3i34DeFaF0Q8i0OYA=" }, "node-forge": { "version": "0.2.24", - "from": "node-forge@0.2.24", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.2.24.tgz" + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.2.24.tgz", + "integrity": "sha1-+m+Eb0L6k/Y6CjDJ+/97ThMOCFg=" }, "node-html-encoder": { "version": "0.0.2", - "from": "node-html-encoder@0.0.2", - "resolved": "https://registry.npmjs.org/node-html-encoder/-/node-html-encoder-0.0.2.tgz" + "resolved": "https://registry.npmjs.org/node-html-encoder/-/node-html-encoder-0.0.2.tgz", + "integrity": "sha1-iXNhjXJ9pVJqgwtH0HwNgD4KFcY=" }, "node-pre-gyp": { "version": "0.6.30", - "from": "node-pre-gyp@0.6.30", "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.30.tgz", + "integrity": "sha1-ZNMHOm9XMANxfM/jDIkCMpe6u6E=", + "requires": { + "mkdirp": "0.5.1", + "nopt": "3.0.6", + "npmlog": "4.1.2", + "rc": "1.1.7", + "request": "2.81.0", + "rimraf": "2.5.4", + "semver": "5.3.0", + "tar": "2.2.1", + "tar-pack": "3.1.4" + }, "dependencies": { "glob": { "version": "7.1.2", - "from": "glob@>=7.0.5 <8.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz" + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } }, "minimatch": { "version": "3.0.4", - "from": "minimatch@>=3.0.4 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "requires": { + "brace-expansion": "1.1.8" + } }, "rimraf": { "version": "2.5.4", - "from": "rimraf@>=2.5.0 <2.6.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz" + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", + "integrity": "sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=", + "requires": { + "glob": "7.1.2" + } } } }, "nodemailer": { "version": "2.1.0", - "from": "nodemailer@2.1.0", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-2.1.0.tgz" + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-2.1.0.tgz", + "integrity": "sha1-zxCt/eblwfEL6pcAcavZ6DoJ0/Q=", + "requires": { + "libmime": "2.0.0", + "mailcomposer": "3.3.2", + "nodemailer-direct-transport": "2.0.1", + "nodemailer-shared": "1.0.3", + "nodemailer-smtp-pool": "2.1.0", + "nodemailer-smtp-transport": "2.0.1" + } }, "nodemailer-direct-transport": { "version": "2.0.1", - "from": "nodemailer-direct-transport@2.0.1", - "resolved": "https://registry.npmjs.org/nodemailer-direct-transport/-/nodemailer-direct-transport-2.0.1.tgz" + "resolved": "https://registry.npmjs.org/nodemailer-direct-transport/-/nodemailer-direct-transport-2.0.1.tgz", + "integrity": "sha1-qcb0mhz//84F3jaPOJojmP+TzLM=", + "requires": { + "nodemailer-shared": "1.0.3", + "smtp-connection": "2.0.1" + } }, "nodemailer-fetch": { "version": "1.2.1", - "from": "nodemailer-fetch@1.2.1", - "resolved": "https://registry.npmjs.org/nodemailer-fetch/-/nodemailer-fetch-1.2.1.tgz" + "resolved": "https://registry.npmjs.org/nodemailer-fetch/-/nodemailer-fetch-1.2.1.tgz", + "integrity": "sha1-78GM7CBbT/BR0gWHPXkGStGFCeM=" }, "nodemailer-sendgrid-transport": { "version": "0.2.0", - "from": "nodemailer-sendgrid-transport@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/nodemailer-sendgrid-transport/-/nodemailer-sendgrid-transport-0.2.0.tgz" + "resolved": "https://registry.npmjs.org/nodemailer-sendgrid-transport/-/nodemailer-sendgrid-transport-0.2.0.tgz", + "integrity": "sha1-pRZZO/49HyeM/hcGDh2yNlio9Pw=", + "requires": { + "sendgrid": "1.9.2" + } }, "nodemailer-ses-transport": { "version": "1.5.1", - "from": "nodemailer-ses-transport@>=1.3.0 <2.0.0", - "resolved": "https://registry.npmjs.org/nodemailer-ses-transport/-/nodemailer-ses-transport-1.5.1.tgz" + "resolved": "https://registry.npmjs.org/nodemailer-ses-transport/-/nodemailer-ses-transport-1.5.1.tgz", + "integrity": "sha1-3AWYwb9T6GUuYy6PMWks4CLX3qk=", + "requires": { + "aws-sdk": "2.113.0" + } }, "nodemailer-shared": { "version": "1.0.3", - "from": "nodemailer-shared@1.0.3", - "resolved": "https://registry.npmjs.org/nodemailer-shared/-/nodemailer-shared-1.0.3.tgz" + "resolved": "https://registry.npmjs.org/nodemailer-shared/-/nodemailer-shared-1.0.3.tgz", + "integrity": "sha1-99Zf9ywCnY8d2KaLgBGGIZLcogA=", + "requires": { + "nodemailer-fetch": "1.2.1" + } }, "nodemailer-smtp-pool": { "version": "2.1.0", - "from": "nodemailer-smtp-pool@2.1.0", - "resolved": "https://registry.npmjs.org/nodemailer-smtp-pool/-/nodemailer-smtp-pool-2.1.0.tgz" + "resolved": "https://registry.npmjs.org/nodemailer-smtp-pool/-/nodemailer-smtp-pool-2.1.0.tgz", + "integrity": "sha1-Nn10/pyJ8SnJnXuhbnJqa++u7Mg=", + "requires": { + "clone": "1.0.2", + "nodemailer-shared": "1.0.3", + "nodemailer-wellknown": "0.1.7", + "smtp-connection": "2.0.1" + } }, "nodemailer-smtp-transport": { "version": "2.0.1", - "from": "nodemailer-smtp-transport@2.0.1", - "resolved": "https://registry.npmjs.org/nodemailer-smtp-transport/-/nodemailer-smtp-transport-2.0.1.tgz" + "resolved": "https://registry.npmjs.org/nodemailer-smtp-transport/-/nodemailer-smtp-transport-2.0.1.tgz", + "integrity": "sha1-KZqulFCpAxjDSPrSc26ihIFURGY=", + "requires": { + "clone": "1.0.2", + "nodemailer-shared": "1.0.3", + "nodemailer-wellknown": "0.1.7", + "smtp-connection": "2.0.1" + } }, "nodemailer-wellknown": { "version": "0.1.7", - "from": "nodemailer-wellknown@0.1.7", - "resolved": "https://registry.npmjs.org/nodemailer-wellknown/-/nodemailer-wellknown-0.1.7.tgz" + "resolved": "https://registry.npmjs.org/nodemailer-wellknown/-/nodemailer-wellknown-0.1.7.tgz", + "integrity": "sha1-qmQZkKmfqAqdqKI1YpBUd8zu5V8=" }, "nomnom": { "version": "1.6.2", - "from": "nomnom@>=1.6.0 <1.7.0", "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.6.2.tgz", + "integrity": "sha1-hKZqJgF0QI/Ft3oY+IjszET7aXE=", "dev": true, + "requires": { + "colors": "0.5.1", + "underscore": "1.4.4" + }, "dependencies": { "colors": { "version": "0.5.1", - "from": "colors@>=0.5.0 <0.6.0", "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", + "integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q=", "dev": true }, "underscore": { "version": "1.4.4", - "from": "underscore@>=1.4.4 <1.5.0", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", + "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=", "dev": true } } }, "nopt": { "version": "3.0.6", - "from": "nopt@>=3.0.1 <3.1.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz" + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "requires": { + "abbrev": "1.1.0" + } }, "normalize-range": { "version": "0.1.2", - "from": "normalize-range@>=0.1.2 <0.2.0", "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", "dev": true }, "npm-run-path": { "version": "2.0.2", - "from": "npm-run-path@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "dev": true + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "2.0.1" + } }, "npmlog": { "version": "4.1.2", - "from": "npmlog@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz" + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha1-CKfyqL9zRgR3mp76StXMcXq7lUs=", + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } }, "nssocket": { "version": "0.5.3", - "from": "nssocket@>=0.5.1 <0.6.0", "resolved": "https://registry.npmjs.org/nssocket/-/nssocket-0.5.3.tgz", - "dev": true + "integrity": "sha1-iDyi7GBfXtZKTVGQsmJUAZKPj40=", + "dev": true, + "requires": { + "eventemitter2": "0.4.14", + "lazy": "1.0.11" + } }, "num2fraction": { "version": "1.2.2", - "from": "num2fraction@>=1.2.2 <2.0.0", "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", "dev": true }, "number-is-nan": { "version": "1.0.1", - "from": "number-is-nan@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, "oauth": { "version": "0.9.15", - "from": "oauth@>=0.9.0 <0.10.0", - "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz" + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha1-vR/vr2hslrdUda7VGWQS/2DPucE=" }, "oauth-sign": { "version": "0.8.2", - "from": "oauth-sign@>=0.8.1 <0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz" + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" }, "object-assign": { "version": "4.1.1", - "from": "object-assign@>=4.1.0 <5.0.0", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-inspect": { "version": "1.3.0", - "from": "object-inspect@>=1.3.0 <1.4.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.3.0.tgz" + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.3.0.tgz", + "integrity": "sha1-Wx645nQuLugzQqY3A02ESSi6L20=" }, "object-keys": { "version": "1.0.11", - "from": "object-keys@>=1.0.8 <2.0.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz" + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", + "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=" }, "on-finished": { "version": "2.3.0", - "from": "on-finished@>=2.3.0 <2.4.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz" + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } }, "on-headers": { "version": "1.0.1", - "from": "on-headers@>=1.0.1 <1.1.0", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", + "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=" }, "once": { "version": "1.3.3", - "from": "once@>=1.3.0 <1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz" + "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", + "requires": { + "wrappy": "1.0.2" + } }, "one-time": { "version": "0.0.4", - "from": "one-time@>=0.0.0 <0.1.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz" + "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz", + "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=" }, "onesky": { "version": "0.1.6", - "from": "onesky@>=0.1.5 <0.2.0", "resolved": "https://registry.npmjs.org/onesky/-/onesky-0.1.6.tgz", + "integrity": "sha1-fw1oy4DN/51ETHzVURZdgow0zaw=", "dev": true, + "requires": { + "request": "2.40.0", + "underscore": "1.6.0" + }, "dependencies": { "asn1": { "version": "0.1.11", - "from": "asn1@0.1.11", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz", + "integrity": "sha1-VZvhg3bQik7E2+gId9J4GGObLfc=", "dev": true, "optional": true }, "assert-plus": { "version": "0.1.5", - "from": "assert-plus@>=0.1.5 <0.2.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", + "integrity": "sha1-7nQAlBMALYTOxyGcasgRgS5yMWA=", "dev": true, "optional": true }, "async": { "version": "0.9.2", - "from": "async@>=0.9.0 <0.10.0", "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", "dev": true, "optional": true }, "aws-sign2": { "version": "0.5.0", - "from": "aws-sign2@>=0.5.0 <0.6.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz", + "integrity": "sha1-xXED96F/wDfwLXwuZLYC6iI/fWM=", "dev": true, "optional": true }, "boom": { "version": "0.4.2", - "from": "boom@>=0.4.0 <0.5.0", "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz", - "dev": true + "integrity": "sha1-emNune1O/O+xnO9JR6PGffrukRs=", + "dev": true, + "requires": { + "hoek": "0.9.1" + } }, "combined-stream": { "version": "0.0.7", - "from": "combined-stream@>=0.0.4 <0.1.0", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", + "integrity": "sha1-ATfmV7qlp1QcV6w3rF/AfXO03B8=", "dev": true, - "optional": true + "optional": true, + "requires": { + "delayed-stream": "0.0.5" + } }, "cryptiles": { "version": "0.2.2", - "from": "cryptiles@>=0.2.0 <0.3.0", "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz", + "integrity": "sha1-7ZH/HxetE9N0gohZT4pIoNJvMlw=", "dev": true, - "optional": true + "optional": true, + "requires": { + "boom": "0.4.2" + } }, "delayed-stream": { "version": "0.0.5", - "from": "delayed-stream@0.0.5", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz", + "integrity": "sha1-1LH0OpPoKW3+AmlPRoC8N6MTxz8=", "dev": true, "optional": true }, "forever-agent": { "version": "0.5.2", - "from": "forever-agent@>=0.5.0 <0.6.0", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz", + "integrity": "sha1-bQ4JxJIflKJ/Y9O0nF/v8epMUTA=", "dev": true }, "form-data": { "version": "0.1.4", - "from": "form-data@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", + "integrity": "sha1-kavXiKupcCsaq/qLwBAxoqyeOxI=", "dev": true, - "optional": true + "optional": true, + "requires": { + "async": "0.9.2", + "combined-stream": "0.0.7", + "mime": "1.2.11" + } }, "hawk": { "version": "1.1.1", - "from": "hawk@1.1.1", "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.1.1.tgz", + "integrity": "sha1-h81JH5tG5OKurKM1QWdmiF0tHtk=", "dev": true, - "optional": true + "optional": true, + "requires": { + "boom": "0.4.2", + "cryptiles": "0.2.2", + "hoek": "0.9.1", + "sntp": "0.2.4" + } }, "hoek": { "version": "0.9.1", - "from": "hoek@>=0.9.0 <0.10.0", "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz", + "integrity": "sha1-PTIkYrrfB3Fup+uFuviAec3c5QU=", "dev": true }, "http-signature": { "version": "0.10.1", - "from": "http-signature@>=0.10.0 <0.11.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", + "integrity": "sha1-T72sEyVZqoMjEh5UB3nAoBKyfmY=", "dev": true, - "optional": true + "optional": true, + "requires": { + "asn1": "0.1.11", + "assert-plus": "0.1.5", + "ctype": "0.5.3" + } }, "mime": { "version": "1.2.11", - "from": "mime@~1.2.11", "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", + "integrity": "sha1-WCA+7Ybjpe8XrtK32evUfwpg3RA=", "dev": true, "optional": true }, "mime-types": { "version": "1.0.2", - "from": "mime-types@>=1.0.1 <1.1.0", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-1.0.2.tgz", + "integrity": "sha1-mVrhOSq4r/y/yyZB3QVOlDwNXc4=", "dev": true }, "node-uuid": { "version": "1.4.8", - "from": "node-uuid@>=1.4.0 <1.5.0", "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", + "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=", "dev": true }, "oauth-sign": { "version": "0.3.0", - "from": "oauth-sign@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz", + "integrity": "sha1-y1QPk7srIqfVlBaRoojWDo6pOG4=", "dev": true, "optional": true }, "qs": { "version": "1.0.2", - "from": "qs@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/qs/-/qs-1.0.2.tgz", + "integrity": "sha1-UKk+K1r2aRwxvOpdrnjubqGQN2g=", "dev": true }, "request": { "version": "2.40.0", - "from": "request@>=2.40.0 <2.41.0", "resolved": "https://registry.npmjs.org/request/-/request-2.40.0.tgz", - "dev": true + "integrity": "sha1-TdZw9pbx5uhC5mtLXoOTAaub62c=", + "dev": true, + "requires": { + "aws-sign2": "0.5.0", + "forever-agent": "0.5.2", + "form-data": "0.1.4", + "hawk": "1.1.1", + "http-signature": "0.10.1", + "json-stringify-safe": "5.0.1", + "mime-types": "1.0.2", + "node-uuid": "1.4.8", + "oauth-sign": "0.3.0", + "qs": "1.0.2", + "stringstream": "0.0.5", + "tough-cookie": "2.3.2", + "tunnel-agent": "0.4.3" + } }, "sntp": { "version": "0.2.4", - "from": "sntp@>=0.2.0 <0.3.0", "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz", + "integrity": "sha1-+4hfGLDzqtGJ+CSGJTa87ux1CQA=", "dev": true, - "optional": true + "optional": true, + "requires": { + "hoek": "0.9.1" + } }, "tunnel-agent": { "version": "0.4.3", - "from": "tunnel-agent@>=0.4.0 <0.5.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", + "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", "dev": true, "optional": true } @@ -3429,1263 +4855,1866 @@ }, "optimist": { "version": "0.6.1", - "from": "optimist@0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz" + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "requires": { + "minimist": "0.0.8", + "wordwrap": "0.0.2" + } }, "os-homedir": { "version": "1.0.2", - "from": "os-homedir@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz" + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" }, "os-tmpdir": { "version": "1.0.2", - "from": "os-tmpdir@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, "osenv": { "version": "0.1.4", - "from": "osenv@>=0.1.4 <0.2.0", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz" + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } }, "parse-mongo-url": { "version": "1.1.1", - "from": "parse-mongo-url@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/parse-mongo-url/-/parse-mongo-url-1.1.1.tgz" + "resolved": "https://registry.npmjs.org/parse-mongo-url/-/parse-mongo-url-1.1.1.tgz", + "integrity": "sha1-ZiON9fjnwMjKTNlw1KtqE3PrdbU=" }, "parseurl": { "version": "1.3.2", - "from": "parseurl@>=1.3.0 <1.4.0", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz" + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" }, "passport": { "version": "0.3.2", - "from": "passport@>=0.3.2 <0.4.0", - "resolved": "https://registry.npmjs.org/passport/-/passport-0.3.2.tgz" + "resolved": "https://registry.npmjs.org/passport/-/passport-0.3.2.tgz", + "integrity": "sha1-ndAJ+RXo/glbASSgG4+C2gdRAQI=", + "requires": { + "passport-strategy": "1.0.0", + "pause": "0.0.1" + } }, "passport-ldapauth": { "version": "0.6.0", - "from": "passport-ldapauth@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/passport-ldapauth/-/passport-ldapauth-0.6.0.tgz" + "resolved": "https://registry.npmjs.org/passport-ldapauth/-/passport-ldapauth-0.6.0.tgz", + "integrity": "sha1-SCnrBz4fRjL649F5Xn4PSa9Gv5I=", + "requires": { + "ldapauth-fork": "2.5.5", + "passport-strategy": "1.0.0" + } }, "passport-local": { "version": "1.0.0", - "from": "passport-local@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", + "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=", + "requires": { + "passport-strategy": "1.0.0" + } }, "passport-oauth2": { "version": "1.4.0", - "from": "passport-oauth2@>=1.4.0 <2.0.0", - "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.4.0.tgz" + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.4.0.tgz", + "integrity": "sha1-9i+BWDy+EmCb585vFguTlaJ7hq0=", + "requires": { + "oauth": "0.9.15", + "passport-strategy": "1.0.0", + "uid2": "0.0.3", + "utils-merge": "1.0.0" + } }, "passport-oauth2-refresh": { "version": "1.0.0", - "from": "passport-oauth2-refresh@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/passport-oauth2-refresh/-/passport-oauth2-refresh-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/passport-oauth2-refresh/-/passport-oauth2-refresh-1.0.0.tgz", + "integrity": "sha1-hvOFco9dbTPrkRwlKcG+I60yS88=" }, "passport-saml": { "version": "0.15.0", - "from": "passport-saml@>=0.15.0 <0.16.0", "resolved": "https://registry.npmjs.org/passport-saml/-/passport-saml-0.15.0.tgz", + "integrity": "sha1-fUXAe6r4DY4s+Jg2cTKl5MBTXK0=", + "requires": { + "passport-strategy": "1.0.0", + "q": "1.1.2", + "xml-crypto": "0.8.5", + "xml-encryption": "0.7.4", + "xml2js": "0.4.19", + "xmlbuilder": "2.5.2", + "xmldom": "0.1.27" + }, "dependencies": { "lodash": { "version": "3.2.0", - "from": "lodash@>=3.2.0 <3.3.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.2.0.tgz" + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.2.0.tgz", + "integrity": "sha1-S/UKMkP5rrC6xBpV09WZBnWkYvs=" }, "xml2js": { "version": "0.4.19", - "from": "xml2js@>=0.4.0 <0.5.0", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha1-aGwg8hMgnpSr8NG88e+qKRx4J6c=", + "requires": { + "sax": "1.2.1", + "xmlbuilder": "9.0.4" + }, "dependencies": { "xmlbuilder": { "version": "9.0.4", - "from": "xmlbuilder@>=9.0.1 <9.1.0", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.4.tgz" + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.4.tgz", + "integrity": "sha1-UZy0ymhtAFqEINNJbz8MruzKWA8=" } } }, "xmlbuilder": { "version": "2.5.2", - "from": "xmlbuilder@>=2.5.0 <2.6.0", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-2.5.2.tgz" + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-2.5.2.tgz", + "integrity": "sha1-WriPxQirL/FIcwELVhY9P5KxkyU=", + "requires": { + "lodash": "3.2.0" + } } } }, "passport-strategy": { "version": "1.0.0", - "from": "passport-strategy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" }, "path-is-absolute": { "version": "1.0.1", - "from": "path-is-absolute@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-key": { "version": "2.0.1", - "from": "path-key@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", "dev": true }, "path-parse": { "version": "1.0.5", - "from": "path-parse@>=1.0.5 <2.0.0", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz" + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=" }, "path-to-regexp": { "version": "0.1.6", - "from": "path-to-regexp@0.1.6", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.6.tgz" + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.6.tgz", + "integrity": "sha1-8B/Vc0BHtr+8XyCMYTWjPXrwnDY=" }, "pathval": { "version": "1.1.0", - "from": "pathval@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz" + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=" }, "pause": { "version": "0.0.1", - "from": "pause@0.0.1", - "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz" + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" }, "performance-now": { "version": "0.2.0", - "from": "performance-now@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz" + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=" }, "pkginfo": { "version": "0.3.1", - "from": "pkginfo@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", + "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=", "dev": true }, "platform": { "version": "1.3.4", - "from": "platform@1.3.4", - "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.4.tgz" + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.4.tgz", + "integrity": "sha1-bw+xftqqSPIUQrOpdcBjEw8cPr0=" }, "pooling": { "version": "0.4.6", - "from": "pooling@0.4.6", "resolved": "https://registry.npmjs.org/pooling/-/pooling-0.4.6.tgz", + "integrity": "sha1-dqMXNx6oo2O0hY+keZ5gJF8w5mQ=", + "requires": { + "assert-plus": "0.1.5", + "bunyan": "0.22.1", + "dtrace-provider": "0.2.8", + "once": "1.3.0", + "vasync": "1.4.0" + }, "dependencies": { "assert-plus": { "version": "0.1.5", - "from": "assert-plus@0.1.5", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz" + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", + "integrity": "sha1-7nQAlBMALYTOxyGcasgRgS5yMWA=" }, "once": { "version": "1.3.0", - "from": "once@1.3.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.3.0.tgz" + "resolved": "https://registry.npmjs.org/once/-/once-1.3.0.tgz", + "integrity": "sha1-FRr4a/wfCMS58H0GqyUP/L61ZYE=" } } }, "postcss": { "version": "5.2.17", - "from": "postcss@>=5.2.16 <6.0.0", "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.3.2", + "source-map": "0.5.7", + "supports-color": "3.2.3" + }, "dependencies": { "source-map": { "version": "0.5.7", - "from": "source-map@>=0.5.6 <0.6.0", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true } } }, "postcss-value-parser": { "version": "3.3.0", - "from": "postcss-value-parser@>=3.2.3 <4.0.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", + "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=", "dev": true }, "precond": { "version": "0.2.3", - "from": "precond@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz" + "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", + "integrity": "sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw=" }, "process-nextick-args": { "version": "1.0.7", - "from": "process-nextick-args@>=1.0.6 <1.1.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" }, "promise": { "version": "2.0.0", - "from": "promise@>=2.0.0 <2.1.0", - "resolved": "https://registry.npmjs.org/promise/-/promise-2.0.0.tgz" + "resolved": "https://registry.npmjs.org/promise/-/promise-2.0.0.tgz", + "integrity": "sha1-RmSKqdYFr10ucMMCS/WUNtoCuA4=", + "requires": { + "is-promise": "1.0.1" + } }, "prompt": { "version": "0.2.14", - "from": "prompt@0.2.14", "resolved": "https://registry.npmjs.org/prompt/-/prompt-0.2.14.tgz", - "dev": true + "integrity": "sha1-V3VPZPVD/XsIRXB8gY7OYY8F/9w=", + "dev": true, + "requires": { + "pkginfo": "0.3.1", + "read": "1.0.7", + "revalidator": "0.1.8", + "utile": "0.2.1", + "winston": "0.8.3" + } }, "proxy-addr": { "version": "1.0.10", - "from": "proxy-addr@>=1.0.8 <1.1.0", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.0.10.tgz" + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.0.10.tgz", + "integrity": "sha1-DUCoL4Afw1VWfS7LZe/j8HfxIcU=", + "requires": { + "forwarded": "0.1.1", + "ipaddr.js": "1.0.5" + } }, "ps-tree": { "version": "0.0.3", - "from": "ps-tree@>=0.0.0 <0.1.0", "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-0.0.3.tgz", - "dev": true + "integrity": "sha1-2/jXUqf+Ivp9WGNWiUmWEOknbdw=", + "dev": true, + "requires": { + "event-stream": "0.5.3" + } }, "pseudomap": { "version": "1.0.2", - "from": "pseudomap@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz" + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, "pug": { "version": "2.0.0-rc.4", - "from": "pug@>=2.0.0-beta6 <3.0.0", - "resolved": "https://registry.npmjs.org/pug/-/pug-2.0.0-rc.4.tgz" + "resolved": "https://registry.npmjs.org/pug/-/pug-2.0.0-rc.4.tgz", + "integrity": "sha1-t7CPZZm9UwJWgEK3Q2mE+yjIChM=", + "requires": { + "pug-code-gen": "2.0.0", + "pug-filters": "2.1.5", + "pug-lexer": "3.1.0", + "pug-linker": "3.0.3", + "pug-load": "2.0.9", + "pug-parser": "4.0.0", + "pug-runtime": "2.0.3", + "pug-strip-comments": "1.0.2" + } }, "pug-attrs": { "version": "2.0.2", - "from": "pug-attrs@>=2.0.2 <3.0.0", "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-2.0.2.tgz", + "integrity": "sha1-i+KyIlVo/6ddG4Zpgr/59BEa/8s=", + "requires": { + "constantinople": "3.1.0", + "js-stringify": "1.0.2", + "pug-runtime": "2.0.3" + }, "dependencies": { "constantinople": { "version": "3.1.0", - "from": "constantinople@>=3.0.1 <4.0.0", - "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.0.tgz" + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.0.tgz", + "integrity": "sha1-dWnKqKo/jVk11i4fqW+fcCzYHHk=", + "requires": { + "acorn": "3.3.0", + "is-expression": "2.1.0" + } } } }, "pug-code-gen": { "version": "2.0.0", - "from": "pug-code-gen@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-2.0.0.tgz", + "integrity": "sha1-lq6jmp5i8exdK2pbQqKdUoxwtD0=", + "requires": { + "constantinople": "3.1.0", + "doctypes": "1.1.0", + "js-stringify": "1.0.2", + "pug-attrs": "2.0.2", + "pug-error": "1.3.2", + "pug-runtime": "2.0.3", + "void-elements": "2.0.1", + "with": "5.1.1" + }, "dependencies": { "constantinople": { "version": "3.1.0", - "from": "constantinople@>=3.0.1 <4.0.0", - "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.0.tgz" + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.0.tgz", + "integrity": "sha1-dWnKqKo/jVk11i4fqW+fcCzYHHk=", + "requires": { + "acorn": "3.3.0", + "is-expression": "2.1.0" + } }, "with": { "version": "5.1.1", - "from": "with@>=5.0.0 <6.0.0", - "resolved": "https://registry.npmjs.org/with/-/with-5.1.1.tgz" + "resolved": "https://registry.npmjs.org/with/-/with-5.1.1.tgz", + "integrity": "sha1-+k2qktrzLE6pTtRTyB8EaGtXXf4=", + "requires": { + "acorn": "3.3.0", + "acorn-globals": "3.1.0" + } } } }, "pug-error": { "version": "1.3.2", - "from": "pug-error@>=1.3.2 <2.0.0", - "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-1.3.2.tgz" + "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-1.3.2.tgz", + "integrity": "sha1-U659nSm7A89WRJOgJhCfVMR/XyY=" }, "pug-filters": { "version": "2.1.5", - "from": "pug-filters@>=2.1.5 <3.0.0", "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-2.1.5.tgz", + "integrity": "sha1-Zr9ugNl/vvgpurCqNe3f8z/JZPM=", + "requires": { + "clean-css": "3.4.28", + "constantinople": "3.1.0", + "jstransformer": "1.0.0", + "pug-error": "1.3.2", + "pug-walk": "1.1.5", + "resolve": "1.4.0", + "uglify-js": "2.8.29" + }, "dependencies": { "constantinople": { "version": "3.1.0", - "from": "constantinople@>=3.0.1 <4.0.0", - "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.0.tgz" + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.0.tgz", + "integrity": "sha1-dWnKqKo/jVk11i4fqW+fcCzYHHk=", + "requires": { + "acorn": "3.3.0", + "is-expression": "2.1.0" + } }, "source-map": { "version": "0.5.7", - "from": "source-map@>=0.5.1 <0.6.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" }, "uglify-js": { "version": "2.8.29", - "from": "uglify-js@>=2.6.1 <3.0.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz" + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "requires": { + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" + } }, "yargs": { "version": "3.10.0", - "from": "yargs@>=3.10.0 <3.11.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz" + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "requires": { + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", + "window-size": "0.1.0" + } } } }, "pug-lexer": { "version": "3.1.0", - "from": "pug-lexer@>=3.1.0 <4.0.0", "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-3.1.0.tgz", + "integrity": "sha1-/QhzdtSmdbT1n4/vQiiDQ06VgaI=", + "requires": { + "character-parser": "2.2.0", + "is-expression": "3.0.0", + "pug-error": "1.3.2" + }, "dependencies": { "acorn": { "version": "4.0.13", - "from": "acorn@>=4.0.2 <4.1.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz" + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" }, "character-parser": { "version": "2.2.0", - "from": "character-parser@>=2.1.1 <3.0.0", - "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz" + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", + "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=", + "requires": { + "is-regex": "1.0.4" + } }, "is-expression": { "version": "3.0.0", - "from": "is-expression@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz" + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz", + "integrity": "sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8=", + "requires": { + "acorn": "4.0.13", + "object-assign": "4.1.1" + } } } }, "pug-linker": { "version": "3.0.3", - "from": "pug-linker@>=3.0.3 <4.0.0", - "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-3.0.3.tgz" + "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-3.0.3.tgz", + "integrity": "sha1-JfWet1AjfwNo5ZwzeXZCKcAYnEE=", + "requires": { + "pug-error": "1.3.2", + "pug-walk": "1.1.5" + } }, "pug-load": { "version": "2.0.9", - "from": "pug-load@>=2.0.9 <3.0.0", - "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-2.0.9.tgz" + "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-2.0.9.tgz", + "integrity": "sha1-7iF8kUzB2TJNRLhsMtHfJB023no=", + "requires": { + "object-assign": "4.1.1", + "pug-walk": "1.1.5" + } }, "pug-parser": { "version": "4.0.0", - "from": "pug-parser@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-4.0.0.tgz" + "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-4.0.0.tgz", + "integrity": "sha1-yfUjIuTqvkv1vuumTtGDc7tieAE=", + "requires": { + "pug-error": "1.3.2", + "token-stream": "0.0.1" + } }, "pug-runtime": { "version": "2.0.3", - "from": "pug-runtime@>=2.0.3 <3.0.0", - "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-2.0.3.tgz" + "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-2.0.3.tgz", + "integrity": "sha1-mBYmB7D86eJU1CfzOYelrucWi9o=" }, "pug-strip-comments": { "version": "1.0.2", - "from": "pug-strip-comments@>=1.0.2 <2.0.0", - "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-1.0.2.tgz" + "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-1.0.2.tgz", + "integrity": "sha1-0xOvoBvMN0mA4TmeI+vy65vchRM=", + "requires": { + "pug-error": "1.3.2" + } }, "pug-walk": { "version": "1.1.5", - "from": "pug-walk@>=1.1.5 <2.0.0", - "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-1.1.5.tgz" + "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-1.1.5.tgz", + "integrity": "sha1-kOlDrLz3Ah5kVM8bMiRYkcum+FE=" }, "punycode": { "version": "1.4.1", - "from": "punycode@>=1.4.1 <2.0.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" }, "q": { "version": "1.1.2", - "from": "q@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/q/-/q-1.1.2.tgz" + "resolved": "https://registry.npmjs.org/q/-/q-1.1.2.tgz", + "integrity": "sha1-Y1fikSBnAdmfGXq4TlforRlvKok=" }, "qs": { "version": "6.4.0", - "from": "qs@>=6.4.0 <6.5.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz" + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=" }, "querystring": { "version": "0.2.0", - "from": "querystring@0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz" + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" }, "random-bytes": { "version": "1.0.0", - "from": "random-bytes@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" }, "range-parser": { "version": "1.0.3", - "from": "range-parser@>=1.0.2 <1.1.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.3.tgz" + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.3.tgz", + "integrity": "sha1-aHKCNTXGkuLCoBA4Jq/YLC4P8XU=" }, "raven": { "version": "1.2.1", - "from": "raven@>=1.1.3 <2.0.0", "resolved": "https://registry.npmjs.org/raven/-/raven-1.2.1.tgz", + "integrity": "sha1-lJwTTbAooZC3u/j3kKrlQbfAIL0=", + "requires": { + "cookie": "0.3.1", + "json-stringify-safe": "5.0.1", + "lsmod": "1.0.0", + "stack-trace": "0.0.9", + "uuid": "3.0.0" + }, "dependencies": { "cookie": { "version": "0.3.1", - "from": "cookie@0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz" + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" }, "uuid": { "version": "3.0.0", - "from": "uuid@3.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz" + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz", + "integrity": "sha1-Zyj8BFnEUNeWqZwxg3VpvfZy1yg=" } } }, "raw-body": { "version": "2.3.1", - "from": "raw-body@2.3.1", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.1.tgz", + "integrity": "sha1-MPleKmehTi5EE9jVH92SyHfo8u0=", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.2", + "iconv-lite": "0.4.18", + "unpipe": "1.0.0" + }, "dependencies": { "iconv-lite": { "version": "0.4.18", - "from": "iconv-lite@0.4.18", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz" + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz", + "integrity": "sha1-I9hlaxaq5nQqwpcy6o8DNqR4nPI=" } } }, "rc": { "version": "1.1.7", - "from": "rc@>=1.1.0 <1.2.0", "resolved": "https://registry.npmjs.org/rc/-/rc-1.1.7.tgz", + "integrity": "sha1-xepWS7B6/5/TpbMukGwdOmWUD+o=", + "requires": { + "deep-extend": "0.4.2", + "ini": "1.3.4", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, "dependencies": { "minimist": { "version": "1.2.0", - "from": "minimist@>=1.2.0 <2.0.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" } } }, "read": { "version": "1.0.7", - "from": "read@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", - "dev": true + "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", + "dev": true, + "requires": { + "mute-stream": "0.0.7" + } }, "readable-stream": { "version": "1.0.34", - "from": "readable-stream@>=1.0.24 <1.1.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } }, "readdirp": { "version": "0.2.5", - "from": "readdirp@>=0.2.3 <0.3.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-0.2.5.tgz" + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-0.2.5.tgz", + "integrity": "sha1-xMJ25Sl3riXbUZH+UdAIVQ8V2bs=", + "requires": { + "minimatch": "0.2.14" + } }, "redis": { "version": "0.10.1", - "from": "redis@0.10.1", - "resolved": "https://registry.npmjs.org/redis/-/redis-0.10.1.tgz" + "resolved": "https://registry.npmjs.org/redis/-/redis-0.10.1.tgz", + "integrity": "sha1-TwkliTHZYTdyOf29SV4dmaJjqOw=" }, "redis-commands": { "version": "1.3.1", - "from": "redis-commands@>=1.2.0 <2.0.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.1.tgz" + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.1.tgz", + "integrity": "sha1-gdgm9F+pyLIBH0zXoP5ZfSQdRCs=" }, "redis-parser": { "version": "2.6.0", - "from": "redis-parser@>=2.6.0 <3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz" + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", + "integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs=" }, "redis-sentinel": { "version": "0.1.1", - "from": "redis-sentinel@0.1.1", "resolved": "https://registry.npmjs.org/redis-sentinel/-/redis-sentinel-0.1.1.tgz", + "integrity": "sha1-Vj3TQduZMgMfSX+v3Td+hkj/s+U=", + "requires": { + "q": "0.9.2", + "redis": "0.11.0" + }, "dependencies": { "q": { "version": "0.9.2", - "from": "q@0.9.2", - "resolved": "https://registry.npmjs.org/q/-/q-0.9.2.tgz" + "resolved": "https://registry.npmjs.org/q/-/q-0.9.2.tgz", + "integrity": "sha1-I8BsRsgTKGFqrhaNPuI6Vr1D2vY=" }, "redis": { "version": "0.11.0", - "from": "redis@>=0.11.0 <0.12.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-0.11.0.tgz" + "resolved": "https://registry.npmjs.org/redis/-/redis-0.11.0.tgz", + "integrity": "sha1-/cAdSrTL5LO7LLKByP5WnDhX9XE=" } } }, "redis-sharelatex": { - "version": "1.0.2", - "from": "git+https://github.com/sharelatex/redis-sharelatex.git#v1.0.2", - "resolved": "git+https://github.com/sharelatex/redis-sharelatex.git#143b7eb192675f36d835080e534a4ac4899f918a", + "version": "git+https://github.com/sharelatex/redis-sharelatex.git#143b7eb192675f36d835080e534a4ac4899f918a", + "requires": { + "async": "2.5.0", + "chai": "1.9.1", + "coffee-script": "1.8.0", + "grunt": "0.4.5", + "grunt-contrib-coffee": "0.11.1", + "grunt-mocha-test": "0.12.0", + "ioredis": "2.5.0", + "mocha": "1.21.4", + "redis": "0.12.1", + "redis-sentinel": "0.1.1", + "sandboxed-module": "1.0.1", + "sinon": "1.10.3", + "underscore": "1.7.0" + }, "dependencies": { "ansi-regex": { "version": "0.2.1", - "from": "ansi-regex@^0.2.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", + "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=" }, "ansi-styles": { "version": "1.1.0", - "from": "ansi-styles@^1.1.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz" + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz", + "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=" }, "assertion-error": { "version": "1.0.0", - "from": "assertion-error@1.0.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz", + "integrity": "sha1-x/hUOP3UZrx8oWq5DIFRN5el0js=" }, "async": { "version": "2.5.0", - "from": "async@>=2.4.0 <3.0.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz" + "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", + "integrity": "sha1-hDGQ/WtzV6C54clW7d3V7IRitU0=", + "requires": { + "lodash": "4.17.4" + } }, "chai": { "version": "1.9.1", - "from": "chai@1.9.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-1.9.1.tgz" + "resolved": "https://registry.npmjs.org/chai/-/chai-1.9.1.tgz", + "integrity": "sha1-NxG7ZwbhVo80wLNgmL+PGUVcga4=", + "requires": { + "assertion-error": "1.0.0", + "deep-eql": "0.1.3" + } }, "chalk": { "version": "0.5.1", - "from": "chalk@~0.5.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz" + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", + "requires": { + "ansi-styles": "1.1.0", + "escape-string-regexp": "1.0.5", + "has-ansi": "0.1.0", + "strip-ansi": "0.3.0", + "supports-color": "0.2.0" + } }, "coffee-script": { "version": "1.8.0", - "from": "coffee-script@1.8.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz" + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz", + "integrity": "sha1-nJ8dK0pSoADe0Vtll5FwNkgmPB0=", + "requires": { + "mkdirp": "0.3.5" + } }, "commander": { "version": "2.0.0", - "from": "commander@2.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz" + "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz", + "integrity": "sha1-0bhvkB+LZL2UG96tr5JFMDk76Sg=" }, "deep-eql": { "version": "0.1.3", - "from": "deep-eql@0.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz" + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", + "requires": { + "type-detect": "0.1.1" + } }, "formatio": { "version": "1.0.2", - "from": "formatio@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.0.2.tgz" + "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.0.2.tgz", + "integrity": "sha1-55kcoUT/fYz/B7uayGqbeca6R+8=", + "requires": { + "samsam": "1.1.3" + } }, "fs-extra": { "version": "0.11.1", - "from": "fs-extra@>=0.11.1 <0.12.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.11.1.tgz", + "integrity": "sha1-3xBPlMyEHu+Pr+KkRsiPXTW7Lnk=", + "requires": { + "jsonfile": "2.4.0", + "mkdirp": "0.5.1", + "ncp": "0.6.0", + "rimraf": "2.6.2" + }, "dependencies": { "mkdirp": { "version": "0.5.1", - "from": "mkdirp@^0.5.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz" + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } } } }, "glob": { "version": "7.1.2", - "from": "glob@>=7.0.5 <8.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz" + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } }, "growl": { "version": "1.8.1", - "from": "growl@>=1.8.0 <1.9.0", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz" + "resolved": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz", + "integrity": "sha1-Sy3sjZB+k9szZiTc7AGDUC+MlCg=" }, "grunt-contrib-coffee": { "version": "0.11.1", - "from": "grunt-contrib-coffee@0.11.1", "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.11.1.tgz", + "integrity": "sha1-+v48nuikQryNF9WlwZ/I5i2fP0U=", + "requires": { + "chalk": "0.5.1", + "coffee-script": "1.7.1", + "lodash": "2.4.2" + }, "dependencies": { "coffee-script": { "version": "1.7.1", - "from": "coffee-script@~1.7.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz" + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz", + "integrity": "sha1-YplqhheAx15tUGnROCJyO3NAS/w=", + "requires": { + "mkdirp": "0.3.5" + } }, "lodash": { "version": "2.4.2", - "from": "lodash@~2.4.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=" } } }, "grunt-mocha-test": { "version": "0.12.0", - "from": "grunt-mocha-test@0.12.0", - "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.12.0.tgz" + "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.12.0.tgz", + "integrity": "sha1-nQu5enkEQIr9x64qAj3X1rJg5uM=", + "requires": { + "fs-extra": "0.11.1", + "hooker": "0.2.3" + } }, "has-ansi": { "version": "0.1.0", - "from": "has-ansi@^0.1.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz" + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", + "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=", + "requires": { + "ansi-regex": "0.2.1" + } }, "jade": { "version": "0.26.3", - "from": "jade@0.26.3", "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", + "requires": { + "commander": "0.6.1", + "mkdirp": "0.3.0" + }, "dependencies": { "commander": { "version": "0.6.1", - "from": "commander@0.6.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz" + "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", + "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=" }, "mkdirp": { "version": "0.3.0", - "from": "mkdirp@0.3.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz" + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=" } } }, "jsonfile": { "version": "2.4.0", - "from": "jsonfile@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz" + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "requires": { + "graceful-fs": "4.1.11" + } }, "minimatch": { "version": "3.0.4", - "from": "minimatch@>=3.0.4 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "requires": { + "brace-expansion": "1.1.8" + } }, "mkdirp": { "version": "0.3.5", - "from": "mkdirp@>=0.3.5 <0.4.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=" }, "mocha": { "version": "1.21.4", - "from": "mocha@1.21.4", "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.21.4.tgz", + "integrity": "sha1-531pw3c7o+K0/mtijCi13UOICtw=", + "requires": { + "commander": "2.0.0", + "debug": "1.0.5", + "diff": "1.0.7", + "glob": "3.2.3", + "growl": "1.8.1", + "jade": "0.26.3", + "mkdirp": "0.3.5" + }, "dependencies": { "glob": { "version": "3.2.3", - "from": "glob@3.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz" + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", + "integrity": "sha1-4xPusknHr/qlxHUoaw4RW1mDlGc=", + "requires": { + "graceful-fs": "2.0.3", + "inherits": "2.0.3", + "minimatch": "0.2.14" + } }, "graceful-fs": { "version": "2.0.3", - "from": "graceful-fs@>=2.0.0 <2.1.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz" + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz", + "integrity": "sha1-fNLNsiiko/Nule+mzBQt59GhNtA=" }, "minimatch": { "version": "0.2.14", - "from": "minimatch@~0.2.11", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz" + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } } } }, "ncp": { "version": "0.6.0", - "from": "ncp@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.6.0.tgz" + "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.6.0.tgz", + "integrity": "sha1-34zgIeJiviG1L+s9Plz6qxJJHw0=" }, "redis": { "version": "0.12.1", - "from": "redis@0.12.1", - "resolved": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz" + "resolved": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz", + "integrity": "sha1-ZN92rQ/IrOuuvSoGReikj6xJGF4=" }, "rimraf": { "version": "2.6.2", - "from": "rimraf@>=2.2.8 <3.0.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz" + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha1-LtgVDSShbqhlHm1u8PR8QVjOejY=", + "requires": { + "glob": "7.1.2" + } }, "samsam": { "version": "1.1.3", - "from": "samsam@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.3.tgz" + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.3.tgz", + "integrity": "sha1-n1CHQZtNCR8jJXHn+lLpCw9VJiE=" }, "sandboxed-module": { "version": "1.0.1", - "from": "sandboxed-module@1.0.1", - "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-1.0.1.tgz", + "integrity": "sha1-/XVEsYgexul8kd8r0CE8Nbj8P0s=", + "requires": { + "require-like": "0.1.2", + "stack-trace": "0.0.9" + } }, "sinon": { "version": "1.10.3", - "from": "sinon@1.10.3", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.10.3.tgz" + "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.10.3.tgz", + "integrity": "sha1-wGPg6Z2DJ9wZkROqtS64Oi6ePCw=", + "requires": { + "formatio": "1.0.2", + "util": "0.10.3" + } }, "strip-ansi": { "version": "0.3.0", - "from": "strip-ansi@^0.3.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz" + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", + "requires": { + "ansi-regex": "0.2.1" + } }, "supports-color": { "version": "0.2.0", - "from": "supports-color@^0.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz" + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", + "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=" }, "type-detect": { "version": "0.1.1", - "from": "type-detect@0.1.1", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz" + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", + "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=" }, "underscore": { "version": "1.7.0", - "from": "underscore@1.7.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", + "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=" } } }, "referrer-policy": { "version": "1.1.0", - "from": "referrer-policy@1.1.0", - "resolved": "https://registry.npmjs.org/referrer-policy/-/referrer-policy-1.1.0.tgz" + "resolved": "https://registry.npmjs.org/referrer-policy/-/referrer-policy-1.1.0.tgz", + "integrity": "sha1-NXdOtzW/UPtsB46DM0tHI1AgfXk=" }, "regexp-clone": { "version": "0.0.1", - "from": "regexp-clone@0.0.1", - "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-0.0.1.tgz" + "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-0.0.1.tgz", + "integrity": "sha1-p8LgmJH9vzj7sQ03b7cwA+aKxYk=" }, "regexp-quote": { "version": "0.0.0", - "from": "regexp-quote@0.0.0", "resolved": "https://registry.npmjs.org/regexp-quote/-/regexp-quote-0.0.0.tgz", + "integrity": "sha1-Hg9GUMhi3L/tVP1CsUjpuxch/PI=", "dev": true }, "repeat-string": { "version": "1.6.1", - "from": "repeat-string@>=1.5.2 <2.0.0", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" }, "replace": { "version": "0.2.10", - "from": "replace@>=0.2.4 <0.3.0", "resolved": "https://registry.npmjs.org/replace/-/replace-0.2.10.tgz", + "integrity": "sha1-ESM5fplbO/75mF/GPN3PeaAU/WQ=", "dev": true, + "requires": { + "colors": "0.5.1", + "minimatch": "0.2.14", + "nomnom": "1.6.2" + }, "dependencies": { "colors": { "version": "0.5.1", - "from": "colors@>=0.5.0 <0.6.0", "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", + "integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q=", "dev": true } } }, "request": { "version": "2.81.0", - "from": "request@>=2.69.0 <3.0.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz" + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.2", + "tunnel-agent": "0.6.0", + "uuid": "3.1.0" + } }, "requests": { "version": "0.1.7", - "from": "requests@>=0.1.7 <0.2.0", "resolved": "https://registry.npmjs.org/requests/-/requests-0.1.7.tgz", + "integrity": "sha1-e0lPH15ZYBa0OTgyzt00ZZ/RxiM=", + "requires": { + "axo": "0.0.2", + "eventemitter3": "1.1.1", + "extendible": "0.1.1", + "hang": "1.0.0", + "loads": "0.0.4", + "xhr-send": "1.0.0" + }, "dependencies": { "eventemitter3": { "version": "1.1.1", - "from": "eventemitter3@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.1.1.tgz" + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.1.1.tgz", + "integrity": "sha1-R3hr2qCHyvext15zq8XH1UAVjNA=" } } }, - "require_optional": { - "version": "1.0.1", - "from": "require_optional@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz" - }, "require-like": { "version": "0.1.2", - "from": "require-like@0.1.2", - "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz" + "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", + "integrity": "sha1-rW8wwTvs15cBDEaK+ndcDAprR/o=" + }, + "require_optional": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", + "integrity": "sha1-TPNaQkf2TKPfjC7yCMxJSxyo/C4=", + "requires": { + "resolve-from": "2.0.0", + "semver": "5.3.0" + } }, "requirejs": { "version": "2.1.22", - "from": "requirejs@>=2.1.0 <2.2.0", "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.1.22.tgz", + "integrity": "sha1-3Xj9LTQYDA1ixyS1uK68BmTgNm8=", "dev": true }, "requires-port": { "version": "1.0.0", - "from": "requires-port@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" }, "resolve": { "version": "1.4.0", - "from": "resolve@>=1.1.6 <2.0.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz" + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz", + "integrity": "sha1-p1vgHFPaJdk0qY69DkxKcxL5KoY=", + "requires": { + "path-parse": "1.0.5" + } }, "resolve-from": { "version": "2.0.0", - "from": "resolve-from@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz" + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" }, "resumer": { "version": "0.0.0", - "from": "resumer@>=0.0.0 <0.1.0", - "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz" + "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", + "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", + "requires": { + "through": "2.3.8" + } }, "retry-as-promised": { "version": "2.3.0", - "from": "retry-as-promised@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-2.3.0.tgz", + "integrity": "sha1-J79czZmZMrMWZWloJc82MMJ8Vi0=", + "requires": { + "bluebird": "3.5.0", + "debug": "2.6.8" + }, "dependencies": { "debug": { "version": "2.6.8", - "from": "debug@>=2.2.0 <3.0.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz" + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "requires": { + "ms": "2.0.0" + } } } }, "revalidator": { "version": "0.1.8", - "from": "revalidator@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz", + "integrity": "sha1-/s5hv6DBtSoga9axgZgYS91SOjs=", "dev": true }, "right-align": { "version": "0.1.3", - "from": "right-align@>=0.1.1 <0.2.0", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz" + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "requires": { + "align-text": "0.1.4" + } }, "rimraf": { "version": "2.2.6", - "from": "rimraf@2.2.6", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.6.tgz" + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.6.tgz", + "integrity": "sha1-xZWXVpsU2VatKcrMQr3d9fDqT0w=" }, "rndm": { "version": "1.2.0", - "from": "rndm@1.2.0", - "resolved": "https://registry.npmjs.org/rndm/-/rndm-1.2.0.tgz" + "resolved": "https://registry.npmjs.org/rndm/-/rndm-1.2.0.tgz", + "integrity": "sha1-8z/pz7Urv9UgqhgyO8ZdsRCht2w=" }, "rolling-rate-limiter": { - "version": "0.1.4", - "from": "git+https://github.com/ShaneKilkelly/rolling-rate-limiter.git#master", - "resolved": "git+https://github.com/ShaneKilkelly/rolling-rate-limiter.git#8a1a2cd8aaf9cd1a75cc81317b7f261157be2149" + "version": "git+https://github.com/ShaneKilkelly/rolling-rate-limiter.git#8a1a2cd8aaf9cd1a75cc81317b7f261157be2149", + "requires": { + "microtime-nodejs": "1.0.0" + } }, "safe-buffer": { "version": "5.1.1", - "from": "safe-buffer@>=5.1.1 <5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=" }, "safe-json-stringify": { "version": "1.0.4", - "from": "safe-json-stringify@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.0.4.tgz", + "integrity": "sha1-gaCY9Efku8P/MxKiQ1IbwGDvWRE=", "optional": true }, "samsam": { "version": "1.1.2", - "from": "samsam@1.1.2", - "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz" + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz", + "integrity": "sha1-vsEf3IOp/aBjQBIQ5AF2wwJNFWc=" }, "sandboxed-module": { "version": "0.2.0", - "from": "sandboxed-module@0.2.0", "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-0.2.0.tgz", + "integrity": "sha1-xZRDVA93A0t/z8o3B6Urskw2FkU=", "dev": true, + "requires": { + "require-like": "0.1.2", + "stack-trace": "0.0.6" + }, "dependencies": { "stack-trace": { "version": "0.0.6", - "from": "stack-trace@0.0.6", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.6.tgz", + "integrity": "sha1-HnGb1qJin/CcGJ4Xqe+QKpT8XbA=", "dev": true } } }, "sanitize-html": { "version": "1.14.1", - "from": "sanitize-html@>=1.14.1 <2.0.0", "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.14.1.tgz", - "dev": true + "integrity": "sha1-cw/6Ikm98YMz7/5FsoYXPJxa0Lg=", + "dev": true, + "requires": { + "htmlparser2": "3.9.2", + "regexp-quote": "0.0.0", + "xtend": "4.0.1" + } }, "sanitizer": { "version": "0.1.1", - "from": "sanitizer@0.1.1", - "resolved": "https://registry.npmjs.org/sanitizer/-/sanitizer-0.1.1.tgz" + "resolved": "https://registry.npmjs.org/sanitizer/-/sanitizer-0.1.1.tgz", + "integrity": "sha1-VIv706Xzoafg32seaydkhwN9fEw=" }, "sax": { "version": "1.2.1", - "from": "sax@1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz" + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" }, "semver": { "version": "5.3.0", - "from": "semver@>=5.3.0 <5.4.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz" + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" }, "send": { "version": "0.13.0", - "from": "send@0.13.0", "resolved": "https://registry.npmjs.org/send/-/send-0.13.0.tgz", + "integrity": "sha1-UY+SGusFYK7H3KspkLFM9vPM5d4=", + "requires": { + "debug": "2.2.0", + "depd": "1.0.1", + "destroy": "1.0.3", + "escape-html": "1.0.2", + "etag": "1.7.0", + "fresh": "0.3.0", + "http-errors": "1.3.1", + "mime": "1.3.4", + "ms": "0.7.1", + "on-finished": "2.3.0", + "range-parser": "1.0.3", + "statuses": "1.2.1" + }, "dependencies": { "debug": { "version": "2.2.0", - "from": "debug@>=2.2.0 <2.3.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz" + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "requires": { + "ms": "0.7.1" + } }, "depd": { "version": "1.0.1", - "from": "depd@>=1.0.1 <1.1.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/depd/-/depd-1.0.1.tgz", + "integrity": "sha1-gK7GTJ1tl+ZcwqnKqTwKpqv3Oqo=" }, "http-errors": { "version": "1.3.1", - "from": "http-errors@>=1.3.1 <1.4.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz" + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz", + "integrity": "sha1-GX4izevUGYWF6GlO9nhhl7ke2UI=", + "requires": { + "inherits": "2.0.3", + "statuses": "1.2.1" + } }, "ms": { "version": "0.7.1", - "from": "ms@0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" }, "statuses": { "version": "1.2.1", - "from": "statuses@>=1.2.1 <1.3.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz" + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz", + "integrity": "sha1-3e1FzBglbVHtQK7BQkidXGECbSg=" } } }, "sendgrid": { "version": "1.9.2", - "from": "sendgrid@>=1.8.0 <2.0.0", "resolved": "https://registry.npmjs.org/sendgrid/-/sendgrid-1.9.2.tgz", + "integrity": "sha1-1AfmogawoqaWQkbdnAZBwQvwLxk=", + "requires": { + "lodash": "3.10.1", + "mime": "1.3.4", + "request": "2.81.0", + "smtpapi": "1.3.1" + }, "dependencies": { "lodash": { "version": "3.10.1", - "from": "lodash@>=3.0.1 <4.0.0||>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz" + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" } } }, "sequelize": { "version": "3.30.4", - "from": "sequelize@>=3.2.0 <4.0.0", "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-3.30.4.tgz", + "integrity": "sha1-vaLfHjGFSwmeQUmhEen8Clyh0aQ=", + "requires": { + "bluebird": "3.5.0", + "depd": "1.1.1", + "dottie": "1.1.1", + "generic-pool": "2.4.2", + "inflection": "1.12.0", + "lodash": "4.12.0", + "moment": "2.18.1", + "moment-timezone": "0.5.13", + "retry-as-promised": "2.3.0", + "semver": "5.3.0", + "shimmer": "1.1.0", + "terraformer-wkt-parser": "1.1.2", + "toposort-class": "1.0.1", + "uuid": "3.1.0", + "validator": "5.7.0", + "wkx": "0.2.0" + }, "dependencies": { "lodash": { "version": "4.12.0", - "from": "lodash@4.12.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.12.0.tgz" + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.12.0.tgz", + "integrity": "sha1-K9bcRqBA9Z5obJcu0h2T3FkFMlg=" } } }, "serve-static": { "version": "1.10.3", - "from": "serve-static@>=1.10.0 <1.11.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.10.3.tgz", + "integrity": "sha1-zlpuzTEB/tXsCYJ9rCKpwpv7BTU=", + "requires": { + "escape-html": "1.0.3", + "parseurl": "1.3.2", + "send": "0.13.2" + }, "dependencies": { "debug": { "version": "2.2.0", - "from": "debug@>=2.2.0 <2.3.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz" + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "requires": { + "ms": "0.7.1" + } }, "destroy": { "version": "1.0.4", - "from": "destroy@>=1.0.4 <1.1.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz" + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, "escape-html": { "version": "1.0.3", - "from": "escape-html@>=1.0.3 <1.1.0", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, "http-errors": { "version": "1.3.1", - "from": "http-errors@>=1.3.1 <1.4.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz" + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz", + "integrity": "sha1-GX4izevUGYWF6GlO9nhhl7ke2UI=", + "requires": { + "inherits": "2.0.3", + "statuses": "1.2.1" + } }, "ms": { "version": "0.7.1", - "from": "ms@0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" }, "send": { "version": "0.13.2", - "from": "send@0.13.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.13.2.tgz" + "resolved": "https://registry.npmjs.org/send/-/send-0.13.2.tgz", + "integrity": "sha1-dl52B8gFVFK7pvCwUllTUJhgNt4=", + "requires": { + "debug": "2.2.0", + "depd": "1.1.1", + "destroy": "1.0.4", + "escape-html": "1.0.3", + "etag": "1.7.0", + "fresh": "0.3.0", + "http-errors": "1.3.1", + "mime": "1.3.4", + "ms": "0.7.1", + "on-finished": "2.3.0", + "range-parser": "1.0.3", + "statuses": "1.2.1" + } }, "statuses": { "version": "1.2.1", - "from": "statuses@>=1.2.1 <1.3.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz" + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz", + "integrity": "sha1-3e1FzBglbVHtQK7BQkidXGECbSg=" } } }, "set-blocking": { "version": "2.0.0", - "from": "set-blocking@>=2.0.0 <2.1.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "setprototypeof": { "version": "1.0.3", - "from": "setprototypeof@1.0.3", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz" + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" }, "settings-sharelatex": { - "version": "1.0.0", - "from": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", - "resolved": "git+https://github.com/sharelatex/settings-sharelatex.git#cbc5e41c1dbe6789721a14b3fdae05bf22546559", + "version": "git+https://github.com/sharelatex/settings-sharelatex.git#cbc5e41c1dbe6789721a14b3fdae05bf22546559", + "requires": { + "coffee-script": "1.6.0" + }, "dependencies": { "coffee-script": { "version": "1.6.0", - "from": "coffee-script@1.6.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz" + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz", + "integrity": "sha1-gIs5bhEPU9AhoZpO8fZb4OjjX6M=" } } }, "shimmer": { "version": "1.1.0", - "from": "shimmer@1.1.0", - "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.1.0.tgz" + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.1.0.tgz", + "integrity": "sha1-l9c3cTf/u6tCVSLkKf4KqJpIizU=" }, "sigmund": { "version": "1.0.1", - "from": "sigmund@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" }, "signal-exit": { "version": "3.0.2", - "from": "signal-exit@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz" + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, "sinon": { "version": "1.17.7", - "from": "sinon@>=1.17.0 <2.0.0", "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.17.7.tgz", - "dev": true + "integrity": "sha1-RUKk9JugxFwF6y6d2dID4rjv4L8=", + "dev": true, + "requires": { + "formatio": "1.1.1", + "lolex": "1.3.2", + "samsam": "1.1.2", + "util": "0.10.3" + } }, "sixpack-client": { "version": "1.0.0", - "from": "sixpack-client@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/sixpack-client/-/sixpack-client-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/sixpack-client/-/sixpack-client-1.0.0.tgz", + "integrity": "sha1-BwEiFjjhZ3Om7xbk5FySzpzSa2Y=" }, "sliced": { "version": "1.0.1", - "from": "sliced@1.0.1", - "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", + "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" }, "smtp-connection": { "version": "2.0.1", - "from": "smtp-connection@2.0.1", - "resolved": "https://registry.npmjs.org/smtp-connection/-/smtp-connection-2.0.1.tgz" + "resolved": "https://registry.npmjs.org/smtp-connection/-/smtp-connection-2.0.1.tgz", + "integrity": "sha1-5pKP44ZrQ2p8YHtZHa6Z2aB0WYE=", + "requires": { + "nodemailer-shared": "1.0.3" + } }, "smtpapi": { "version": "1.3.1", - "from": "smtpapi@>=1.2.0 <2.0.0", - "resolved": "https://registry.npmjs.org/smtpapi/-/smtpapi-1.3.1.tgz" + "resolved": "https://registry.npmjs.org/smtpapi/-/smtpapi-1.3.1.tgz", + "integrity": "sha1-GHQp607SffSjz/0j8OAkXN/mh9Q=" }, "sntp": { "version": "1.0.9", - "from": "sntp@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "requires": { + "hoek": "2.16.3" + } }, "source-map": { "version": "0.1.34", - "from": "source-map@0.1.34", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz" + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz", + "integrity": "sha1-p8/omux7FoLDsZjQrPtH19CQVms=", + "requires": { + "amdefine": "1.0.1" + } }, "sshpk": { "version": "1.13.1", - "from": "sshpk@>=1.7.0 <2.0.0", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, "dependencies": { "assert-plus": { "version": "1.0.0", - "from": "assert-plus@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" } } }, "stack-trace": { "version": "0.0.9", - "from": "stack-trace@0.0.9", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz" + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", + "integrity": "sha1-qPbq7KkGdMMz58Q5U/J1tFFRBpU=" }, "statsd-parser": { "version": "0.0.4", - "from": "statsd-parser@>=0.0.4 <0.1.0", - "resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz" + "resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz", + "integrity": "sha1-y9JDlTzELv/VSLXSI4jtaJ7GOb0=" }, "statuses": { "version": "1.3.1", - "from": "statuses@>=1.3.1 <2.0.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz" + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" }, "stream-consume": { "version": "0.1.0", - "from": "stream-consume@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.0.tgz" + "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.0.tgz", + "integrity": "sha1-pB6tGm1ggc63n2WwYZAbbY89HQ8=" }, "streamsearch": { "version": "0.1.2", - "from": "streamsearch@0.1.2", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz" - }, - "string_decoder": { - "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" }, "string-width": { "version": "1.0.2", - "from": "string-width@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz" + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } }, "string.prototype.trim": { "version": "1.1.2", - "from": "string.prototype.trim@>=1.1.2 <1.2.0", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz" + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz", + "integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=", + "requires": { + "define-properties": "1.1.2", + "es-abstract": "1.8.2", + "function-bind": "1.1.1" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" }, "stringstream": { "version": "0.0.5", - "from": "stringstream@>=0.0.4 <0.1.0", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz" + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" }, "strip-ansi": { "version": "3.0.1", - "from": "strip-ansi@>=3.0.1 <4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "2.1.1" + } }, "strip-json-comments": { "version": "2.0.1", - "from": "strip-json-comments@>=2.0.1 <2.1.0", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" }, "supports-color": { "version": "3.2.3", - "from": "supports-color@>=3.2.3 <4.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "dev": true + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } }, "tape": { "version": "4.8.0", - "from": "tape@>=4.6.3 <5.0.0", "resolved": "https://registry.npmjs.org/tape/-/tape-4.8.0.tgz", + "integrity": "sha1-9qn+xBzFCh3lD6M2A6tYCZH2Bo4=", + "requires": { + "deep-equal": "1.0.1", + "defined": "1.0.0", + "for-each": "0.3.2", + "function-bind": "1.1.1", + "glob": "7.1.2", + "has": "1.0.1", + "inherits": "2.0.3", + "minimist": "1.2.0", + "object-inspect": "1.3.0", + "resolve": "1.4.0", + "resumer": "0.0.0", + "string.prototype.trim": "1.1.2", + "through": "2.3.8" + }, "dependencies": { "glob": { "version": "7.1.2", - "from": "glob@>=7.1.2 <7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz" + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } }, "minimatch": { "version": "3.0.4", - "from": "minimatch@>=3.0.4 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "requires": { + "brace-expansion": "1.1.8" + } }, "minimist": { "version": "1.2.0", - "from": "minimist@>=1.2.0 <1.3.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" } } }, "tar": { "version": "2.2.1", - "from": "tar@>=2.2.0 <2.3.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz" + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } }, "tar-pack": { "version": "3.1.4", - "from": "tar-pack@>=3.1.0 <3.2.0", "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.1.4.tgz", + "integrity": "sha1-vIz5oi9YMnOfEvORDaweuXtJcIw=", + "requires": { + "debug": "2.2.0", + "fstream": "1.0.11", + "fstream-ignore": "1.0.5", + "once": "1.3.3", + "readable-stream": "2.1.5", + "rimraf": "2.5.4", + "tar": "2.2.1", + "uid-number": "0.0.6" + }, "dependencies": { "debug": { "version": "2.2.0", - "from": "debug@>=2.2.0 <2.3.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz" + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "requires": { + "ms": "0.7.1" + } }, "glob": { "version": "7.1.2", - "from": "glob@>=7.0.5 <8.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz" + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } }, "isarray": { "version": "1.0.0", - "from": "isarray@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "minimatch": { "version": "3.0.4", - "from": "minimatch@>=3.0.4 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "requires": { + "brace-expansion": "1.1.8" + } }, "ms": { "version": "0.7.1", - "from": "ms@0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" }, "readable-stream": { "version": "2.1.5", - "from": "readable-stream@>=2.1.4 <2.2.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz" + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz", + "integrity": "sha1-ZvqLcg4UOLNkaB8q0aY8YYRIydA=", + "requires": { + "buffer-shims": "1.0.0", + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" + } }, "rimraf": { "version": "2.5.4", - "from": "rimraf@>=2.5.1 <2.6.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz" + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", + "integrity": "sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=", + "requires": { + "glob": "7.1.2" + } } } }, "tar-stream": { "version": "0.3.3", - "from": "tar-stream@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-0.3.3.tgz" + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-0.3.3.tgz", + "integrity": "sha1-I8pTvXOLhwInKoDibMk4vEuEuHs=", + "requires": { + "bl": "0.6.0", + "end-of-stream": "0.1.5", + "readable-stream": "1.0.34" + } }, "temp": { "version": "0.8.3", - "from": "temp@>=0.8.3 <0.9.0", - "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz" + "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz", + "integrity": "sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k=", + "requires": { + "os-tmpdir": "1.0.2", + "rimraf": "2.2.6" + } }, "terraformer": { "version": "1.0.8", - "from": "terraformer@>=1.0.5 <1.1.0", - "resolved": "https://registry.npmjs.org/terraformer/-/terraformer-1.0.8.tgz" + "resolved": "https://registry.npmjs.org/terraformer/-/terraformer-1.0.8.tgz", + "integrity": "sha1-UeCtiXRvzyFh3G9lqnDkI3fItZM=", + "requires": { + "@types/geojson": "1.0.3" + } }, "terraformer-wkt-parser": { "version": "1.1.2", - "from": "terraformer-wkt-parser@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/terraformer-wkt-parser/-/terraformer-wkt-parser-1.1.2.tgz" + "resolved": "https://registry.npmjs.org/terraformer-wkt-parser/-/terraformer-wkt-parser-1.1.2.tgz", + "integrity": "sha1-M2oMj8gglKWv+DKI9prt7NNpvww=", + "requires": { + "terraformer": "1.0.8" + } }, "text-encoding": { "version": "0.6.4", - "from": "text-encoding@0.6.4", - "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz" + "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", + "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=" }, "through": { "version": "2.3.8", - "from": "through@>=2.3.8 <2.4.0", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, "thunky": { "version": "0.1.0", - "from": "thunky@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz" + "resolved": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz", + "integrity": "sha1-vzAUaCTituZ7Dy16Ssi+smkIaE4=" }, "timekeeper": { "version": "2.0.0", - "from": "timekeeper@latest", - "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-2.0.0.tgz" + "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-2.0.0.tgz", + "integrity": "sha512-DVH+iEKcVwU3JkZK0Z86qFx8osIG05U1H/F6lAE+iPfvElioM9HPVd2ZKmoI4zS0AWsDogOXl/BuKWXNadI/fw==" }, "timespan": { "version": "2.3.0", - "from": "timespan@>=2.3.0 <2.4.0", "resolved": "https://registry.npmjs.org/timespan/-/timespan-2.3.0.tgz", + "integrity": "sha1-SQLOBAvRPYRcj1myfp1ZutbzmSk=", "dev": true }, "tiny-lr": { "version": "0.2.1", - "from": "tiny-lr@>=0.2.1 <0.3.0", "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.2.1.tgz", + "integrity": "sha1-s/26gC5dVqM8L28QeUsy5Hescp0=", "dev": true, + "requires": { + "body-parser": "1.14.2", + "debug": "2.2.0", + "faye-websocket": "0.10.0", + "livereload-js": "2.2.2", + "parseurl": "1.3.2", + "qs": "5.1.0" + }, "dependencies": { "body-parser": { "version": "1.14.2", - "from": "body-parser@>=1.14.0 <1.15.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.14.2.tgz", + "integrity": "sha1-EBXLH+LEQ4WCWVgdtTMy+NDPUPk=", "dev": true, + "requires": { + "bytes": "2.2.0", + "content-type": "1.0.4", + "debug": "2.2.0", + "depd": "1.1.1", + "http-errors": "1.3.1", + "iconv-lite": "0.4.13", + "on-finished": "2.3.0", + "qs": "5.2.0", + "raw-body": "2.1.7", + "type-is": "1.6.15" + }, "dependencies": { "qs": { "version": "5.2.0", - "from": "qs@5.2.0", "resolved": "https://registry.npmjs.org/qs/-/qs-5.2.0.tgz", + "integrity": "sha1-qfMRQq9GjLcrJbMBNrokVoNJFr4=", "dev": true } } }, "bytes": { "version": "2.2.0", - "from": "bytes@2.2.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.2.0.tgz", + "integrity": "sha1-/TVGSkA/b5EXwt42Cez/nK4ABYg=", "dev": true }, "debug": { "version": "2.2.0", - "from": "debug@>=2.2.0 <2.3.0", "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "dev": true + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "dev": true, + "requires": { + "ms": "0.7.1" + } }, "http-errors": { "version": "1.3.1", - "from": "http-errors@>=1.3.1 <1.4.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz", - "dev": true + "integrity": "sha1-GX4izevUGYWF6GlO9nhhl7ke2UI=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "statuses": "1.3.1" + } }, "iconv-lite": { "version": "0.4.13", - "from": "iconv-lite@0.4.13", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", + "integrity": "sha1-H4irpKsLFQjoMSrMOTRfNumS4vI=", "dev": true }, "ms": { "version": "0.7.1", - "from": "ms@0.7.1", "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", "dev": true }, "qs": { "version": "5.1.0", - "from": "qs@>=5.1.0 <5.2.0", "resolved": "https://registry.npmjs.org/qs/-/qs-5.1.0.tgz", + "integrity": "sha1-TZMuXH6kEcynajEtOaYGIA/VDNk=", "dev": true }, "raw-body": { "version": "2.1.7", - "from": "raw-body@>=2.1.5 <2.2.0", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.1.7.tgz", + "integrity": "sha1-rf6s4uT7MJgFgBTQjActzFl1h3Q=", "dev": true, + "requires": { + "bytes": "2.4.0", + "iconv-lite": "0.4.13", + "unpipe": "1.0.0" + }, "dependencies": { "bytes": { "version": "2.4.0", - "from": "bytes@2.4.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz", + "integrity": "sha1-fZcZb51br39pNeJZhVSe3SpsIzk=", "dev": true } } @@ -4694,481 +6723,687 @@ }, "to-mongodb-core": { "version": "2.0.0", - "from": "to-mongodb-core@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/to-mongodb-core/-/to-mongodb-core-2.0.0.tgz" + "resolved": "https://registry.npmjs.org/to-mongodb-core/-/to-mongodb-core-2.0.0.tgz", + "integrity": "sha1-NZbsdhOsmtO5ioncua77pWnNJ+s=" }, "token-stream": { "version": "0.0.1", - "from": "token-stream@0.0.1", - "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz" + "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz", + "integrity": "sha1-zu78cXp2xDFvEm0LnbqlXX598Bo=" }, "toposort-class": { "version": "1.0.1", - "from": "toposort-class@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg=" }, "tough-cookie": { "version": "2.3.2", - "from": "tough-cookie@>=2.3.0 <2.4.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz" + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", + "requires": { + "punycode": "1.4.1" + } }, "transformers": { "version": "2.1.0", - "from": "transformers@2.1.0", "resolved": "https://registry.npmjs.org/transformers/-/transformers-2.1.0.tgz", + "integrity": "sha1-XSPLNVYd2F3Gf7hIIwm0fVPM6ac=", + "requires": { + "css": "1.0.8", + "promise": "2.0.0", + "uglify-js": "2.2.5" + }, "dependencies": { "optimist": { "version": "0.3.7", - "from": "optimist@>=0.3.5 <0.4.0", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz" + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", + "integrity": "sha1-yQlBrVnkJzMokjB00s8ufLxuwNk=", + "requires": { + "wordwrap": "0.0.2" + } }, "uglify-js": { "version": "2.2.5", - "from": "uglify-js@>=2.2.5 <2.3.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.2.5.tgz" + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.2.5.tgz", + "integrity": "sha1-puAqcNg5eSuXgEiLe4sYTAlcmcc=", + "requires": { + "optimist": "0.3.7", + "source-map": "0.1.34" + } } } }, "translations-sharelatex": { - "version": "0.1.4", - "from": "git+https://github.com/sharelatex/translations-sharelatex.git#master", - "resolved": "git+https://github.com/sharelatex/translations-sharelatex.git#12d5cefc591bcc0b4e54b5ce9acfbd980208b9e1", + "version": "git+https://github.com/sharelatex/translations-sharelatex.git#12d5cefc591bcc0b4e54b5ce9acfbd980208b9e1", "dev": true, + "requires": { + "async": "2.5.0", + "coffee-script": "1.12.4", + "i18next": "1.7.10", + "onesky": "0.1.6", + "sanitize-html": "1.14.1", + "underscore": "1.6.0" + }, "dependencies": { "async": { "version": "2.5.0", - "from": "async@>=2.1.4 <3.0.0", "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", - "dev": true + "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==", + "dev": true, + "requires": { + "lodash": "4.17.4" + } } } }, "tsscmp": { "version": "1.0.5", - "from": "tsscmp@1.0.5", - "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.5.tgz" + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.5.tgz", + "integrity": "sha1-fcSjOvcVgatDN9qR2FylQn69mpc=" }, "tunnel-agent": { "version": "0.6.0", - "from": "tunnel-agent@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "5.1.1" + } }, "tweetnacl": { "version": "0.14.5", - "from": "tweetnacl@>=0.14.0 <0.15.0", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "optional": true }, "type-detect": { "version": "1.0.0", - "from": "type-detect@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz", + "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=", "dev": true }, "type-is": { "version": "1.6.15", - "from": "type-is@>=1.6.15 <1.7.0", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz" + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", + "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", + "requires": { + "media-typer": "0.3.0", + "mime-types": "2.1.17" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, "uglify-js": { "version": "2.4.24", - "from": "uglify-js@>=2.4.0 <2.5.0", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.4.24.tgz", + "integrity": "sha1-+tV1XB4Vd2WLsG/5q25UjJW+vW4=", + "requires": { + "async": "0.2.10", + "source-map": "0.1.34", + "uglify-to-browserify": "1.0.2", + "yargs": "3.5.4" + }, "dependencies": { "async": { "version": "0.2.10", - "from": "async@>=0.2.6 <0.3.0", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz" + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" } } }, "uglify-to-browserify": { "version": "1.0.2", - "from": "uglify-to-browserify@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz" + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=" }, "uid-number": { "version": "0.0.6", - "from": "uid-number@>=0.0.6 <0.1.0", - "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz" + "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", + "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=" }, "uid-safe": { "version": "2.1.4", - "from": "uid-safe@2.1.4", - "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.4.tgz" + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.4.tgz", + "integrity": "sha1-Otbzg2jG1MjHXsF2I/t5qh0HHYE=", + "requires": { + "random-bytes": "1.0.0" + } }, "uid2": { "version": "0.0.3", - "from": "uid2@>=0.0.0 <0.1.0", - "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz" + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz", + "integrity": "sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I=" }, "underscore": { "version": "1.6.0", - "from": "underscore@1.6.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz" + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" }, "underscore.string": { "version": "2.2.1", - "from": "underscore.string@>=2.2.1 <2.3.0", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz" + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz", + "integrity": "sha1-18D6KvXVoaZ/QlPa7pgTLnM/Dxk=" + }, + "universalify": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", + "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=" }, "unpipe": { "version": "1.0.0", - "from": "unpipe@1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, "url": { "version": "0.10.3", - "from": "url@0.10.3", "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, "dependencies": { "punycode": { "version": "1.3.2", - "from": "punycode@1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" } } }, "util": { "version": "0.10.3", - "from": "util@>=0.10.3 <1.0.0", "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "requires": { + "inherits": "2.0.1" + }, "dependencies": { "inherits": { "version": "2.0.1", - "from": "inherits@2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" } } }, "util-deprecate": { "version": "1.0.2", - "from": "util-deprecate@>=1.0.1 <1.1.0", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "utile": { "version": "0.2.1", - "from": "utile@>=0.2.1 <0.3.0", "resolved": "https://registry.npmjs.org/utile/-/utile-0.2.1.tgz", + "integrity": "sha1-kwyI6ZCY1iIINMNWy9mncFItkNc=", "dev": true, + "requires": { + "async": "0.2.10", + "deep-equal": "1.0.1", + "i": "0.3.5", + "mkdirp": "0.5.1", + "ncp": "0.4.2", + "rimraf": "2.2.6" + }, "dependencies": { "async": { "version": "0.2.10", - "from": "async@>=0.2.9 <0.3.0", "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", "dev": true }, "ncp": { "version": "0.4.2", - "from": "ncp@>=0.4.0 <0.5.0", "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz", + "integrity": "sha1-q8xsvT7C7Spyn/bnwfqPAXhKhXQ=", "dev": true } } }, "utils-merge": { "version": "1.0.0", - "from": "utils-merge@1.0.0", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", + "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=" }, "uuid": { "version": "3.1.0", - "from": "uuid@>=3.0.1 <4.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz" + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha1-PdPT55Crwk17DToDT/q6vijrvAQ=" }, "v8-profiler": { "version": "5.7.0", - "from": "v8-profiler@>=5.2.3 <6.0.0", "resolved": "https://registry.npmjs.org/v8-profiler/-/v8-profiler-5.7.0.tgz", + "integrity": "sha1-6DgcvrtbX9DKjSsJ9qAYGhWNs00=", + "requires": { + "nan": "2.7.0", + "node-pre-gyp": "0.6.37" + }, "dependencies": { "debug": { "version": "2.6.8", - "from": "debug@>=2.2.0 <3.0.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz" + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "requires": { + "ms": "2.0.0" + } }, "glob": { "version": "7.1.2", - "from": "glob@>=7.0.5 <8.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz" + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } }, "isarray": { "version": "1.0.0", - "from": "isarray@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "minimatch": { "version": "3.0.4", - "from": "minimatch@>=3.0.4 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "requires": { + "brace-expansion": "1.1.8" + } }, "nan": { "version": "2.7.0", - "from": "nan@>=2.5.1 <3.0.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz" + "resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz", + "integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY=" }, "node-pre-gyp": { "version": "0.6.37", - "from": "node-pre-gyp@>=0.6.34 <0.7.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.37.tgz" + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.37.tgz", + "integrity": "sha1-PIcrI2suJm5BQFeP4e6I9pMyOgU=", + "requires": { + "mkdirp": "0.5.1", + "nopt": "4.0.1", + "npmlog": "4.1.2", + "rc": "1.1.7", + "request": "2.81.0", + "rimraf": "2.6.2", + "semver": "5.3.0", + "tape": "4.8.0", + "tar": "2.2.1", + "tar-pack": "3.4.0" + } }, "nopt": { "version": "4.0.1", - "from": "nopt@>=4.0.1 <5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz" + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "requires": { + "abbrev": "1.1.0", + "osenv": "0.1.4" + } }, "readable-stream": { "version": "2.3.3", - "from": "readable-stream@>=2.1.4 <3.0.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz" + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha1-No8lEtefnUb9/HE0mueHi7weuVw=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } }, "rimraf": { "version": "2.6.2", - "from": "rimraf@>=2.6.1 <3.0.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz" + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha1-LtgVDSShbqhlHm1u8PR8QVjOejY=", + "requires": { + "glob": "7.1.2" + } }, "string_decoder": { "version": "1.0.3", - "from": "string_decoder@>=1.0.3 <1.1.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz" + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", + "requires": { + "safe-buffer": "5.1.1" + } }, "tar-pack": { "version": "3.4.0", - "from": "tar-pack@>=3.4.0 <4.0.0", - "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz" + "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz", + "integrity": "sha1-I74tf2cagzk3bL2wuP4/3r8xeYQ=", + "requires": { + "debug": "2.6.8", + "fstream": "1.0.11", + "fstream-ignore": "1.0.5", + "once": "1.3.3", + "readable-stream": "2.3.3", + "rimraf": "2.6.2", + "tar": "2.2.1", + "uid-number": "0.0.6" + } } } }, "validator": { "version": "5.7.0", - "from": "validator@>=5.2.0 <6.0.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-5.7.0.tgz" + "resolved": "https://registry.npmjs.org/validator/-/validator-5.7.0.tgz", + "integrity": "sha1-eoelgUa2laxIYHEUHAxJ1n2gXlw=" }, "vary": { "version": "1.0.1", - "from": "vary@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/vary/-/vary-1.0.1.tgz", + "integrity": "sha1-meSYFWaihhGN+yuBc1ffeZM3bRA=" }, "vasync": { "version": "1.4.0", - "from": "vasync@1.4.0", "resolved": "https://registry.npmjs.org/vasync/-/vasync-1.4.0.tgz", + "integrity": "sha1-bqWmNYI1iGjYdDy91v+tyQg7kQ8=", + "requires": { + "jsprim": "0.3.0", + "verror": "1.1.0" + }, "dependencies": { "extsprintf": { "version": "1.0.0", - "from": "extsprintf@1.0.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.0.tgz", + "integrity": "sha1-TVi4Fazlvr/E6/A8+YsKdgSpm4Y=" }, "json-schema": { "version": "0.2.2", - "from": "json-schema@0.2.2", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.2.tgz" + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.2.tgz", + "integrity": "sha1-UDVPGfYDkXxpX3C4Wvp3w7DyNQY=" }, "jsprim": { "version": "0.3.0", - "from": "jsprim@0.3.0", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-0.3.0.tgz", + "integrity": "sha1-zRNGbqJIDb2DlqVw1H0x3aR2+LE=", + "requires": { + "extsprintf": "1.0.0", + "json-schema": "0.2.2", + "verror": "1.3.3" + }, "dependencies": { "verror": { "version": "1.3.3", - "from": "verror@1.3.3", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.3.tgz" + "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.3.tgz", + "integrity": "sha1-impKw6jHdLb2h/7OSb3/14VS4s0=", + "requires": { + "extsprintf": "1.0.0" + } } } }, "verror": { "version": "1.1.0", - "from": "verror@1.1.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.1.0.tgz" + "resolved": "https://registry.npmjs.org/verror/-/verror-1.1.0.tgz", + "integrity": "sha1-KktOsUogcFHnWm+U7lExW/FzobA=", + "requires": { + "extsprintf": "1.0.0" + } } } }, "verror": { "version": "1.10.0", - "from": "verror@1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + }, "dependencies": { "assert-plus": { "version": "1.0.0", - "from": "assert-plus@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" } } }, "void-elements": { "version": "2.0.1", - "from": "void-elements@>=2.0.1 <3.0.0", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz" + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" }, "watch": { "version": "0.13.0", - "from": "watch@>=0.13.0 <0.14.0", "resolved": "https://registry.npmjs.org/watch/-/watch-0.13.0.tgz", + "integrity": "sha1-/MbSs/DoxzSC61Qjmhn9W8+adTw=", "dev": true, + "requires": { + "minimist": "1.2.0" + }, "dependencies": { "minimist": { "version": "1.2.0", - "from": "minimist@>=1.1.0 <2.0.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } } }, "websocket-driver": { "version": "0.7.0", - "from": "websocket-driver@>=0.5.1", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", - "dev": true + "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", + "dev": true, + "requires": { + "http-parser-js": "0.4.8", + "websocket-extensions": "0.1.2" + } }, "websocket-extensions": { "version": "0.1.2", - "from": "websocket-extensions@>=0.1.1", "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.2.tgz", + "integrity": "sha1-Dhh4HeYpoYMIzhSBZQ9n/6JpOl0=", "dev": true }, "which": { "version": "1.0.9", - "from": "which@>=1.0.5 <1.1.0", - "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz" + "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz", + "integrity": "sha1-RgwdoPgQED0DIam2M6+eV15kSG8=" }, "wide-align": { "version": "1.1.2", - "from": "wide-align@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz" + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", + "integrity": "sha1-Vx4PGwYEY268DfwhsDObvjE0FxA=", + "requires": { + "string-width": "1.0.2" + } }, "window-size": { "version": "0.1.0", - "from": "window-size@0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz" + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" }, "winston": { "version": "0.8.3", - "from": "winston@>=0.8.1 <0.9.0", "resolved": "https://registry.npmjs.org/winston/-/winston-0.8.3.tgz", + "integrity": "sha1-ZLar9M0Brcrv1QCTk7HY6L7BnbA=", "dev": true, + "requires": { + "async": "0.2.10", + "colors": "0.6.2", + "cycle": "1.0.3", + "eyes": "0.1.8", + "isstream": "0.1.2", + "pkginfo": "0.3.1", + "stack-trace": "0.0.9" + }, "dependencies": { "async": { "version": "0.2.10", - "from": "async@>=0.2.0 <0.3.0", "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", "dev": true } } }, "with": { "version": "3.0.1", - "from": "with@>=3.0.0 <3.1.0", - "resolved": "https://registry.npmjs.org/with/-/with-3.0.1.tgz" + "resolved": "https://registry.npmjs.org/with/-/with-3.0.1.tgz", + "integrity": "sha1-CDVNpBAkPPYXP7FCuwTmxm+W+FQ=", + "requires": { + "uglify-js": "2.4.24" + } }, "wkx": { "version": "0.2.0", - "from": "wkx@0.2.0", - "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.2.0.tgz" + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.2.0.tgz", + "integrity": "sha1-dsJPFqzQzY+TzTSqMx4PeWElboQ=" }, "wordwrap": { "version": "0.0.2", - "from": "wordwrap@0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz" + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" }, "wrappy": { "version": "1.0.2", - "from": "wrappy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "x-xss-protection": { "version": "1.0.0", - "from": "x-xss-protection@1.0.0", - "resolved": "https://registry.npmjs.org/x-xss-protection/-/x-xss-protection-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/x-xss-protection/-/x-xss-protection-1.0.0.tgz", + "integrity": "sha1-iYr7k4abJGYc+cUvnujbjtB2Tdk=" }, "xhr-response": { "version": "1.0.1", - "from": "xhr-response@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/xhr-response/-/xhr-response-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/xhr-response/-/xhr-response-1.0.1.tgz", + "integrity": "sha1-r/46CFRLpyGG5NxSzQImZx2Gc/4=" }, "xhr-send": { "version": "1.0.0", - "from": "xhr-send@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/xhr-send/-/xhr-send-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/xhr-send/-/xhr-send-1.0.0.tgz", + "integrity": "sha1-GkDHPl2yAo7gj+kPXD1tx/eWqFQ=" }, "xhr-status": { "version": "1.0.0", - "from": "xhr-status@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/xhr-status/-/xhr-status-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/xhr-status/-/xhr-status-1.0.0.tgz", + "integrity": "sha1-mo0ew670hvdnKcHq1921RWLY8I8=" }, "xml-crypto": { "version": "0.8.5", - "from": "xml-crypto@>=0.8.0 <0.9.0", "resolved": "https://registry.npmjs.org/xml-crypto/-/xml-crypto-0.8.5.tgz", + "integrity": "sha1-K7z7PrM/OoKiGLgiv2craxwg5Tg=", + "requires": { + "xmldom": "0.1.19", + "xpath.js": "1.0.7" + }, "dependencies": { "xmldom": { "version": "0.1.19", - "from": "xmldom@0.1.19", - "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.19.tgz" + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.19.tgz", + "integrity": "sha1-Yx/Ad3bv2EEYvyUXGzftTQdaCrw=" } } }, "xml-encryption": { "version": "0.7.4", - "from": "xml-encryption@>=0.7.0 <0.8.0", "resolved": "https://registry.npmjs.org/xml-encryption/-/xml-encryption-0.7.4.tgz", + "integrity": "sha1-Qnkexk1VbSRV3LnaClQSNmWsZcc=", + "requires": { + "async": "0.2.10", + "ejs": "0.8.8", + "node-forge": "0.2.24", + "xmldom": "0.1.27", + "xpath": "0.0.5" + }, "dependencies": { "async": { "version": "0.2.10", - "from": "async@>=0.2.7 <0.3.0", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz" + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" } } }, "xml2js": { "version": "0.2.0", - "from": "xml2js@0.2.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.2.0.tgz" + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.2.0.tgz", + "integrity": "sha1-99pSJ33rtkeYMFOtti2XLe5loaw=", + "requires": { + "sax": "1.2.1" + } }, "xmlbuilder": { "version": "4.2.1", - "from": "xmlbuilder@4.2.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz" + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz", + "integrity": "sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU=", + "requires": { + "lodash": "4.17.4" + } }, "xmldom": { "version": "0.1.27", - "from": "xmldom@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz" + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz", + "integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk=" }, "xpath": { "version": "0.0.5", - "from": "xpath@0.0.5", - "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.5.tgz" + "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.5.tgz", + "integrity": "sha1-RUA29u8PPfWvXUukoRn7dWdLPmw=" }, "xpath.js": { "version": "1.0.7", - "from": "xpath.js@>=0.0.3", - "resolved": "https://registry.npmjs.org/xpath.js/-/xpath.js-1.0.7.tgz" + "resolved": "https://registry.npmjs.org/xpath.js/-/xpath.js-1.0.7.tgz", + "integrity": "sha1-fpRif1QSdsvGprArXTXpQYVls+Q=" }, "xtend": { "version": "4.0.1", - "from": "xtend@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" }, "yargs": { "version": "3.5.4", - "from": "yargs@>=3.5.4 <3.6.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.5.4.tgz" + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.5.4.tgz", + "integrity": "sha1-2K/49mXpTDS9JZvevRv68N3TU2E=", + "requires": { + "camelcase": "1.2.1", + "decamelize": "1.2.0", + "window-size": "0.1.0", + "wordwrap": "0.0.2" + } + }, + "yauzl": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.8.0.tgz", + "integrity": "sha1-eUUK/yKyqcWkHvVOAtuQfM+/nuI=", + "requires": { + "buffer-crc32": "0.2.13", + "fd-slicer": "1.0.1" + } }, "zip-stream": { "version": "0.3.7", - "from": "zip-stream@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-0.3.7.tgz", + "integrity": "sha1-yE0FfrC8wBOXR708bJcoC89fK7I=", + "requires": { + "buffer-crc32": "0.2.13", + "crc32-stream": "0.2.0", + "debug": "1.0.5", + "deflate-crc32-stream": "0.1.2", + "lodash": "2.4.2", + "readable-stream": "1.0.34" + }, "dependencies": { "lodash": { "version": "2.4.2", - "from": "lodash@>=2.4.1 <2.5.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=" } } }, "zlib-browserify": { "version": "0.0.1", - "from": "zlib-browserify@0.0.1", "resolved": "https://registry.npmjs.org/zlib-browserify/-/zlib-browserify-0.0.1.tgz", + "integrity": "sha1-T6akXQDbwV8xikr6HZr8Aljhdsw=", "dev": true } } diff --git a/services/web/package.json b/services/web/package.json index a35688bfe7..80cba81a5f 100644 --- a/services/web/package.json +++ b/services/web/package.json @@ -25,6 +25,7 @@ "dateformat": "1.0.4-1.2.3", "express": "4.13.0", "express-session": "^1.14.2", + "fs-extra": "^4.0.2", "heapdump": "^0.3.7", "helmet": "^3.8.1", "http-proxy": "^1.8.1", @@ -69,7 +70,8 @@ "underscore": "1.6.0", "uuid": "^3.0.1", "v8-profiler": "^5.2.3", - "xml2js": "0.2.0" + "xml2js": "0.2.0", + "yauzl": "^2.8.0" }, "devDependencies": { "autoprefixer": "^6.6.1", From 96d08e41a7c30b30cdbb9799546be72abb9a285c Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 16 Oct 2017 11:13:53 +0100 Subject: [PATCH 149/206] update tests --- .../Features/Uploads/ArchiveManager.coffee | 13 +- .../coffee/Uploads/ArchiveManagerTests.coffee | 180 ++++++++++++++---- 2 files changed, 156 insertions(+), 37 deletions(-) diff --git a/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee b/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee index b71b5cd2bc..b52ff72afa 100644 --- a/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee +++ b/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee @@ -15,7 +15,7 @@ module.exports = ArchiveManager = _isZipTooLarge: (source, callback = (err, isTooLarge)->)-> callback = _.once callback - totalSizeInBytes = 0 + totalSizeInBytes = null yauzl.open source, {lazyEntries: true}, (err, zipfile) -> return callback(err) if err? @@ -61,11 +61,15 @@ module.exports = ArchiveManager = readStream.on "error", callback readStream.on "end", callback + errorHandler = (err) -> # clean up before calling callback + readStream.unpipe() + readStream.destroy() + callback(err) + fse.ensureDir Path.dirname(destFile), (err) -> - return callback(err) if err? + return errorHandler(err) if err? writeStream = fs.createWriteStream destFile - writeStream.on 'error', (err) -> - return callback(err) + writeStream.on 'error', errorHandler readStream.pipe(writeStream) _extractZipFiles: (source, destination, callback = (err) ->) -> @@ -110,6 +114,7 @@ module.exports = ArchiveManager = logger.log source: source, destination: destination, "unzipping file" ArchiveManager._extractZipFiles source, destination, (err) -> + timer.done() if err? logger.error {err, source, destination}, "unzip failed" callback(err) diff --git a/services/web/test/UnitTests/coffee/Uploads/ArchiveManagerTests.coffee b/services/web/test/UnitTests/coffee/Uploads/ArchiveManagerTests.coffee index fd6153efae..61efbccce7 100644 --- a/services/web/test/UnitTests/coffee/Uploads/ArchiveManagerTests.coffee +++ b/services/web/test/UnitTests/coffee/Uploads/ArchiveManagerTests.coffee @@ -10,24 +10,22 @@ describe "ArchiveManager", -> beforeEach -> @logger = error: sinon.stub() + warn: sinon.stub() err:-> log: sinon.stub() - @process = new events.EventEmitter - @process.stdout = new events.EventEmitter - @process.stderr = new events.EventEmitter - - @child = - spawn: sinon.stub().returns(@process) - - @metrics = Timer: class Timer done: sinon.stub() + @zipfile = new events.EventEmitter + @zipfile.readEntry = sinon.stub() + @zipfile.close = sinon.stub() + @ArchiveManager = SandboxedModule.require modulePath, requires: - "child_process": @child + "yauzl": @yauzl = {open: sinon.stub().callsArgWith(2, null, @zipfile)} "logger-sharelatex": @logger "metrics-sharelatex": @metrics "fs": @fs = {} + "fs-extra": @fse = {} describe "extractZipArchive", -> beforeEach -> @@ -39,10 +37,10 @@ describe "ArchiveManager", -> describe "successfully", -> beforeEach (done) -> @ArchiveManager.extractZipArchive @source, @destination, done - @process.emit "close" + @zipfile.emit "end" - it "should run unzip", -> - @child.spawn.calledWithExactly("unzip", [@source, "-d", @destination]).should.equal true + it "should run yauzl", -> + @yauzl.open.calledWith(@source).should.equal true it "should time the unzip", -> @metrics.Timer::done.called.should.equal true @@ -50,13 +48,12 @@ describe "ArchiveManager", -> it "should log the unzip", -> @logger.log.calledWith(sinon.match.any, "unzipping file").should.equal true - describe "with an error on stderr", -> + describe "with an error in the zip file header", -> beforeEach (done) -> + @yauzl.open = sinon.stub().callsArgWith(2, new Error("Something went wrong")) @ArchiveManager.extractZipArchive @source, @destination, (error) => @callback(error) done() - @process.stderr.emit "data", "Something went wrong" - @process.emit "close" it "should return the callback with an error", -> @callback.calledWithExactly(new Error("Something went wrong")).should.equal true @@ -74,60 +71,177 @@ describe "ArchiveManager", -> it "should return the callback with an error", -> @callback.calledWithExactly(new Error("zip_too_large")).should.equal true - it "should not call spawn", -> - @child.spawn.called.should.equal false + it "should not call yauzl.open", -> + @yauzl.open.called.should.equal false - describe "with an error on the process", -> + describe "with an error in the extracted files", -> beforeEach (done) -> @ArchiveManager.extractZipArchive @source, @destination, (error) => @callback(error) done() - @process.emit "error", new Error("Something went wrong") + @zipfile.emit "error", new Error("Something went wrong") it "should return the callback with an error", -> @callback.calledWithExactly(new Error("Something went wrong")).should.equal true it "should log out the error", -> @logger.error.called.should.equal true - + + describe "with a relative extracted file path", -> + beforeEach (done) -> + @zipfile.openReadStream = sinon.stub() + @ArchiveManager.extractZipArchive @source, @destination, (error) => + @callback(error) + done() + @zipfile.emit "entry", {fileName: "../testfile.txt"} + @zipfile.emit "end" + + it "should not write try to read the file entry", -> + @zipfile.openReadStream.called.should.equal false + + it "should log out a warning", -> + @logger.warn.called.should.equal true + + describe "with an unnormalized extracted file path", -> + beforeEach (done) -> + @zipfile.openReadStream = sinon.stub() + @ArchiveManager.extractZipArchive @source, @destination, (error) => + @callback(error) + done() + @zipfile.emit "entry", {fileName: "foo/./testfile.txt"} + @zipfile.emit "end" + + it "should not write try to read the file entry", -> + @zipfile.openReadStream.called.should.equal false + + it "should log out a warning", -> + @logger.warn.called.should.equal true + + describe "with a directory entry", -> + beforeEach (done) -> + @zipfile.openReadStream = sinon.stub() + @ArchiveManager.extractZipArchive @source, @destination, (error) => + @callback(error) + done() + @zipfile.emit "entry", {fileName: "testdir/"} + @zipfile.emit "end" + + it "should not write try to read the entry", -> + @zipfile.openReadStream.called.should.equal false + + it "should not log out a warning", -> + @logger.warn.called.should.equal false + + describe "with an error opening the file read stream", -> + beforeEach (done) -> + @zipfile.openReadStream = sinon.stub().callsArgWith(1, new Error("Something went wrong")) + @writeStream = new events.EventEmitter + @ArchiveManager.extractZipArchive @source, @destination, (error) => + @callback(error) + done() + @zipfile.emit "entry", {fileName: "testfile.txt"} + @zipfile.emit "end" + + it "should return the callback with an error", -> + @callback.calledWithExactly(new Error("Something went wrong")).should.equal true + + it "should log out the error", -> + @logger.error.called.should.equal true + + it "should close the zipfile", -> + @zipfile.close.called.should.equal true + + describe "with an error in the file read stream", -> + beforeEach (done) -> + @readStream = new events.EventEmitter + @readStream.pipe = sinon.stub() + @zipfile.openReadStream = sinon.stub().callsArgWith(1, null, @readStream) + @writeStream = new events.EventEmitter + @fs.createWriteStream = sinon.stub().returns @writeStream + @fse.ensureDir = sinon.stub().callsArg(1) + @ArchiveManager.extractZipArchive @source, @destination, (error) => + @callback(error) + done() + @zipfile.emit "entry", {fileName: "testfile.txt"} + @readStream.emit "error", new Error("Something went wrong") + @zipfile.emit "end" + + it "should return the callback with an error", -> + @callback.calledWithExactly(new Error("Something went wrong")).should.equal true + + it "should log out the error", -> + @logger.error.called.should.equal true + + it "should close the zipfile", -> + @zipfile.close.called.should.equal true + + describe "with an error in the file write stream", -> + beforeEach (done) -> + @readStream = new events.EventEmitter + @readStream.pipe = sinon.stub() + @readStream.unpipe = sinon.stub() + @readStream.destroy = sinon.stub() + @zipfile.openReadStream = sinon.stub().callsArgWith(1, null, @readStream) + @writeStream = new events.EventEmitter + @fs.createWriteStream = sinon.stub().returns @writeStream + @fse.ensureDir = sinon.stub().callsArg(1) + @ArchiveManager.extractZipArchive @source, @destination, (error) => + @callback(error) + done() + @zipfile.emit "entry", {fileName: "testfile.txt"} + @writeStream.emit "error", new Error("Something went wrong") + @zipfile.emit "end" + + it "should return the callback with an error", -> + @callback.calledWithExactly(new Error("Something went wrong")).should.equal true + + it "should log out the error", -> + @logger.error.called.should.equal true + + it "should unpipe from the readstream", -> + @readStream.unpipe.called.should.equal true + + it "should destroy the readstream", -> + @readStream.destroy.called.should.equal true + + it "should close the zipfile", -> + @zipfile.close.called.should.equal true + describe "_isZipTooLarge", -> - beforeEach -> - @output = (totalSize)->" Length Date Time Name \n-------- ---- ---- ---- \n241 03-12-16 12:20 main.tex \n108801 03-12-16 12:20 ddd/x1J5kHh.jpg \n-------- ------- \n#{totalSize} 2 files\n" it "should return false with small output", (done)-> @ArchiveManager._isZipTooLarge @source, (error, isTooLarge) => isTooLarge.should.equal false done() - @process.stdout.emit "data", @output("109042") - @process.emit "close" + @zipfile.emit "entry", {uncompressedSize: 109042} + @zipfile.emit "end" it "should return true with large bytes", (done)-> @ArchiveManager._isZipTooLarge @source, (error, isTooLarge) => isTooLarge.should.equal true done() - @process.stdout.emit "data", @output("1090000000000000042") - @process.emit "close" + @zipfile.emit "entry", {uncompressedSize: 1090000000000000042} + @zipfile.emit "end" it "should return error on no data", (done)-> @ArchiveManager._isZipTooLarge @source, (error, isTooLarge) => expect(error).to.exist done() - @process.stdout.emit "data", "" - @process.emit "close" + @zipfile.emit "entry", {} + @zipfile.emit "end" it "should return error if it didn't get a number", (done)-> @ArchiveManager._isZipTooLarge @source, (error, isTooLarge) => expect(error).to.exist done() - @process.stdout.emit "data", @output("total_size_string") - @process.emit "close" + @zipfile.emit "entry", {uncompressedSize:"random-error"} + @zipfile.emit "end" - it "should return error if the is only a bit of data", (done)-> + it "should return error if there is no data", (done)-> @ArchiveManager._isZipTooLarge @source, (error, isTooLarge) => expect(error).to.exist done() - @process.stdout.emit "data", " Length Date Time Name \n--------" - @process.emit "close" + @zipfile.emit "end" describe "findTopLevelDirectory", -> beforeEach -> From 2bb7c6d4c6d89f21b4e4f90c79de130923f6daf6 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 16 Oct 2017 14:21:28 +0100 Subject: [PATCH 150/206] remove child_process module from ArchiveManager --- services/web/app/coffee/Features/Uploads/ArchiveManager.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee b/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee index b52ff72afa..db31e5f0f4 100644 --- a/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee +++ b/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee @@ -1,4 +1,3 @@ -child = require "child_process" logger = require "logger-sharelatex" metrics = require "metrics-sharelatex" fs = require "fs" From 4c78b5770c7c20206f1002b567fbc2b98738ec40 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 16 Oct 2017 15:17:33 +0100 Subject: [PATCH 151/206] use regex for directory check --- services/web/app/coffee/Features/Uploads/ArchiveManager.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee b/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee index db31e5f0f4..9407cd9e5b 100644 --- a/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee +++ b/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee @@ -39,7 +39,8 @@ module.exports = ArchiveManager = _checkFilePath: (entry, destination, callback = (err, destFile) ->) -> # check if the entry is a directory - if /\/$/.test(entry.fileName) + endsWithSlash = /\/$/ + if endsWithSlash.test(entry.fileName) return callback() # don't give a destfile for directory # check that the file does not use a relative path for dir in entry.fileName.split('/') From ec4ff902731bdec51d4273f71d1587d44519ce2c Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Tue, 17 Oct 2017 12:24:47 +0100 Subject: [PATCH 152/206] IE scrollbar adjustments; use calc to set content min-height (IE fails with flex and min-height). --- services/web/public/stylesheets/app/project-list.less | 2 ++ services/web/public/stylesheets/core/ol-variables.less | 2 +- services/web/public/stylesheets/core/scaffolding.less | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less index d196ef43cf..72c320d388 100644 --- a/services/web/public/stylesheets/app/project-list.less +++ b/services/web/public/stylesheets/app/project-list.less @@ -68,6 +68,7 @@ height: 100%; overflow-x: hidden; overflow-y: auto; + -ms-overflow-style: -ms-autohiding-scrollbar; } .project-list-main { @@ -272,6 +273,7 @@ ul.structured-list { margin: 0; overflow: hidden; overflow-y: auto; + -ms-overflow-style: -ms-autohiding-scrollbar; li { border-bottom: 1px solid @structured-list-border-color; padding: (@line-height-computed / 4) 0; diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index 7b373ae0ca..9938b301bc 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -29,7 +29,7 @@ @navbar-brand-image-url : url(/img/ol-brand/overleaf-white.svg); // Backgrounds -@body-bg : @ol-blue-gray-1; +@body-bg : #FFF; @content-alt-bg-color : @ol-blue-gray-1; // Typography diff --git a/services/web/public/stylesheets/core/scaffolding.less b/services/web/public/stylesheets/core/scaffolding.less index 3258e36c2c..41c70bb11d 100755 --- a/services/web/public/stylesheets/core/scaffolding.less +++ b/services/web/public/stylesheets/core/scaffolding.less @@ -33,6 +33,9 @@ body { position: relative; padding-top: @header-height; padding-bottom: @footer-height; + & > .content { + min-height: calc(~"100vh -" (@header-height + @footer-height)); + } } // Reset fonts for relevant elements From aff5823a2d15d914e9a729304dfec36b13e02dae Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 17 Oct 2017 13:30:47 +0100 Subject: [PATCH 153/206] Revert "Complete 'usepackage' with packages" --- .../auto-complete/AutoCompleteManager.coffee | 13 +++--- .../auto-complete/CommandManager.coffee | 26 +++++------ .../auto-complete/PackageManager.coffee | 43 ------------------- 3 files changed, 18 insertions(+), 64 deletions(-) delete mode 100644 services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/PackageManager.coffee diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee index 60241da990..24a524b4b6 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee @@ -1,11 +1,10 @@ define [ "ide/editor/directives/aceEditor/auto-complete/CommandManager" "ide/editor/directives/aceEditor/auto-complete/EnvironmentManager" - "ide/editor/directives/aceEditor/auto-complete/PackageManager" "ide/editor/directives/aceEditor/auto-complete/Helpers" "ace/ace" "ace/ext-language_tools" -], (CommandManager, EnvironmentManager, PackageManager, Helpers) -> +], (CommandManager, EnvironmentManager, Helpers) -> Range = ace.require("ace/range").Range aceSnippetManager = ace.require('ace/snippets').snippetManager @@ -36,7 +35,6 @@ define [ }) SnippetCompleter = new EnvironmentManager() - PackageCompleter = new PackageManager() Graphics = @graphics Preamble = @preamble @@ -128,11 +126,10 @@ define [ callback null, result @editor.completers = [ - @suggestionManager - SnippetCompleter - PackageCompleter - ReferencesCompleter - LabelsCompleter + @suggestionManager, + SnippetCompleter, + ReferencesCompleter, + LabelsCompleter, GraphicsCompleter ] diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/CommandManager.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/CommandManager.coffee index 88cd3a2e84..fd6cdf7e64 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/CommandManager.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/CommandManager.coffee @@ -12,7 +12,7 @@ define [], () -> 'Lambda', 'Xi', 'Pi', 'Sigma', 'Upsilon', 'Phi', 'Psi', 'Omega' ] singleArgumentCommands = [ - 'chapter', 'section', 'label', 'textbf', 'subsection', + 'chapter', 'usepackage', 'section', 'label', 'textbf', 'subsection', 'vspace', 'cite', 'textit', 'documentclass', 'includegraphics', 'input', 'emph','caption', 'ref', 'title', 'author', 'texttt', 'include', 'hspace', 'bibitem', 'url', 'large', 'subsubsection', 'textsc', 'date', @@ -63,19 +63,19 @@ define [], () -> meta: "cmd" } special = for cmd in special - { - caption: "\\#{cmd}{}" - snippet: "\\#{cmd}{}" - meta: "cmd" - } + { + caption: "\\#{cmd}{}" + snippet: "\\#{cmd}{}" + meta: "cmd" + } staticCommands = [].concat( - noArgumentCommands, - singleArgumentCommands, - doubleArgumentCommands, - tripleArgumentCommands, - special - ) + noArgumentCommands, + singleArgumentCommands, + doubleArgumentCommands, + tripleArgumentCommands, + special + ) class Parser constructor: (@doc, @prefix) -> @@ -172,7 +172,7 @@ define [], () -> commands = parser.parse() completions = [] for command in commands - if command[0] not in rawCommands and command[0] != "usepackage" + if command[0] not in rawCommands caption = "\\#{command[0]}" score = if caption == prefix then 99 else 50 snippet = caption diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/PackageManager.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/PackageManager.coffee deleted file mode 100644 index c0e1cf5e8f..0000000000 --- a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/PackageManager.coffee +++ /dev/null @@ -1,43 +0,0 @@ -define () -> - packages = [ - 'inputenc', 'graphicx', 'amsmath', 'geometry', 'amssymb', 'hyperref', - 'babel', 'color', 'xcolor', 'url', 'natbib', 'fontenc', 'fancyhdr', - 'amsfonts', 'booktabs', 'amsthm', 'float', 'tikz', 'caption', - 'setspace', 'multirow', 'array', 'multicol', 'titlesec', 'enumitem', - 'ifthen', 'listings', 'blindtext', 'subcaption', 'times', 'bm', - 'subfigure', 'algorithm', 'fontspec', 'biblatex', 'tabularx', - 'microtype', 'etoolbox', 'parskip', 'calc', 'verbatim', 'mathtools', - 'epsfig', 'wrapfig', 'lipsum', 'cite', 'textcomp', 'longtable', - 'textpos', 'algpseudocode', 'enumerate', 'subfig', 'pdfpages', - 'epstopdf', 'latexsym', 'lmodern', 'pifont', 'ragged2e', 'rotating', - 'dcolumn', 'xltxtra', 'marvosym', 'indentfirst', 'xspace', 'csquotes', - 'xparse', 'changepage', 'soul', 'xunicode', 'comment', 'mathrsfs', - 'tocbibind', 'lastpage', 'algorithm2e', 'pgfplots', 'lineno', - 'graphics', 'algorithmic', 'fullpage', 'mathptmx', 'todonotes', - 'ulem', 'tweaklist', 'moderncvstyleclassic', 'collection', - 'moderncvcompatibility', 'gensymb', 'helvet', 'siunitx', 'adjustbox', - 'placeins', 'colortbl', 'appendix', 'makeidx', 'supertabular', 'ifpdf', - 'framed', 'aliascnt', 'layaureo', 'authblk' - ] - - packageSnippets = for pkg in packages - { - caption: "\\usepackage{#{pkg}}" - snippet: "\\usepackage{#{pkg}}" - meta: "pkg" - } - - packageSnippets.push { - caption: "\\usepackage{}" - snippet: "\\usepackage{}" - meta: "pkg" - score: 55 - } - - class PackageManager - getCompletions: (editor, session, pos, prefix, callback) -> - docText = session.getValue() - loaded = parseLoadedPackages docText - callback null, packageSnippets - - return PackageManager From 6ff3a2de9ff984bea6998a4f8d292b6c245d756a Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Tue, 17 Oct 2017 15:04:27 +0100 Subject: [PATCH 154/206] Rollout autocompile to 1% of users and improve readability --- .../web/app/coffee/Features/Project/ProjectController.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/app/coffee/Features/Project/ProjectController.coffee b/services/web/app/coffee/Features/Project/ProjectController.coffee index 68d2c49bca..d9a3664165 100644 --- a/services/web/app/coffee/Features/Project/ProjectController.coffee +++ b/services/web/app/coffee/Features/Project/ProjectController.coffee @@ -257,8 +257,8 @@ module.exports = ProjectController = timestamp = parseInt(user_id.toString().substring(0, 8), 16) counter = parseInt(user_id.toString().substring(18, 24), 16) - rolloutProportion = 0 - if counter % 1000 >= rolloutProportion + rolloutPercentage = 1 # Percentage of users to roll out to + if counter % 1000 > rolloutPercentage # Don't show if user is not part of roll out return cb(null, false) userSignupDate = new Date(timestamp * 1000) From 79c77188f38404d8bf1b66cf10817167ef287ea4 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Tue, 17 Oct 2017 15:04:43 +0100 Subject: [PATCH 155/206] Disable source maps (to be reenabled later on). --- services/web/Gruntfile.coffee | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services/web/Gruntfile.coffee b/services/web/Gruntfile.coffee index 6e986edb61..c20912e0ae 100644 --- a/services/web/Gruntfile.coffee +++ b/services/web/Gruntfile.coffee @@ -137,13 +137,13 @@ module.exports = (grunt) -> less: app: - options: - sourceMap: true + # options: + # sourceMap: true files: "public/stylesheets/style.css": "public/stylesheets/style.less" ol: - options: - sourceMap: true + # options: + # sourceMap: true files: "public/stylesheets/ol-style.css": "public/stylesheets/ol-style.less" From 8e742785038d3b3146d8b16044e669bd0c96a256 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Tue, 17 Oct 2017 15:06:19 +0100 Subject: [PATCH 156/206] Remove test stuff. --- services/web/public/stylesheets/app/project-list.less | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less index 72c320d388..0aa44d1188 100644 --- a/services/web/public/stylesheets/app/project-list.less +++ b/services/web/public/stylesheets/app/project-list.less @@ -38,16 +38,6 @@ .container; } -// .project-list-content when (@is-overleaf) { -// height: 100%; -// } - -// .project-list-content when (@is-overleaf = false) { -// overflow: scroll; -// } - - - .sidebar-new-proj-btn when (@is-overleaf) { .btn-block; } From cf89c22ca50bcb537219ad5be574563b8510353c Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Tue, 17 Oct 2017 15:06:44 +0100 Subject: [PATCH 157/206] Clarify comments. --- services/web/public/stylesheets/components/footer.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/public/stylesheets/components/footer.less b/services/web/public/stylesheets/components/footer.less index ca33db8705..5b68fd7dde 100644 --- a/services/web/public/stylesheets/components/footer.less +++ b/services/web/public/stylesheets/components/footer.less @@ -6,7 +6,7 @@ footer.site-footer { bottom: 0; width: 100%; height: @footer-height; - line-height: @footer-height - 1; // Hack — in Chrome, using @line-height would generate vertical scrolling + line-height: @footer-height - 1; // Hack — in Chrome, using the full @footer-height would generate vertical scrolling ul { list-style: none; From db6ca5d1c399d51dd63a9be79de55e7712030ff0 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Tue, 17 Oct 2017 15:42:37 +0100 Subject: [PATCH 158/206] Reorganize common vars file. --- .../stylesheets/core/_common-variables.less | 167 ++++++++++-------- 1 file changed, 89 insertions(+), 78 deletions(-) diff --git a/services/web/public/stylesheets/core/_common-variables.less b/services/web/public/stylesheets/core/_common-variables.less index c181bd4b07..5135678a0f 100644 --- a/services/web/public/stylesheets/core/_common-variables.less +++ b/services/web/public/stylesheets/core/_common-variables.less @@ -791,84 +791,6 @@ @content-margin-top: @line-height-computed; @content-margin-top: @line-height-computed; -// Custom -@is-overleaf: false; -@header-height: 68px; -@footer-height: 50px; -@text-small-color: @gray; - -@content-alt-bg-color: lighten(@gray-lightest, 2.5%); - -@navbar-default-padding: 1rem 2rem; -@navbar-brand-width: 180px; -@navbar-btn-font-size: @font-size-base * 0.8; -@navbar-btn-border-radius: @border-radius-base; -@navbar-btn-font-weight: 700; -@navbar-btn-padding: 10px 10px 11px; -@navbar-btn-line-height: 1; - -@navbar-subdued-padding: 12px 12px 13px; -@navbar-subdued-color: @gray; -@navbar-subdued-hover-bg: @gray-lightest; -@navbar-subdued-hover-color: @gray-dark; - -@btn-border-radius-large: @border-radius-large; -@btn-border-radius-base: @border-radius-base; -@btn-border-radius-small: @border-radius-small; - -@card-box-shadow: 0 2px 4px rgba(0,0,0,0.15); - -@structured-list-link-color: darken(@blue, 10%); -@structured-header-border-color: @gray-lightest; -@structured-list-border-color: @gray-lightest; -@structured-list-hover-color: @gray-lightest; -@structured-list-line-height: @line-height-base; - -@sidebar-bg: transparent; -@sidebar-color: @gray; -@sidebar-link-color: #333; -@sidebar-active-border-radius: @border-radius-small; -@sidebar-active-bg: @link-color; -@sidebar-hover-bg: transparent; -@sidebar-hover-text-decoration: underline; - -@folders-menu-margin: 0; -@folders-menu-line-height: 1.2; -@folders-menu-item-v-padding: (@line-height-computed / 4); -@folders-menu-item-h-padding: (@line-height-computed / 4); -@folders-title-padding: 0; -@folders-title-margin-top: (@line-height-computed / 2); -@folders-title-margin-bottom: (@line-height-computed / 4); -@folders-title-font-size: @font-size-base; -@folders-title-font-weight: 500; -@folders-title-line-height: @headings-line-height; -@folders-title-color: inherit; -@folders-title-text-transform: none; -@folders-tag-padding: 2px 20px 2px @folders-menu-item-h-padding; -@folders-tag-line-height: 1.8; -@folders-tag-display: block; -@folders-tag-menu-color: @gray; -@folders-tag-hover: darken(@gray-lightest, 2%); -@folders-tag-border-color: @text-color; -@folders-tag-menu-active-hover: darken(@brand-primary, 10%); -@folders-tag-menu-hover: @gray-light; -@folders-untagged-line-height: 1.7; - -@progress-border-radius: @border-radius-base; -@progress-border-width: 1px; -@progress-bar-shadow: inset 0 -1px 0 rgba(0,0,0,.15); - -@footer-link-color: @link-color; -@footer-link-hover-color: @link-hover-color; -@footer-bg-color: transparent; -@footer-padding: 2em; - -@btn-border-width: 1px; -@btn-border-bottom-width: 2px; - -@tag-border-radius: 0.25em; -@tag-bg-color: @label-default-bg; -@tag-bg-hover-color: darken(@label-default-bg, 10%); @left-menu-width: 260px; @left-menu-animation-duration: 0.35s; @@ -879,3 +801,92 @@ @editor-dark-background-color: #333; @editor-dark-toolbar-border-color: #222; @editor-dark-highlight-color: #FFA03A; + + +// Custom +@is-overleaf : false; +@header-height : 68px; +@footer-height : 50px; + +// Backgrounds +@content-alt-bg-color: lighten(@gray-lightest, 2.5%); + +// Typography +@text-small-color: @gray; + +// Navbar +@navbar-default-padding : 1rem 2rem; +@navbar-brand-width : 180px; +@navbar-btn-font-size : @font-size-base * 0.8; +@navbar-btn-border-radius : @border-radius-base; +@navbar-btn-font-weight : 700; +@navbar-btn-padding : 10px 10px 11px; +@navbar-btn-line-height : 1; +@navbar-subdued-padding : 12px 12px 13px; +@navbar-subdued-color : @gray; +@navbar-subdued-hover-bg : @gray-lightest; +@navbar-subdued-hover-color : @gray-dark; + +// Button colors and sizing +@btn-border-radius-large : @border-radius-large; +@btn-border-radius-base : @border-radius-base; +@btn-border-radius-small : @border-radius-small; +@btn-border-width : 1px; +@btn-border-bottom-width : 2px; + +// Cards +@card-box-shadow: 0 2px 4px rgba(0,0,0,0.15); + +// Project table +@structured-list-link-color : darken(@blue, 10%); +@structured-header-border-color : @gray-lightest; +@structured-list-border-color : @gray-lightest; +@structured-list-hover-color : @gray-lightest; +@structured-list-line-height : @line-height-base; + +// Sidebar +@sidebar-bg : transparent; +@sidebar-color : @gray; +@sidebar-link-color : #333; +@sidebar-active-border-radius : @border-radius-small; +@sidebar-active-bg : @link-color; +@sidebar-hover-bg : transparent; +@sidebar-hover-text-decoration : underline; + +@folders-menu-margin : 0; +@folders-menu-line-height : 1.2; +@folders-menu-item-v-padding : (@line-height-computed / 4); +@folders-menu-item-h-padding : (@line-height-computed / 4); +@folders-title-padding : 0; +@folders-title-margin-top : (@line-height-computed / 2); +@folders-title-margin-bottom : (@line-height-computed / 4); +@folders-title-font-size : @font-size-base; +@folders-title-font-weight : 500; +@folders-title-line-height : @headings-line-height; +@folders-title-color : inherit; +@folders-title-text-transform : none; +@folders-tag-padding : 2px 20px 2px @folders-menu-item-h-padding; +@folders-tag-line-height : 1.8; +@folders-tag-display : block; +@folders-tag-menu-color : @gray; +@folders-tag-hover : darken(@gray-lightest, 2%); +@folders-tag-border-color : @text-color; +@folders-tag-menu-active-hover : darken(@brand-primary, 10%); +@folders-tag-menu-hover : @gray-light; +@folders-untagged-line-height : 1.7; + +// Progress bars +@progress-border-radius : @border-radius-base; +@progress-border-width : 1px; +@progress-bar-shadow : inset 0 -1px 0 rgba(0,0,0,.15); + +// Footer +@footer-link-color : @link-color; +@footer-link-hover-color : @link-hover-color; +@footer-bg-color : transparent; +@footer-padding : 2em; + +// Tags +@tag-border-radius : 0.25em; +@tag-bg-color : @label-default-bg; +@tag-bg-hover-color : darken(@label-default-bg, 10%); \ No newline at end of file From b0dc84748abb9ada18b89320905ac922ef559df0 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Tue, 17 Oct 2017 15:44:15 +0100 Subject: [PATCH 159/206] Fix to actual percentage --- .../web/app/coffee/Features/Project/ProjectController.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/app/coffee/Features/Project/ProjectController.coffee b/services/web/app/coffee/Features/Project/ProjectController.coffee index d9a3664165..d210d5d40a 100644 --- a/services/web/app/coffee/Features/Project/ProjectController.coffee +++ b/services/web/app/coffee/Features/Project/ProjectController.coffee @@ -258,7 +258,7 @@ module.exports = ProjectController = counter = parseInt(user_id.toString().substring(18, 24), 16) rolloutPercentage = 1 # Percentage of users to roll out to - if counter % 1000 > rolloutPercentage + if counter % 100 > rolloutPercentage # Don't show if user is not part of roll out return cb(null, false) userSignupDate = new Date(timestamp * 1000) From f70ab03bd8fbd0ad68ae2b7bdb6fa3d542e59b15 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 17 Oct 2017 15:47:50 +0100 Subject: [PATCH 160/206] show an error if the main file cannot be found also improve logic for detecting the main file, if there's only one file it must be the main file. --- .../Features/Compile/ClsiManager.coffee | 22 ++++++++++++++++--- services/web/app/views/project/editor/pdf.pug | 3 +++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/services/web/app/coffee/Features/Compile/ClsiManager.coffee b/services/web/app/coffee/Features/Compile/ClsiManager.coffee index cff2b931b6..0a939505e5 100755 --- a/services/web/app/coffee/Features/Compile/ClsiManager.coffee +++ b/services/web/app/coffee/Features/Compile/ClsiManager.coffee @@ -29,7 +29,11 @@ module.exports = ClsiManager = sendRequestOnce: (project_id, user_id, options = {}, callback = (error, status, outputFiles, clsiServerId, validationProblems) ->) -> ClsiManager._buildRequest project_id, options, (error, req) -> - return callback(error) if error? + if error? + if error.message is "no main file specified" + return callback(null, "validation-problems", null, null, {mainFile:error.message}) + else + return callback(error) logger.log project_id: project_id, "sending compile to CLSI" ClsiFormatChecker.checkRecoursesForProblems req.compile?.resources, (err, validationProblems)-> if err? @@ -206,9 +210,12 @@ module.exports = ClsiManager = resources = [] rootResourcePath = null rootResourcePathOverride = null + hasMainFile = false + numberOfDocsInProject = 0 for path, doc of docs path = path.replace(/^\//, "") # Remove leading / + numberOfDocsInProject++ if doc.lines? # add doc to resources unless it is just a stub entry resources.push path: path @@ -217,11 +224,20 @@ module.exports = ClsiManager = rootResourcePath = path if options.rootDoc_id? and doc._id.toString() == options.rootDoc_id.toString() rootResourcePathOverride = path + if path is "main.tex" + hasMainFile = true rootResourcePath = rootResourcePathOverride if rootResourcePathOverride? if !rootResourcePath? - logger.warn {project_id}, "no root document found, setting to main.tex" - rootResourcePath = "main.tex" + if hasMainFile + logger.warn {project_id}, "no root document found, setting to main.tex" + rootResourcePath = "main.tex" + else if numberOfDocsInProject is 1 # only one file, must be the main document + for path, doc of docs + rootResourcePath = path.replace(/^\//, "") # Remove leading / + logger.warn {project_id, rootResourcePath: rootResourcePath}, "no root document found, single document in project" + else + return callback new Error("no main file specified") for path, file of files path = path.replace(/^\//, "") # Remove leading / diff --git a/services/web/app/views/project/editor/pdf.pug b/services/web/app/views/project/editor/pdf.pug index 58b0aa6521..429fa41c6f 100644 --- a/services/web/app/views/project/editor/pdf.pug +++ b/services/web/app/views/project/editor/pdf.pug @@ -317,6 +317,9 @@ div.full-size.pdf(ng-controller="PdfController") div li(ng-repeat="entry in pdf.validation.conflictedPaths") {{ '/'+entry['path'] }} + .alert.alert-danger(ng-show="pdf.validation.mainFile") + strong #{translate("main_file_not_found")} + span #{translate("please_set_main_file")} .pdf-errors(ng-switch-when="errors") From 56cb901b4125c85e75a154d46958003d251012e8 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 17 Oct 2017 15:49:02 +0100 Subject: [PATCH 161/206] filter missing doc_ids from root doc_ids list --- services/web/app/coffee/Features/Compile/ClsiManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/app/coffee/Features/Compile/ClsiManager.coffee b/services/web/app/coffee/Features/Compile/ClsiManager.coffee index 0a939505e5..c27bc2fe90 100755 --- a/services/web/app/coffee/Features/Compile/ClsiManager.coffee +++ b/services/web/app/coffee/Features/Compile/ClsiManager.coffee @@ -184,7 +184,7 @@ module.exports = ClsiManager = # present in the docupdater. This allows finaliseRequest to # identify the root doc. possibleRootDocIds = [options.rootDoc_id, project.rootDoc_id] - for rootDoc_id in possibleRootDocIds when rootDoc_id? + for rootDoc_id in possibleRootDocIds when rootDoc_id? and rootDoc_id of docPath path = docPath[rootDoc_id] docs[path] ?= {_id: rootDoc_id, path: path} ClsiManager._finaliseRequest project_id, options, project, docs, [], callback From bcabec1c5de28342df9a20d9034ba570a03a9722 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 17 Oct 2017 15:50:29 +0100 Subject: [PATCH 162/206] added tests --- .../coffee/Compile/ClsiManagerTests.coffee | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/services/web/test/UnitTests/coffee/Compile/ClsiManagerTests.coffee b/services/web/test/UnitTests/coffee/Compile/ClsiManagerTests.coffee index 6786c3a1a8..bd02af2f28 100644 --- a/services/web/test/UnitTests/coffee/Compile/ClsiManagerTests.coffee +++ b/services/web/test/UnitTests/coffee/Compile/ClsiManagerTests.coffee @@ -346,7 +346,49 @@ describe "ClsiManager", -> it "should set to main.tex", -> @request.compile.rootResourcePath.should.equal "main.tex" - + + describe "when there is no valid root document and no main.tex document", -> + beforeEach () -> + @project.rootDoc_id = "not-valid" + @docs = { + "/other.tex": @doc_1 = { + name: "other.tex" + _id: "mock-doc-id-1" + lines: ["Hello", "world"] + }, + "/chapters/chapter1.tex": @doc_2 = { + name: "chapter1.tex" + _id: "mock-doc-id-2" + lines: [ + "Chapter 1" + ] + } + } + @ProjectEntityHandler.getAllDocs = sinon.stub().callsArgWith(1, null, @docs) + @ClsiManager._buildRequest @project, null, @callback + + it "should report an error", -> + @callback.calledWith(new Error("no main file specified")).should.equal true + + + describe "when there is no valid root document and a single document which is not main.tex", -> + beforeEach (done) -> + @project.rootDoc_id = "not-valid" + @docs = { + "/other.tex": @doc_1 = { + name: "other.tex" + _id: "mock-doc-id-1" + lines: ["Hello", "world"] + } + } + @ProjectEntityHandler.getAllDocs = sinon.stub().callsArgWith(1, null, @docs) + @ClsiManager._buildRequest @project, null, (@error, @request) => + done() + + it "should set io to the only file", -> + @request.compile.rootResourcePath.should.equal "other.tex" + + describe "with the draft option", -> it "should add the draft option into the request", (done) -> @ClsiManager._buildRequest @project_id, {timeout:100, draft: true}, (error, request) => From 82b84fd5f434fce11bc80658eed32520e9ce9e04 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 17 Oct 2017 15:50:38 +0100 Subject: [PATCH 163/206] hide logs when there are validation errors --- .../web/public/coffee/ide/pdf/controllers/PdfController.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee index 640c224e7b..4846837856 100644 --- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee +++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee @@ -231,6 +231,7 @@ define [ else if response.status == "validation-problems" $scope.pdf.view = "validation-problems" $scope.pdf.validation = response.validationProblems + $scope.shouldShowLogs = false else if response.status == "compile-in-progress" $scope.pdf.view = 'errors' $scope.pdf.compileInProgress = true From 76d73951f249b3daaaf8187aa6c7d466c561b5a5 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 17 Oct 2017 16:31:35 +0100 Subject: [PATCH 164/206] fix bug when unzipping directory --- services/web/app/coffee/Features/Uploads/ArchiveManager.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee b/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee index 9407cd9e5b..a0e3863a19 100644 --- a/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee +++ b/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee @@ -94,6 +94,8 @@ module.exports = ArchiveManager = return callback(err) else zipfile.readEntry() # continue to the next file + else # if it's a directory, continue + zipfile.readEntry() # no more entries to read zipfile.on "end", callback From 6001f142619ed11aae04ce146a94e0509d533c3b Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 17 Oct 2017 16:31:56 +0100 Subject: [PATCH 165/206] log each file unzipped --- services/web/app/coffee/Features/Uploads/ArchiveManager.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee b/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee index a0e3863a19..3db074b13f 100644 --- a/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee +++ b/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee @@ -81,6 +81,7 @@ module.exports = ArchiveManager = # read all the entries zipfile.readEntry() zipfile.on "entry", (entry) -> + logger.log {source:source, fileName: entry.fileName}, "processing zip file entry" ArchiveManager._checkFilePath entry, destination, (err, destFile) -> if err? logger.warn err:err, source:source, destination:destination, "skipping bad file path" From 277b9c5d500c0168a4cffc96f708ed33649a2c18 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 17 Oct 2017 16:32:26 +0100 Subject: [PATCH 166/206] ignore .git directories in uploads --- services/web/app/coffee/Features/Uploads/FileTypeManager.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/services/web/app/coffee/Features/Uploads/FileTypeManager.coffee b/services/web/app/coffee/Features/Uploads/FileTypeManager.coffee index 2e22ed5665..40c6e126de 100644 --- a/services/web/app/coffee/Features/Uploads/FileTypeManager.coffee +++ b/services/web/app/coffee/Features/Uploads/FileTypeManager.coffee @@ -18,6 +18,7 @@ module.exports = FileTypeManager = IGNORE_FILENAMES : [ "__MACOSX" + ".git" ] MAX_TEXT_FILE_SIZE: 1 * 1024 * 1024 # 1 MB From 290361dc723fbc088c5b1c1a2f8db0001f770a18 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 17 Oct 2017 16:49:51 +0100 Subject: [PATCH 167/206] ignore .gitignore files in uploads too --- services/web/app/coffee/Features/Uploads/FileTypeManager.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/services/web/app/coffee/Features/Uploads/FileTypeManager.coffee b/services/web/app/coffee/Features/Uploads/FileTypeManager.coffee index 40c6e126de..918f66df7b 100644 --- a/services/web/app/coffee/Features/Uploads/FileTypeManager.coffee +++ b/services/web/app/coffee/Features/Uploads/FileTypeManager.coffee @@ -19,6 +19,7 @@ module.exports = FileTypeManager = IGNORE_FILENAMES : [ "__MACOSX" ".git" + ".gitignore" ] MAX_TEXT_FILE_SIZE: 1 * 1024 * 1024 # 1 MB From cfa0989a031e47aea2e9e193158bc4a4656f72ac Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Wed, 18 Oct 2017 10:55:32 +0100 Subject: [PATCH 168/206] Remove extra semicolon. --- services/web/public/stylesheets/core/ol-variables.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index 9938b301bc..6255d44371 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -109,7 +109,7 @@ @sidebar-hover-bg : @ol-blue-gray-4; @sidebar-hover-text-decoration : none; -@folders-menu-margin : 0 -(@grid-gutter-width / 2);; +@folders-menu-margin : 0 -(@grid-gutter-width / 2); @folders-menu-line-height : @structured-list-line-height; @folders-menu-item-v-padding : (@line-height-computed / 4); @folders-menu-item-h-padding : (@grid-gutter-width / 2); From f6ebbc37db0eeed76167ad0b88ce8ed6ffc02a3c Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Wed, 18 Oct 2017 11:09:55 +0100 Subject: [PATCH 169/206] Fix z-index issue in the resolved comments dropdown. --- services/web/public/stylesheets/app/editor/review-panel.less | 1 + 1 file changed, 1 insertion(+) diff --git a/services/web/public/stylesheets/app/editor/review-panel.less b/services/web/public/stylesheets/app/editor/review-panel.less index 13b8b7e03f..f6896c5753 100644 --- a/services/web/public/stylesheets/app/editor/review-panel.less +++ b/services/web/public/stylesheets/app/editor/review-panel.less @@ -941,6 +941,7 @@ justify-content: center; border-radius: 3px; box-shadow: 0 0 20px 10px rgba(0, 0, 0, .3); + z-index: 1; &::before { content: ''; From bf4404b60a52298882158722a8b61b6d2f571a5a Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 18 Oct 2017 14:14:59 +0100 Subject: [PATCH 170/206] handle null values when saving rootDoc_id --- .../ide/settings/controllers/SettingsController.coffee | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/services/web/public/coffee/ide/settings/controllers/SettingsController.coffee b/services/web/public/coffee/ide/settings/controllers/SettingsController.coffee index 26520d76e0..df65a2f721 100644 --- a/services/web/public/coffee/ide/settings/controllers/SettingsController.coffee +++ b/services/web/public/coffee/ide/settings/controllers/SettingsController.coffee @@ -55,7 +55,11 @@ define [ $scope.$watch "project.rootDoc_id", (rootDoc_id, oldRootDoc_id) => return if @ignoreUpdates - if oldRootDoc_id? and rootDoc_id != oldRootDoc_id + # don't save on initialisation, Angular passes oldRootDoc_id as + # undefined in this case. + return if typeof oldRootDoc_id is "undefined" + # otherwise only save changes, null values are allowed + if (rootDoc_id != oldRootDoc_id) settings.saveProjectSettings({rootDocId: rootDoc_id}) From 4cc517240c34d33f8914e38b190890f693dc5139 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 19 Oct 2017 10:54:21 +0100 Subject: [PATCH 171/206] Increase rollout to 3% --- .../web/app/coffee/Features/Project/ProjectController.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/app/coffee/Features/Project/ProjectController.coffee b/services/web/app/coffee/Features/Project/ProjectController.coffee index d210d5d40a..80c74754e0 100644 --- a/services/web/app/coffee/Features/Project/ProjectController.coffee +++ b/services/web/app/coffee/Features/Project/ProjectController.coffee @@ -257,7 +257,7 @@ module.exports = ProjectController = timestamp = parseInt(user_id.toString().substring(0, 8), 16) counter = parseInt(user_id.toString().substring(18, 24), 16) - rolloutPercentage = 1 # Percentage of users to roll out to + rolloutPercentage = 3 # Percentage of users to roll out to if counter % 100 > rolloutPercentage # Don't show if user is not part of roll out return cb(null, false) From 4d4dba96777e2c103e9c2e4d316991f8ec783e24 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 19 Oct 2017 15:31:19 +0100 Subject: [PATCH 172/206] Adds some onboarding logging to investigate potential bug --- .../web/app/coffee/Features/Project/ProjectController.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/services/web/app/coffee/Features/Project/ProjectController.coffee b/services/web/app/coffee/Features/Project/ProjectController.coffee index 80c74754e0..4cebc197a7 100644 --- a/services/web/app/coffee/Features/Project/ProjectController.coffee +++ b/services/web/app/coffee/Features/Project/ProjectController.coffee @@ -273,6 +273,7 @@ module.exports = ProjectController = else if event? return cb(null, false) else + logger.log user_id, event, "autocompile onboarding not shown yet to this user" return cb(null, true) }, (err, results)-> if err? From 6fd1d493e3ddf28801c0ba415036760d914fe918 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 19 Oct 2017 15:47:12 +0100 Subject: [PATCH 173/206] Also add logging for track changes --- .../web/app/coffee/Features/Project/ProjectController.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/web/app/coffee/Features/Project/ProjectController.coffee b/services/web/app/coffee/Features/Project/ProjectController.coffee index 4cebc197a7..6e3b84f383 100644 --- a/services/web/app/coffee/Features/Project/ProjectController.coffee +++ b/services/web/app/coffee/Features/Project/ProjectController.coffee @@ -229,6 +229,7 @@ module.exports = ProjectController = else if event? return cb(null, false) else + logger.log user_id, event, "track changes onboarding not shown yet to this user" return cb(null, true) showPerUserTCNotice: (cb) -> cb = underscore.once(cb) @@ -247,6 +248,7 @@ module.exports = ProjectController = else if event? return cb(null, false) else + logger.log user_id, event, "per user track changes notice not shown yet to this user" return cb(null, true) showAutoCompileOnboarding: (cb) -> cb = underscore.once(cb) From ebaa640c516dd2b6646c3c209339c91d7c72ed51 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 19 Oct 2017 16:02:40 +0100 Subject: [PATCH 174/206] Fix log call signature --- .../app/coffee/Features/Project/ProjectController.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/web/app/coffee/Features/Project/ProjectController.coffee b/services/web/app/coffee/Features/Project/ProjectController.coffee index 6e3b84f383..424fe7e248 100644 --- a/services/web/app/coffee/Features/Project/ProjectController.coffee +++ b/services/web/app/coffee/Features/Project/ProjectController.coffee @@ -229,7 +229,7 @@ module.exports = ProjectController = else if event? return cb(null, false) else - logger.log user_id, event, "track changes onboarding not shown yet to this user" + logger.log { user_id, event }, "track changes onboarding not shown yet to this user" return cb(null, true) showPerUserTCNotice: (cb) -> cb = underscore.once(cb) @@ -248,7 +248,7 @@ module.exports = ProjectController = else if event? return cb(null, false) else - logger.log user_id, event, "per user track changes notice not shown yet to this user" + logger.log { user_id, event }, "per user track changes notice not shown yet to this user" return cb(null, true) showAutoCompileOnboarding: (cb) -> cb = underscore.once(cb) @@ -275,7 +275,7 @@ module.exports = ProjectController = else if event? return cb(null, false) else - logger.log user_id, event, "autocompile onboarding not shown yet to this user" + logger.log { user_id, event }, "autocompile onboarding not shown yet to this user" return cb(null, true) }, (err, results)-> if err? From edb2a6c88b803cf9051fbbf63bd4842aec37e66d Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Fri, 20 Oct 2017 10:23:52 +0100 Subject: [PATCH 175/206] Increase rollout to 5% --- .../web/app/coffee/Features/Project/ProjectController.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/app/coffee/Features/Project/ProjectController.coffee b/services/web/app/coffee/Features/Project/ProjectController.coffee index 80c74754e0..8091cf9864 100644 --- a/services/web/app/coffee/Features/Project/ProjectController.coffee +++ b/services/web/app/coffee/Features/Project/ProjectController.coffee @@ -257,7 +257,7 @@ module.exports = ProjectController = timestamp = parseInt(user_id.toString().substring(0, 8), 16) counter = parseInt(user_id.toString().substring(18, 24), 16) - rolloutPercentage = 3 # Percentage of users to roll out to + rolloutPercentage = 5 # Percentage of users to roll out to if counter % 100 > rolloutPercentage # Don't show if user is not part of roll out return cb(null, false) From 033e3cfcc663def1c260bf1b310bcacca3c01054 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Fri, 20 Oct 2017 10:53:08 +0100 Subject: [PATCH 176/206] Add styleguide classes. --- .../stylesheets/app/ol-style-guide.less | 65 +++++++++++++++++++ services/web/public/stylesheets/ol-style.less | 1 + 2 files changed, 66 insertions(+) create mode 100644 services/web/public/stylesheets/app/ol-style-guide.less diff --git a/services/web/public/stylesheets/app/ol-style-guide.less b/services/web/public/stylesheets/app/ol-style-guide.less new file mode 100644 index 0000000000..391bf99a22 --- /dev/null +++ b/services/web/public/stylesheets/app/ol-style-guide.less @@ -0,0 +1,65 @@ +.renderColorSwatchClasses(@colorName) { + @colorVal: @@colorName; + @colorValRed: red(@colorVal); + @colorValGreen: green(@colorVal); + @colorValBlue: blue(@colorVal); + @colorValAsRGB: 'rgb(@{colorValRed}, @{colorValGreen}, @{colorValBlue})'; + + &.@{colorName} { + .color-swatch { + background-color: @colorVal; + } + .color-less-var::before { + content: '@@{colorName}'; + } + .color-hex-val::before { + content: '@{colorVal}'; + } + .color-rgb-val::before { + content: '@{colorValAsRGB}'; + } + } +} + +.color-row { + display: flex; + justify-content: space-between; +} + +.color-box { + background: white; + margin: 10px 4px; + flex: 1; + border-radius: 4px; + .renderColorSwatchClasses(ol-blue-gray-1); + .renderColorSwatchClasses(ol-blue-gray-2); + .renderColorSwatchClasses(ol-blue-gray-3); + .renderColorSwatchClasses(ol-blue-gray-4); + .renderColorSwatchClasses(ol-blue-gray-5); + .renderColorSwatchClasses(ol-blue-gray-6); + .renderColorSwatchClasses(ol-green); + .renderColorSwatchClasses(ol-dark-green); + .renderColorSwatchClasses(ol-blue); + .renderColorSwatchClasses(ol-dark-blue); + .renderColorSwatchClasses(ol-red); + .renderColorSwatchClasses(ol-dark-red); +} + +.color-swatch { + height: 100px; + width: 100px; + margin: 10px auto; + border-radius: 4px; +} + +.color-label { + display: flex; + flex-direction: column; + margin: 0 3px 10px; +} + +.color-label pre { + font-size: 12px; + line-height: 1.8em; + margin: 0 auto; +} \ No newline at end of file diff --git a/services/web/public/stylesheets/ol-style.less b/services/web/public/stylesheets/ol-style.less index ec53e5e8c8..1e48b7284d 100644 --- a/services/web/public/stylesheets/ol-style.less +++ b/services/web/public/stylesheets/ol-style.less @@ -1,3 +1,4 @@ // Core variables and mixins @import "core/ol-variables.less"; +@import "app/ol-style-guide.less"; @import "_style_includes.less"; \ No newline at end of file From 525defb92ddd152769a2df37890036aa190e2921 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Fri, 20 Oct 2017 10:53:26 +0100 Subject: [PATCH 177/206] Add router entry to the new styleguide (WIP). --- .../web/app/coffee/Features/StaticPages/StaticPagesRouter.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/services/web/app/coffee/Features/StaticPages/StaticPagesRouter.coffee b/services/web/app/coffee/Features/StaticPages/StaticPagesRouter.coffee index 07923d48d0..e33cae555a 100644 --- a/services/web/app/coffee/Features/StaticPages/StaticPagesRouter.coffee +++ b/services/web/app/coffee/Features/StaticPages/StaticPagesRouter.coffee @@ -14,6 +14,7 @@ module.exports = webRouter.get '/privacy_policy', HomeController.externalPage("privacy", "Privacy Policy") webRouter.get '/planned_maintenance', HomeController.externalPage("planned_maintenance", "Planned Maintenance") webRouter.get '/style', HomeController.externalPage("style_guide", "Style Guide") + webRouter.get '/ol-style', HomeController.externalPage("ol_style_guide", "Overleaf Style Guide") webRouter.get '/jobs', HomeController.externalPage("jobs", "Jobs") webRouter.get '/track-changes-and-comments-in-latex', HomeController.externalPage("review-features-page", "Review features") From d21137ed730c85addfb092ca2b31bee3e4e43c82 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Fri, 20 Oct 2017 13:27:33 +0100 Subject: [PATCH 178/206] Clean up color box sizes --- services/web/public/stylesheets/app/ol-style-guide.less | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/web/public/stylesheets/app/ol-style-guide.less b/services/web/public/stylesheets/app/ol-style-guide.less index 391bf99a22..30e4e5045f 100644 --- a/services/web/public/stylesheets/app/ol-style-guide.less +++ b/services/web/public/stylesheets/app/ol-style-guide.less @@ -16,6 +16,7 @@ content: '@{colorVal}'; } .color-rgb-val::before { + font-size: 10px; content: '@{colorValAsRGB}'; } } @@ -29,8 +30,8 @@ .color-box { background: white; margin: 10px 4px; - flex: 1; border-radius: 4px; + width: 16.666%; .renderColorSwatchClasses(ol-blue-gray-1); .renderColorSwatchClasses(ol-blue-gray-2); .renderColorSwatchClasses(ol-blue-gray-3); From dcdcbd8388c917a6d456ee5973b9ed076fa0dcc1 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 20 Oct 2017 15:31:10 +0100 Subject: [PATCH 179/206] exit if mock servers fail to start --- .../test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee | 3 +++ .../test/acceptance/coffee/helpers/MockDocstoreApi.coffee | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/services/web/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee b/services/web/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee index 07ecc33b9a..aefcd4513a 100644 --- a/services/web/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee +++ b/services/web/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee @@ -8,5 +8,8 @@ module.exports = MockDocUpdaterApi = app.listen 3003, (error) -> throw error if error? + .on "error", (error) -> + console.error "error starting MockDocUpdaterApi:", error.message + process.exit(1) MockDocUpdaterApi.run() diff --git a/services/web/test/acceptance/coffee/helpers/MockDocstoreApi.coffee b/services/web/test/acceptance/coffee/helpers/MockDocstoreApi.coffee index 5caed105b9..2133d40b9f 100644 --- a/services/web/test/acceptance/coffee/helpers/MockDocstoreApi.coffee +++ b/services/web/test/acceptance/coffee/helpers/MockDocstoreApi.coffee @@ -24,6 +24,10 @@ module.exports = MockDocStoreApi = app.listen 3016, (error) -> throw error if error? + .on "error", (error) -> + console.error "error starting MockDocStoreApi:", error.message + process.exit(1) -MockDocUpdaterApi.run() + +MockDocStoreApi.run() From 624dd26f390092c9f94fc5888418989d10c84ce1 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Fri, 20 Oct 2017 16:20:38 +0100 Subject: [PATCH 180/206] Avoid loading onboarding images on page load. --- .../web/app/views/project/editor/feature-onboarding.pug | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services/web/app/views/project/editor/feature-onboarding.pug b/services/web/app/views/project/editor/feature-onboarding.pug index 833eac5a80..f0047ce45c 100644 --- a/services/web/app/views/project/editor/feature-onboarding.pug +++ b/services/web/app/views/project/editor/feature-onboarding.pug @@ -33,7 +33,7 @@ loop ) source(ng-src="{{ '/img/onboarding/review-panel/open-review.mp4' }}", type="video/mp4") - img(src="/img/onboarding/review-panel/open-review.gif") + img(ng-src="{{ '/img/onboarding/review-panel/open-review.gif' }}", alt="Open review panel demo") div(ng-show="onboarding.innerStep === 2;") video.feat-onboard-video( video-play-state="onboarding.innerStep === 2;" @@ -41,7 +41,7 @@ loop ) source(ng-src="{{ '/img/onboarding/review-panel/commenting.mp4' }}", type="video/mp4") - img(src="/img/onboarding/review-panel/commenting.gif") + img(ng-src="{{ '/img/onboarding/review-panel/commenting.gif' }}", alt="Commenting demo") div(ng-show="onboarding.innerStep === 3;") video.feat-onboard-video( video-play-state="onboarding.innerStep === 3;" @@ -49,7 +49,7 @@ loop ) source(ng-src="{{ '/img/onboarding/review-panel/add-changes.mp4' }}", type="video/mp4") - img(src="/img/onboarding/review-panel/add-changes.gif") + img(ng-src="{{ '/img/onboarding/review-panel/add-changes.gif' }}", alt="Add changes demo") div(ng-show="onboarding.innerStep === 4;") video.feat-onboard-video( video-play-state="onboarding.innerStep === 4;" @@ -57,7 +57,7 @@ loop ) source(ng-src="{{ '/img/onboarding/review-panel/accept-changes.mp4' }}", type="video/mp4") - img(src="/img/onboarding/review-panel/accept-changes.gif") + img(ng-src="{{ '/img/onboarding/review-panel/accept-changes.gif' }}", alt="Accept changes demo") button.btn.btn-primary.feat-onboard-nav-btn( ng-click="gotoNextStep();" ng-disabled="onboarding.innerStep === onboarding.nSteps;") From 8421f6767ad2aea35cfd2d99782c949df2d5869a Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Mon, 23 Oct 2017 13:14:20 +0100 Subject: [PATCH 181/206] Rollout to 10% of users --- .../web/app/coffee/Features/Project/ProjectController.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/app/coffee/Features/Project/ProjectController.coffee b/services/web/app/coffee/Features/Project/ProjectController.coffee index d65e0f6d58..61e3caa02d 100644 --- a/services/web/app/coffee/Features/Project/ProjectController.coffee +++ b/services/web/app/coffee/Features/Project/ProjectController.coffee @@ -259,7 +259,7 @@ module.exports = ProjectController = timestamp = parseInt(user_id.toString().substring(0, 8), 16) counter = parseInt(user_id.toString().substring(18, 24), 16) - rolloutPercentage = 5 # Percentage of users to roll out to + rolloutPercentage = 10 # Percentage of users to roll out to if counter % 100 > rolloutPercentage # Don't show if user is not part of roll out return cb(null, false) From 299112f6e06297449fb855eb944ac870e00c92e8 Mon Sep 17 00:00:00 2001 From: Hayden Faulds Date: Wed, 11 Oct 2017 11:18:34 +0100 Subject: [PATCH 182/206] toggle project history using setting --- .../Features/History/HistoryController.coffee | 9 ++- services/web/config/settings.defaults.coffee | 3 + .../History/HistoryControllerTests.coffee | 59 +++++++++++++------ 3 files changed, 52 insertions(+), 19 deletions(-) diff --git a/services/web/app/coffee/Features/History/HistoryController.coffee b/services/web/app/coffee/Features/History/HistoryController.coffee index d4f42b38b1..6ab7f917eb 100644 --- a/services/web/app/coffee/Features/History/HistoryController.coffee +++ b/services/web/app/coffee/Features/History/HistoryController.coffee @@ -6,7 +6,8 @@ AuthenticationController = require "../Authentication/AuthenticationController" module.exports = HistoryController = proxyToHistoryApi: (req, res, next = (error) ->) -> user_id = AuthenticationController.getLoggedInUserId req - url = settings.apis.trackchanges.url + req.url + url = HistoryController.buildHistoryServiceUrl() + req.url + logger.log url: url, "proxying to track-changes api" getReq = request( url: url @@ -18,3 +19,9 @@ module.exports = HistoryController = getReq.on "error", (error) -> logger.error err: error, "track-changes API error" next(error) + + buildHistoryServiceUrl: () -> + if settings.apis.project_history.enabled + return settings.apis.project_history.url + else + return settings.apis.trackchanges.url diff --git a/services/web/config/settings.defaults.coffee b/services/web/config/settings.defaults.coffee index 2456590709..a013f5c1ec 100644 --- a/services/web/config/settings.defaults.coffee +++ b/services/web/config/settings.defaults.coffee @@ -104,6 +104,9 @@ module.exports = settings = url : "http://localhost:3005" trackchanges: url : "http://localhost:3015" + project_history: + enabled: false + url : "http://localhost:3054" docstore: url : "http://localhost:3016" pubUrl: "http://localhost:3016" diff --git a/services/web/test/UnitTests/coffee/History/HistoryControllerTests.coffee b/services/web/test/UnitTests/coffee/History/HistoryControllerTests.coffee index 577aae6a9d..db5fade45a 100644 --- a/services/web/test/UnitTests/coffee/History/HistoryControllerTests.coffee +++ b/services/web/test/UnitTests/coffee/History/HistoryControllerTests.coffee @@ -22,37 +22,60 @@ describe "HistoryController", -> @next = sinon.stub() @settings.apis = trackchanges: + enabled: false url: "http://trackchanges.example.com" + project_history: + url: "http://project_history.example.com" @proxy = events: {} pipe: sinon.stub() on: (event, handler) -> @events[event] = handler @request.returns @proxy - @HistoryController.proxyToHistoryApi @req, @res, @next describe "successfully", -> - it "should get the user id", -> - @AuthenticationController.getLoggedInUserId - .calledWith(@req) - .should.equal true + describe "with project history enabled", -> + beforeEach -> + @settings.apis.project_history.enabled = true + @HistoryController.proxyToHistoryApi @req, @res, @next - it "should call the track changes api", -> - @request - .calledWith({ - url: "#{@settings.apis.trackchanges.url}#{@req.url}" - method: @req.method - headers: - "X-User-Id": @user_id - }) - .should.equal true + it "should get the user id", -> + @AuthenticationController.getLoggedInUserId + .calledWith(@req) + .should.equal true - it "should pipe the response to the client", -> - @proxy.pipe - .calledWith(@res) - .should.equal true + it "should call the project history api", -> + @request + .calledWith({ + url: "#{@settings.apis.project_history.url}#{@req.url}" + method: @req.method + headers: + "X-User-Id": @user_id + }) + .should.equal true + + it "should pipe the response to the client", -> + @proxy.pipe + .calledWith(@res) + .should.equal true + + describe "with project history disabled", -> + beforeEach -> + @settings.apis.project_history.enabled = false + @HistoryController.proxyToHistoryApi @req, @res, @next + + it "should call the track changes api", -> + @request + .calledWith({ + url: "#{@settings.apis.trackchanges.url}#{@req.url}" + method: @req.method + headers: + "X-User-Id": @user_id + }) + .should.equal true describe "with an error", -> beforeEach -> + @HistoryController.proxyToHistoryApi @req, @res, @next @proxy.events["error"].call(@proxy, @error = new Error("oops")) it "should pass the error up the call chain", -> From ff576c5b9a4068885173bb2b1218f5ba428227c2 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Mon, 23 Oct 2017 15:56:52 +0100 Subject: [PATCH 183/206] Make the text-only title customizable; pick nice colours for the OL version. --- .../web/public/stylesheets/components/navbar.less | 4 ++-- .../public/stylesheets/core/_common-variables.less | 2 ++ .../web/public/stylesheets/core/ol-variables.less | 12 +++++++----- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/services/web/public/stylesheets/components/navbar.less b/services/web/public/stylesheets/components/navbar.less index 113d67e7c2..47d76cc1f2 100755 --- a/services/web/public/stylesheets/components/navbar.less +++ b/services/web/public/stylesheets/components/navbar.less @@ -174,9 +174,9 @@ font-size: 20px; display: inline-block; margin-top: 2px; - color: #666; + color: @navbar-title-color; &:hover, &:active, &:focus { - color: #333; + color: @navbar-title-color-hover; text-decoration: none; } } diff --git a/services/web/public/stylesheets/core/_common-variables.less b/services/web/public/stylesheets/core/_common-variables.less index 5135678a0f..817fa858ef 100644 --- a/services/web/public/stylesheets/core/_common-variables.less +++ b/services/web/public/stylesheets/core/_common-variables.less @@ -815,6 +815,8 @@ @text-small-color: @gray; // Navbar +@navbar-title-color : #666; +@navbar-title-color-hover : #333; @navbar-default-padding : 1rem 2rem; @navbar-brand-width : 180px; @navbar-btn-font-size : @font-size-base * 0.8; diff --git a/services/web/public/stylesheets/core/ol-variables.less b/services/web/public/stylesheets/core/ol-variables.less index 6255d44371..43646ec542 100644 --- a/services/web/public/stylesheets/core/ol-variables.less +++ b/services/web/public/stylesheets/core/ol-variables.less @@ -22,11 +22,13 @@ @ol-type-color : @ol-blue-gray-3; // Navbar customization -@navbar-brand-width : 130px; -@navbar-default-color : #FFF; -@navbar-default-bg : @ol-blue-gray-6; -@navbar-default-border : transparent; -@navbar-brand-image-url : url(/img/ol-brand/overleaf-white.svg); +@navbar-title-color : @ol-blue-gray-1; +@navbar-title-color-hover : @ol-blue-gray-2; +@navbar-brand-width : 130px; +@navbar-default-color : #FFF; +@navbar-default-bg : @ol-blue-gray-6; +@navbar-default-border : transparent; +@navbar-brand-image-url : url(/img/ol-brand/overleaf-white.svg); // Backgrounds @body-bg : #FFF; From c105048e8dd633c05bfa4c9383e033ed017c760d Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Mon, 23 Oct 2017 16:09:43 +0100 Subject: [PATCH 184/206] Make project selection ARIA attributes translatable. --- services/web/app/views/project/list/project-list.pug | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/app/views/project/list/project-list.pug b/services/web/app/views/project/list/project-list.pug index a4295a1843..2233ad7dfd 100644 --- a/services/web/app/views/project/list/project-list.pug +++ b/services/web/app/views/project/list/project-list.pug @@ -128,7 +128,7 @@ input.select-all( select-all, type="checkbox" - aria-label="Select all" + aria-label=translate('select_all_projects') ) span.header.clickable(ng-click="changePredicate('name')") #{translate("title")} i.tablesort.fa(ng-class="getSortIconClass('name')") @@ -149,7 +149,7 @@ type="checkbox", ng-model="project.selected" stop-propagation="click" - aria-label="Select {{ project.name }}" + aria-label=translate('select_project') + " '{{ project.name }}'" ) span a.projectName( From 632e3228e0ed2c116668ae6461dd327f1ca79206 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Mon, 23 Oct 2017 16:13:07 +0100 Subject: [PATCH 185/206] Remove unneeded existence checks. --- .../web/public/coffee/main/project-list/project-list.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/public/coffee/main/project-list/project-list.coffee b/services/web/public/coffee/main/project-list/project-list.coffee index d84c348218..24a65d170b 100644 --- a/services/web/public/coffee/main/project-list/project-list.coffee +++ b/services/web/public/coffee/main/project-list/project-list.coffee @@ -27,8 +27,8 @@ define [ recalculateProjectListHeight = () -> $projListCard = $(".project-list-card") - topOffset = $projListCard?.offset()?.top - cardPadding = $projListCard?.outerHeight() - $projListCard?.height() + topOffset = $projListCard.offset()?.top + cardPadding = $projListCard.outerHeight() - $projListCard.height() bottomOffset = $("footer").outerHeight() height = $window.innerHeight - topOffset - bottomOffset - cardPadding $scope.projectListHeight = height From fb121494e66cdadbf2a7347fe5914be3b6db18ce Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Mon, 23 Oct 2017 16:15:30 +0100 Subject: [PATCH 186/206] Remove commented code. --- services/web/public/stylesheets/app/project-list.less | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less index 0aa44d1188..3a70052aea 100644 --- a/services/web/public/stylesheets/app/project-list.less +++ b/services/web/public/stylesheets/app/project-list.less @@ -155,7 +155,6 @@ ul.folders-menu { } } > li.active { - //border-right: 4px solid @red; border-radius: @sidebar-active-border-radius; > a { background-color: @sidebar-active-bg; @@ -267,11 +266,7 @@ ul.structured-list { li { border-bottom: 1px solid @structured-list-border-color; padding: (@line-height-computed / 4) 0; - // &:first-child { - // .header { - // font-size: 1rem; - // } - // } + &:last-child { border-bottom: 0 none; } From 5f5731eca2ee1eca5bb4d571ce874260319e9b41 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Mon, 23 Oct 2017 16:18:16 +0100 Subject: [PATCH 187/206] Rename content margin top variable to content margin vertical, as it affects both top and bottom. --- services/web/public/stylesheets/app/project-list.less | 8 ++++---- .../web/public/stylesheets/core/_common-variables.less | 4 +--- services/web/public/stylesheets/core/scaffolding.less | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less index 3a70052aea..ebdf667f90 100644 --- a/services/web/public/stylesheets/app/project-list.less +++ b/services/web/public/stylesheets/app/project-list.less @@ -50,8 +50,8 @@ } .project-list-sidebar { background-color: @sidebar-bg; - padding-top: @content-margin-top; - padding-bottom: @content-margin-top; + padding-top: @content-margin-vertical; + padding-bottom: @content-margin-vertical; } .project-list-sidebar when (@is-overleaf) { @@ -62,8 +62,8 @@ } .project-list-main { - padding-top: @content-margin-top; - padding-bottom: @content-margin-top; + padding-top: @content-margin-vertical; + padding-bottom: @content-margin-vertical; height: 100%; overflow: hidden; } diff --git a/services/web/public/stylesheets/core/_common-variables.less b/services/web/public/stylesheets/core/_common-variables.less index 817fa858ef..3d3cc383a8 100644 --- a/services/web/public/stylesheets/core/_common-variables.less +++ b/services/web/public/stylesheets/core/_common-variables.less @@ -788,9 +788,7 @@ //** Horizontal offset for forms and lists. @component-offset-horizontal: 180px; -@content-margin-top: @line-height-computed; -@content-margin-top: @line-height-computed; - +@content-margin-vertical: @line-height-computed; @left-menu-width: 260px; @left-menu-animation-duration: 0.35s; diff --git a/services/web/public/stylesheets/core/scaffolding.less b/services/web/public/stylesheets/core/scaffolding.less index 41c70bb11d..0b64cb20c4 100755 --- a/services/web/public/stylesheets/core/scaffolding.less +++ b/services/web/public/stylesheets/core/scaffolding.less @@ -144,8 +144,8 @@ hr { } .content { - padding-top: @content-margin-top; - padding-bottom: @content-margin-top; + padding-top: @content-margin-vertical; + padding-bottom: @content-margin-vertical; } .content-alt { From b834794ba67bbadb1495dbdbcc1f45aa9565053b Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Mon, 23 Oct 2017 16:21:42 +0100 Subject: [PATCH 188/206] Remove more commented-out code. --- services/web/public/stylesheets/app/project-list.less | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less index ebdf667f90..eda35e3da5 100644 --- a/services/web/public/stylesheets/app/project-list.less +++ b/services/web/public/stylesheets/app/project-list.less @@ -289,10 +289,7 @@ ul.structured-list { .header when (@is-overleaf = false) { text-transform: uppercase; } - // .select-item, .select-all { - // display: inline-block; - // margin: 0 (@line-height-computed / 4); - // } + .select-item, .select-all { position: absolute; left: @line-height-computed; From 83a1b2f5a00067ff2c72efe06b9c4ecf780b73d8 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Mon, 23 Oct 2017 16:58:02 +0100 Subject: [PATCH 189/206] Reenable source maps. --- services/web/Gruntfile.coffee | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/services/web/Gruntfile.coffee b/services/web/Gruntfile.coffee index c20912e0ae..3daeec1f5a 100644 --- a/services/web/Gruntfile.coffee +++ b/services/web/Gruntfile.coffee @@ -137,13 +137,17 @@ module.exports = (grunt) -> less: app: - # options: - # sourceMap: true + options: + sourceMap: true + sourceMapFilename: "public/stylesheets/style.map" + sourceMapBasepath: "public/stylesheets" files: "public/stylesheets/style.css": "public/stylesheets/style.less" ol: - # options: - # sourceMap: true + options: + sourceMap: true + sourceMapFilename: "public/stylesheets/ol-style.map" + sourceMapBasepath: "public/stylesheets" files: "public/stylesheets/ol-style.css": "public/stylesheets/ol-style.less" From c437eb37d5188841a4e8a8e5d7d15d2da45455ea Mon Sep 17 00:00:00 2001 From: Hayden Faulds Date: Tue, 24 Oct 2017 09:47:14 +0100 Subject: [PATCH 190/206] handle missing settings.apis.project_history --- .../web/app/coffee/Features/History/HistoryController.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/app/coffee/Features/History/HistoryController.coffee b/services/web/app/coffee/Features/History/HistoryController.coffee index 6ab7f917eb..5fe9cc37b0 100644 --- a/services/web/app/coffee/Features/History/HistoryController.coffee +++ b/services/web/app/coffee/Features/History/HistoryController.coffee @@ -21,7 +21,7 @@ module.exports = HistoryController = next(error) buildHistoryServiceUrl: () -> - if settings.apis.project_history.enabled + if settings.apis.project_history?.enabled return settings.apis.project_history.url else return settings.apis.trackchanges.url From 670b183c8ea40896a0998f2c3ba710733f6c23e8 Mon Sep 17 00:00:00 2001 From: Hayden Faulds Date: Tue, 24 Oct 2017 11:48:21 +0100 Subject: [PATCH 191/206] initialize project history on creation --- .../Features/History/HistoryController.coffee | 23 ++++++ .../Project/ProjectCreationHandler.coffee | 31 ++++---- .../History/HistoryControllerTests.coffee | 78 +++++++++++++++++-- .../ProjectCreationHandlerTests.coffee | 35 ++++++--- 4 files changed, 138 insertions(+), 29 deletions(-) diff --git a/services/web/app/coffee/Features/History/HistoryController.coffee b/services/web/app/coffee/Features/History/HistoryController.coffee index 5fe9cc37b0..c7c869d674 100644 --- a/services/web/app/coffee/Features/History/HistoryController.coffee +++ b/services/web/app/coffee/Features/History/HistoryController.coffee @@ -4,6 +4,29 @@ settings = require "settings-sharelatex" AuthenticationController = require "../Authentication/AuthenticationController" module.exports = HistoryController = + initializeProject: (callback = (error, history_id) ->) -> + return callback() if !settings.apis.project_history?.enabled + request.post { + url: "#{settings.apis.project_history.url}/project" + }, (error, res, body)-> + return callback(error) if error? + + if res.statusCode >= 200 and res.statusCode < 300 + try + project = JSON.parse(body) + catch error + return callback(error) + + overleaf_id = project?.project?.id + if !overleaf_id + error = new Error("project-history did not provide an id", project) + return callback(error) + + callback null, { overleaf_id } + else + error = new Error("project-history returned a non-success status code: #{res.statusCode}") + callback error + proxyToHistoryApi: (req, res, next = (error) ->) -> user_id = AuthenticationController.getLoggedInUserId req url = HistoryController.buildHistoryServiceUrl() + req.url diff --git a/services/web/app/coffee/Features/Project/ProjectCreationHandler.coffee b/services/web/app/coffee/Features/Project/ProjectCreationHandler.coffee index 41b40c11b9..395ab92c51 100644 --- a/services/web/app/coffee/Features/Project/ProjectCreationHandler.coffee +++ b/services/web/app/coffee/Features/Project/ProjectCreationHandler.coffee @@ -2,11 +2,12 @@ logger = require('logger-sharelatex') async = require("async") metrics = require('metrics-sharelatex') Settings = require('settings-sharelatex') -ObjectId = require('mongoose').Types.ObjectId +ObjectId = require('mongoose').Types.ObjectId Project = require('../../models/Project').Project Folder = require('../../models/Folder').Folder ProjectEntityHandler = require('./ProjectEntityHandler') ProjectDetailsHandler = require('./ProjectDetailsHandler') +HistoryController = require('../History/HistoryController') User = require('../../models/User').User fs = require('fs') Path = require "path" @@ -19,18 +20,22 @@ module.exports = ProjectCreationHandler = ProjectDetailsHandler.validateProjectName projectName, (error) -> return callback(error) if error? logger.log owner_id:owner_id, projectName:projectName, "creating blank project" - rootFolder = new Folder {'name':'rootFolder'} - project = new Project - owner_ref : new ObjectId(owner_id) - name : projectName - if Settings.currentImageName? - project.imageName = Settings.currentImageName - project.rootFolder[0] = rootFolder - User.findById owner_id, "ace.spellCheckLanguage", (err, user)-> - project.spellCheckLanguage = user.ace.spellCheckLanguage - project.save (err)-> - return callback(err) if err? - callback err, project + HistoryController.initializeProject (error, history) -> + return callback(error) if error? + rootFolder = new Folder {'name':'rootFolder'} + project = new Project + owner_ref : new ObjectId(owner_id) + name : projectName + if history?.overleaf_id? + project.overleaf.id = history.overleaf_id + if Settings.currentImageName? + project.imageName = Settings.currentImageName + project.rootFolder[0] = rootFolder + User.findById owner_id, "ace.spellCheckLanguage", (err, user)-> + project.spellCheckLanguage = user.ace.spellCheckLanguage + project.save (err)-> + return callback(err) if err? + callback err, project createBasicProject : (owner_id, projectName, callback = (error, project) ->)-> self = @ diff --git a/services/web/test/UnitTests/coffee/History/HistoryControllerTests.coffee b/services/web/test/UnitTests/coffee/History/HistoryControllerTests.coffee index db5fade45a..ae10a34739 100644 --- a/services/web/test/UnitTests/coffee/History/HistoryControllerTests.coffee +++ b/services/web/test/UnitTests/coffee/History/HistoryControllerTests.coffee @@ -6,6 +6,7 @@ SandboxedModule = require('sandboxed-module') describe "HistoryController", -> beforeEach -> + @callback = sinon.stub() @user_id = "user-id-123" @AuthenticationController = getLoggedInUserId: sinon.stub().returns(@user_id) @@ -14,18 +15,18 @@ describe "HistoryController", -> "settings-sharelatex": @settings = {} "logger-sharelatex": @logger = {log: sinon.stub(), error: sinon.stub()} "../Authentication/AuthenticationController": @AuthenticationController + @settings.apis = + trackchanges: + enabled: false + url: "http://trackchanges.example.com" + project_history: + url: "http://project_history.example.com" describe "proxyToHistoryApi", -> beforeEach -> @req = { url: "/mock/url", method: "POST" } @res = "mock-res" @next = sinon.stub() - @settings.apis = - trackchanges: - enabled: false - url: "http://trackchanges.example.com" - project_history: - url: "http://project_history.example.com" @proxy = events: {} pipe: sinon.stub() @@ -80,3 +81,68 @@ describe "HistoryController", -> it "should pass the error up the call chain", -> @next.calledWith(@error).should.equal true + + describe "initializeProject", -> + describe "with project history enabled", -> + beforeEach -> + @settings.apis.project_history.enabled = true + + describe "project history returns a successful response", -> + beforeEach -> + @overleaf_id = 1234 + @res = statusCode: 200 + @body = JSON.stringify(project: id: @overleaf_id) + @request.post = sinon.stub().callsArgWith(1, null, @res, @body) + + @HistoryController.initializeProject @callback + + it "should call the project history api", -> + @request.post.calledWith( + url: "#{@settings.apis.project_history.url}/project" + ).should.equal true + + it "should return the callback with the overleaf id", -> + @callback.calledWithExactly(null, { @overleaf_id }).should.equal true + + describe "project history returns a response without the project id", -> + beforeEach -> + @res = statusCode: 200 + @body = JSON.stringify(project: {}) + @request.post = sinon.stub().callsArgWith(1, null, @res, @body) + + @HistoryController.initializeProject @callback + + it "should return the callback with an error", -> + @callback + .calledWith(sinon.match.has("message", "project-history did not provide an id")) + .should.equal true + + describe "project history returns a unsuccessful response", -> + beforeEach -> + @res = statusCode: 404 + @request.post = sinon.stub().callsArgWith(1, null, @res) + + @HistoryController.initializeProject @callback + + it "should return the callback with an error", -> + @callback + .calledWith(sinon.match.has("message", "project-history returned a non-success status code: 404")) + .should.equal true + + describe "project history errors", -> + beforeEach -> + @error = sinon.stub() + @request.post = sinon.stub().callsArgWith(1, @error) + + @HistoryController.initializeProject @callback + + it "should return the callback with the error", -> + @callback.calledWithExactly(@error).should.equal true + + describe "with project history disabled", -> + beforeEach -> + @settings.apis.project_history.enabled = false + @HistoryController.initializeProject @callback + + it "should return the callback", -> + @callback.calledWithExactly().should.equal true diff --git a/services/web/test/UnitTests/coffee/Project/ProjectCreationHandlerTests.coffee b/services/web/test/UnitTests/coffee/Project/ProjectCreationHandlerTests.coffee index 8aa750b80a..fc590c2bf5 100644 --- a/services/web/test/UnitTests/coffee/Project/ProjectCreationHandlerTests.coffee +++ b/services/web/test/UnitTests/coffee/Project/ProjectCreationHandlerTests.coffee @@ -22,6 +22,7 @@ describe 'ProjectCreationHandler', -> @._id = project_id @owner_ref = options.owner_ref @name = options.name + @overleaf = {} save: sinon.stub().callsArg(0) rootFolder:[{ _id: rootFolderId @@ -36,11 +37,13 @@ describe 'ProjectCreationHandler', -> setRootDoc: sinon.stub().callsArg(2) @ProjectDetailsHandler = validateProjectName: sinon.stub().yields() + @HistoryController = + initializeProject: sinon.stub().callsArg(0) - @user = + @user = first_name:"first name here" last_name:"last name here" - ace: + ace: spellCheckLanguage:"de" @User = findById:sinon.stub().callsArgWith(2, null, @user) @@ -49,6 +52,7 @@ describe 'ProjectCreationHandler', -> '../../models/User': User:@User '../../models/Project':{Project:@ProjectModel} '../../models/Folder':{Folder:@FolderModel} + '../History/HistoryController': @HistoryController './ProjectEntityHandler':@ProjectEntityHandler "./ProjectDetailsHandler":@ProjectDetailsHandler "settings-sharelatex": @Settings = {} @@ -68,24 +72,35 @@ describe 'ProjectCreationHandler', -> @handler.createBlankProject ownerId, projectName, => @ProjectModel::save.called.should.equal true done() - + it "should return the project in the callback", (done)-> @handler.createBlankProject ownerId, projectName, (err, project)-> project.name.should.equal projectName (project.owner_ref + "").should.equal ownerId done() + it "should initialize the project history", (done)-> + @handler.createBlankProject ownerId, projectName, done + @HistoryController.initializeProject.calledWith().should.equal true + + it "should set the overleaf id", (done)-> + overleaf_id = 1234 + @HistoryController.initializeProject = sinon.stub().callsArgWith(0, null, { overleaf_id }) + @handler.createBlankProject ownerId, projectName, (err, project)-> + project.overleaf.id.should.equal overleaf_id + done() + it "should set the language from the user", (done)-> @handler.createBlankProject ownerId, projectName, (err, project)-> project.spellCheckLanguage.should.equal "de" done() - + it "should set the imageName to currentImageName if set", (done) -> @Settings.currentImageName = "mock-image-name" @handler.createBlankProject ownerId, projectName, (err, project)=> project.imageName.should.equal @Settings.currentImageName done() - + it "should not set the imageName if no currentImageName", (done) -> @Settings.currentImageName = null @handler.createBlankProject ownerId, projectName, (err, project)=> @@ -96,21 +111,21 @@ describe 'ProjectCreationHandler', -> beforeEach -> @ProjectModel::save = sinon.stub().callsArgWith(0, new Error("something went wrong")) @handler.createBlankProject ownerId, projectName, @callback - + it 'should return the error to the callback', -> should.exist @callback.args[0][0] - + describe "with an invalid name", -> beforeEach -> @ProjectDetailsHandler.validateProjectName = sinon.stub().yields(new Error("bad name")) @handler.createBlankProject ownerId, projectName, @callback - + it 'should return the error to the callback', -> should.exist @callback.args[0][0] - + it 'should not try to create the project', -> @ProjectModel::save.called.should.equal false - + describe 'Creating a basic project', -> beforeEach -> From 9b128591b149680e004bf27f9913152d0d722f10 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Tue, 24 Oct 2017 16:03:59 +0100 Subject: [PATCH 192/206] Enable sourcemaps on dev and prod environments (both minified and normal). --- services/web/Gruntfile.coffee | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/services/web/Gruntfile.coffee b/services/web/Gruntfile.coffee index 3daeec1f5a..1f81d2f89a 100644 --- a/services/web/Gruntfile.coffee +++ b/services/web/Gruntfile.coffee @@ -28,8 +28,10 @@ module.exports = (grunt) -> exec: run: command:"node app.js | ./node_modules/logger-sharelatex/node_modules/bunyan/bin/bunyan --color" - cssmin: - command:"node_modules/clean-css/bin/cleancss --s0 -o public/stylesheets/style.css public/stylesheets/style.css" + cssmin_sl: + command:"node_modules/clean-css/bin/cleancss --s0 --source-map -o public/stylesheets/style.css public/stylesheets/style.css" + cssmin_ol: + command:"node_modules/clean-css/bin/cleancss --s0 --source-map -o public/stylesheets/ol-style.css public/stylesheets/ol-style.css" watch: @@ -139,26 +141,29 @@ module.exports = (grunt) -> app: options: sourceMap: true - sourceMapFilename: "public/stylesheets/style.map" + sourceMapFilename: "public/stylesheets/style.css.map" sourceMapBasepath: "public/stylesheets" files: "public/stylesheets/style.css": "public/stylesheets/style.less" ol: options: sourceMap: true - sourceMapFilename: "public/stylesheets/ol-style.map" + sourceMapFilename: "public/stylesheets/ol-style.css.map" sourceMapBasepath: "public/stylesheets" files: "public/stylesheets/ol-style.css": "public/stylesheets/ol-style.less" postcss: options: - map: true, + map: + prev: "public/stylesheets/" + inline: false + sourcesContent: true processors: [ require('autoprefixer')({browsers: [ 'last 2 versions', 'ie >= 10' ]}) ] dist: - src: 'public/stylesheets/style.css' + src: [ "public/stylesheets/style.css", "public/stylesheets/ol-style.css" ] env: run: @@ -389,7 +394,7 @@ module.exports = (grunt) -> grunt.registerTask 'compile:server', 'Compile the server side coffee script', ['clean:app', 'coffee:app', 'coffee:app_dir', 'compile:modules:server'] grunt.registerTask 'compile:client', 'Compile the client side coffee script', ['coffee:client', 'coffee:sharejs', 'wrap_sharejs', "compile:modules:client", 'compile:modules:inject_clientside_includes'] grunt.registerTask 'compile:css', 'Compile the less files to css', ['less', 'postcss:dist'] - grunt.registerTask 'compile:minify', 'Concat and minify the client side js', ['requirejs', "file_append", "exec:cssmin",] + grunt.registerTask 'compile:minify', 'Concat and minify the client side js and css', ['requirejs', "file_append", "exec:cssmin_sl", "exec:cssmin_ol"] grunt.registerTask 'compile:unit_tests', 'Compile the unit tests', ['clean:unit_tests', 'coffee:unit_tests'] grunt.registerTask 'compile:acceptance_tests', 'Compile the acceptance tests', ['clean:acceptance_tests', 'coffee:acceptance_tests'] grunt.registerTask 'compile:smoke_tests', 'Compile the smoke tests', ['coffee:smoke_tests'] From 7c5f6539ae2ab5706db1ffdbc048ac3dcc09dcf7 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Tue, 24 Oct 2017 16:24:27 +0100 Subject: [PATCH 193/206] Remove translations from shrinkwrap. --- services/web/npm-shrinkwrap.json | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/services/web/npm-shrinkwrap.json b/services/web/npm-shrinkwrap.json index 277e022779..6ece76837e 100644 --- a/services/web/npm-shrinkwrap.json +++ b/services/web/npm-shrinkwrap.json @@ -6773,29 +6773,6 @@ } } }, - "translations-sharelatex": { - "version": "git+https://github.com/sharelatex/translations-sharelatex.git#12d5cefc591bcc0b4e54b5ce9acfbd980208b9e1", - "dev": true, - "requires": { - "async": "2.5.0", - "coffee-script": "1.12.4", - "i18next": "1.7.10", - "onesky": "0.1.6", - "sanitize-html": "1.14.1", - "underscore": "1.6.0" - }, - "dependencies": { - "async": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", - "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==", - "dev": true, - "requires": { - "lodash": "4.17.4" - } - } - } - }, "tsscmp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.5.tgz", From cbb79e04ce3413cef087c1156186acd2ecf455a7 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Wed, 25 Oct 2017 10:13:42 +0100 Subject: [PATCH 194/206] Ignore CSS source maps. --- services/web/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/services/web/.gitignore b/services/web/.gitignore index 9c2a272a7a..b416345461 100644 --- a/services/web/.gitignore +++ b/services/web/.gitignore @@ -61,6 +61,7 @@ public/js/utils/ public/stylesheets/style.css public/stylesheets/ol-style.css +public/stylesheets/*.map public/brand/plans.css public/minjs/ From 2ef7fd1d0a646610066688d4175eaf207cc59f78 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Wed, 25 Oct 2017 10:14:47 +0100 Subject: [PATCH 195/206] Position the notifications lion relatively to the body. --- services/web/app/views/project/list.pug | 78 +++++++++---------- .../public/stylesheets/app/project-list.less | 2 +- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/services/web/app/views/project/list.pug b/services/web/app/views/project/list.pug index 7cab946d98..c2fcb53a31 100644 --- a/services/web/app/views/project/list.pug +++ b/services/web/app/views/project/list.pug @@ -15,47 +15,47 @@ block content } }; + .announcements( + ng-controller="AnnouncementsController" + ng-class="{ 'announcements-open': ui.isOpen }" + ng-cloak + ) + .announcements-backdrop( + ng-if="ui.isOpen" + ng-click="toggleAnnouncementsUI();" + ) + a.announcements-btn( + href + ng-if="announcements.length" + ng-click="toggleAnnouncementsUI();" + ng-class="{ 'announcements-btn-open': ui.isOpen, 'announcements-btn-has-new': ui.newItems }" + ) + span.announcements-badge(ng-if="ui.newItems") {{ ui.newItems }} + .announcements-body( + ng-if="ui.isOpen" + ) + .announcements-scroller + .announcement( + ng-repeat="announcement in announcements | filter:(ui.newItems ? { read: false } : '') track by announcement.id" + ) + h2.announcement-header {{ announcement.title }} + p.announcement-description(ng-bind-html="announcement.excerpt") + .announcement-meta + p.announcement-date {{ announcement.date | date:"longDate" }} + a.announcement-link( + ng-href="{{ announcement.url }}" + ng-click="logAnnouncementClick()", + target="_blank" + ) Read more + div.text-center( + ng-if="ui.newItems > 0 && ui.newItems < announcements.length" + ) + a.btn.btn-default.btn-sm( + href + ng-click="showAll();" + ) Show all .content.content-alt.project-list-page(ng-controller="ProjectPageController") .project-list-content - .announcements( - ng-controller="AnnouncementsController" - ng-class="{ 'announcements-open': ui.isOpen }" - ng-cloak - ) - .announcements-backdrop( - ng-if="ui.isOpen" - ng-click="toggleAnnouncementsUI();" - ) - a.announcements-btn( - href - ng-if="announcements.length" - ng-click="toggleAnnouncementsUI();" - ng-class="{ 'announcements-btn-open': ui.isOpen, 'announcements-btn-has-new': ui.newItems }" - ) - span.announcements-badge(ng-if="ui.newItems") {{ ui.newItems }} - .announcements-body( - ng-if="ui.isOpen" - ) - .announcements-scroller - .announcement( - ng-repeat="announcement in announcements | filter:(ui.newItems ? { read: false } : '') track by announcement.id" - ) - h2.announcement-header {{ announcement.title }} - p.announcement-description(ng-bind-html="announcement.excerpt") - .announcement-meta - p.announcement-date {{ announcement.date | date:"longDate" }} - a.announcement-link( - ng-href="{{ announcement.url }}" - ng-click="logAnnouncementClick()", - target="_blank" - ) Read more - div.text-center( - ng-if="ui.newItems > 0 && ui.newItems < announcements.length" - ) - a.btn.btn-default.btn-sm( - href - ng-click="showAll();" - ) Show all .row.project-list-row(ng-cloak) .project-list-container(ng-if="projects.length > 0") diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less index eda35e3da5..af33bbbf46 100644 --- a/services/web/public/stylesheets/app/project-list.less +++ b/services/web/public/stylesheets/app/project-list.less @@ -421,7 +421,7 @@ ul.project-list { .announcements { position: absolute; - bottom: 0; + bottom: @footer-height; right: 0; height: 150px; width: 100%; From 5cafee4879a45bdbb8950e5b410d7bfae7bcba63 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Wed, 25 Oct 2017 10:33:20 +0100 Subject: [PATCH 196/206] Constrain announcements height. --- services/web/public/stylesheets/app/project-list.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less index af33bbbf46..5e7442e1ce 100644 --- a/services/web/public/stylesheets/app/project-list.less +++ b/services/web/public/stylesheets/app/project-list.less @@ -503,7 +503,7 @@ ul.project-list { margin-right: 95px; bottom: 30px; width: 700px; - max-height: 52%; + max-height: 40%; min-height: 100px; background: #FFF; z-index: 1; From 97c5e33f547834c2cac2ec244493aae634451d23 Mon Sep 17 00:00:00 2001 From: Paulo Reis Date: Thu, 26 Oct 2017 15:47:39 +0100 Subject: [PATCH 197/206] Use percent-based sizing for body min-height; add explicit height to the html element. --- services/web/public/stylesheets/core/scaffolding.less | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/web/public/stylesheets/core/scaffolding.less b/services/web/public/stylesheets/core/scaffolding.less index 0b64cb20c4..22d7225b95 100755 --- a/services/web/public/stylesheets/core/scaffolding.less +++ b/services/web/public/stylesheets/core/scaffolding.less @@ -21,6 +21,7 @@ html { //font-size: 62.5%; -webkit-tap-highlight-color: rgba(0,0,0,0); + height: 100%; } body { @@ -29,7 +30,7 @@ body { line-height: @line-height-base; color: @text-color; background-color: @body-bg; - min-height: 100vh; + min-height: 100%; position: relative; padding-top: @header-height; padding-bottom: @footer-height; From 20ea9918c9753fb1ec193d297329898bfc376bf7 Mon Sep 17 00:00:00 2001 From: Hayden Faulds Date: Fri, 27 Oct 2017 14:56:16 +0100 Subject: [PATCH 198/206] allow overleafId to be passed in to avoid history initialization --- .../Project/ProjectCreationHandler.coffee | 43 +++++++++++-------- .../ProjectCreationHandlerTests.coffee | 17 +++++--- 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/services/web/app/coffee/Features/Project/ProjectCreationHandler.coffee b/services/web/app/coffee/Features/Project/ProjectCreationHandler.coffee index 395ab92c51..dfc6313983 100644 --- a/services/web/app/coffee/Features/Project/ProjectCreationHandler.coffee +++ b/services/web/app/coffee/Features/Project/ProjectCreationHandler.coffee @@ -15,27 +15,36 @@ _ = require "underscore" module.exports = ProjectCreationHandler = - createBlankProject : (owner_id, projectName, callback = (error, project) ->)-> + createBlankProject : (owner_id, projectName, overleafId, callback = (error, project) ->)-> metrics.inc("project-creation") + if arguments.length == 3 + callback = overleafId + overleafId = null + ProjectDetailsHandler.validateProjectName projectName, (error) -> return callback(error) if error? logger.log owner_id:owner_id, projectName:projectName, "creating blank project" - HistoryController.initializeProject (error, history) -> - return callback(error) if error? - rootFolder = new Folder {'name':'rootFolder'} - project = new Project - owner_ref : new ObjectId(owner_id) - name : projectName - if history?.overleaf_id? - project.overleaf.id = history.overleaf_id - if Settings.currentImageName? - project.imageName = Settings.currentImageName - project.rootFolder[0] = rootFolder - User.findById owner_id, "ace.spellCheckLanguage", (err, user)-> - project.spellCheckLanguage = user.ace.spellCheckLanguage - project.save (err)-> - return callback(err) if err? - callback err, project + if overleafId? + ProjectCreationHandler._createBlankProject owner_id, projectName, overleafId, callback + else + HistoryController.initializeProject (error, history) -> + return callback(error) if error? + ProjectCreationHandler._createBlankProject owner_id, projectName, history.overleaf_id, callback + + _createBlankProject : (owner_id, projectName, overleafId, callback = (error, project) ->)-> + rootFolder = new Folder {'name':'rootFolder'} + project = new Project + owner_ref : new ObjectId(owner_id) + name : projectName + project.overleaf.id = overleafId + if Settings.currentImageName? + project.imageName = Settings.currentImageName + project.rootFolder[0] = rootFolder + User.findById owner_id, "ace.spellCheckLanguage", (err, user)-> + project.spellCheckLanguage = user.ace.spellCheckLanguage + project.save (err)-> + return callback(err) if err? + callback err, project createBasicProject : (owner_id, projectName, callback = (error, project) ->)-> self = @ diff --git a/services/web/test/UnitTests/coffee/Project/ProjectCreationHandlerTests.coffee b/services/web/test/UnitTests/coffee/Project/ProjectCreationHandlerTests.coffee index fc590c2bf5..e9a746fc3d 100644 --- a/services/web/test/UnitTests/coffee/Project/ProjectCreationHandlerTests.coffee +++ b/services/web/test/UnitTests/coffee/Project/ProjectCreationHandlerTests.coffee @@ -64,10 +64,11 @@ describe 'ProjectCreationHandler', -> describe 'Creating a Blank project', -> beforeEach -> + @overleaf_id = 1234 + @HistoryController.initializeProject = sinon.stub().callsArgWith(0, null, { @overleaf_id }) @ProjectModel::save = sinon.stub().callsArg(0) describe "successfully", -> - it "should save the project", (done)-> @handler.createBlankProject ownerId, projectName, => @ProjectModel::save.called.should.equal true @@ -79,14 +80,18 @@ describe 'ProjectCreationHandler', -> (project.owner_ref + "").should.equal ownerId done() - it "should initialize the project history", (done)-> + it "should initialize the project overleaf if history id not provided", (done)-> @handler.createBlankProject ownerId, projectName, done @HistoryController.initializeProject.calledWith().should.equal true - it "should set the overleaf id", (done)-> - overleaf_id = 1234 - @HistoryController.initializeProject = sinon.stub().callsArgWith(0, null, { overleaf_id }) - @handler.createBlankProject ownerId, projectName, (err, project)-> + it "should set the overleaf id if overleaf id not provided", (done)-> + @handler.createBlankProject ownerId, projectName, (err, project)=> + project.overleaf.id.should.equal @overleaf_id + done() + + it "should set the overleaf id if overleaf id provided", (done)-> + overleaf_id = 2345 + @handler.createBlankProject ownerId, projectName, overleaf_id, (err, project)-> project.overleaf.id.should.equal overleaf_id done() From 19bc145ff4227ae8ea8b1b4c847247dc403fb2d9 Mon Sep 17 00:00:00 2001 From: Hayden Faulds Date: Fri, 27 Oct 2017 15:24:18 +0100 Subject: [PATCH 199/206] separate overleaf.id from overleaf.history.id --- .../Features/Project/ProjectCreationHandler.coffee | 14 +++++++------- services/web/app/coffee/models/Project.coffee | 2 ++ .../Project/ProjectCreationHandlerTests.coffee | 7 ++++--- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/services/web/app/coffee/Features/Project/ProjectCreationHandler.coffee b/services/web/app/coffee/Features/Project/ProjectCreationHandler.coffee index dfc6313983..1bf84e6751 100644 --- a/services/web/app/coffee/Features/Project/ProjectCreationHandler.coffee +++ b/services/web/app/coffee/Features/Project/ProjectCreationHandler.coffee @@ -15,28 +15,28 @@ _ = require "underscore" module.exports = ProjectCreationHandler = - createBlankProject : (owner_id, projectName, overleafId, callback = (error, project) ->)-> + createBlankProject : (owner_id, projectName, projectHistoryId, callback = (error, project) ->)-> metrics.inc("project-creation") if arguments.length == 3 - callback = overleafId - overleafId = null + callback = projectHistoryId + projectHistoryId = null ProjectDetailsHandler.validateProjectName projectName, (error) -> return callback(error) if error? logger.log owner_id:owner_id, projectName:projectName, "creating blank project" - if overleafId? - ProjectCreationHandler._createBlankProject owner_id, projectName, overleafId, callback + if projectHistoryId? + ProjectCreationHandler._createBlankProject owner_id, projectName, projectHistoryId, callback else HistoryController.initializeProject (error, history) -> return callback(error) if error? ProjectCreationHandler._createBlankProject owner_id, projectName, history.overleaf_id, callback - _createBlankProject : (owner_id, projectName, overleafId, callback = (error, project) ->)-> + _createBlankProject : (owner_id, projectName, projectHistoryId, callback = (error, project) ->)-> rootFolder = new Folder {'name':'rootFolder'} project = new Project owner_ref : new ObjectId(owner_id) name : projectName - project.overleaf.id = overleafId + project.overleaf.history.id = projectHistoryId if Settings.currentImageName? project.imageName = Settings.currentImageName project.rootFolder[0] = rootFolder diff --git a/services/web/app/coffee/models/Project.coffee b/services/web/app/coffee/models/Project.coffee index f4f3cbbdc4..d60fb1be22 100644 --- a/services/web/app/coffee/models/Project.coffee +++ b/services/web/app/coffee/models/Project.coffee @@ -38,6 +38,8 @@ ProjectSchema = new Schema imported_at_ver_id : { type: Number } token : { type: String } read_token : { type: String } + history : + id : { type: Number } ProjectSchema.statics.getProject = (project_or_id, fields, callback)-> if project_or_id._id? diff --git a/services/web/test/UnitTests/coffee/Project/ProjectCreationHandlerTests.coffee b/services/web/test/UnitTests/coffee/Project/ProjectCreationHandlerTests.coffee index e9a746fc3d..5c32db63d3 100644 --- a/services/web/test/UnitTests/coffee/Project/ProjectCreationHandlerTests.coffee +++ b/services/web/test/UnitTests/coffee/Project/ProjectCreationHandlerTests.coffee @@ -22,7 +22,8 @@ describe 'ProjectCreationHandler', -> @._id = project_id @owner_ref = options.owner_ref @name = options.name - @overleaf = {} + @overleaf = + history: {} save: sinon.stub().callsArg(0) rootFolder:[{ _id: rootFolderId @@ -86,13 +87,13 @@ describe 'ProjectCreationHandler', -> it "should set the overleaf id if overleaf id not provided", (done)-> @handler.createBlankProject ownerId, projectName, (err, project)=> - project.overleaf.id.should.equal @overleaf_id + project.overleaf.history.id.should.equal @overleaf_id done() it "should set the overleaf id if overleaf id provided", (done)-> overleaf_id = 2345 @handler.createBlankProject ownerId, projectName, overleaf_id, (err, project)-> - project.overleaf.id.should.equal overleaf_id + project.overleaf.history.id.should.equal overleaf_id done() it "should set the language from the user", (done)-> From 4ca73f6b53ee59a1ac3029ce5738a1f90f899def Mon Sep 17 00:00:00 2001 From: Hayden Faulds Date: Fri, 27 Oct 2017 16:52:06 +0100 Subject: [PATCH 200/206] allow overleaf.history.id to be blank --- .../app/coffee/Features/Project/ProjectCreationHandler.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/app/coffee/Features/Project/ProjectCreationHandler.coffee b/services/web/app/coffee/Features/Project/ProjectCreationHandler.coffee index 1bf84e6751..a38b214a9e 100644 --- a/services/web/app/coffee/Features/Project/ProjectCreationHandler.coffee +++ b/services/web/app/coffee/Features/Project/ProjectCreationHandler.coffee @@ -29,7 +29,7 @@ module.exports = ProjectCreationHandler = else HistoryController.initializeProject (error, history) -> return callback(error) if error? - ProjectCreationHandler._createBlankProject owner_id, projectName, history.overleaf_id, callback + ProjectCreationHandler._createBlankProject owner_id, projectName, history?.overleaf_id, callback _createBlankProject : (owner_id, projectName, projectHistoryId, callback = (error, project) ->)-> rootFolder = new Folder {'name':'rootFolder'} From b9ad03d20cd0751c12e55b568b3697079b2bae4c Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Mon, 30 Oct 2017 09:44:08 +0000 Subject: [PATCH 201/206] Increase autocompile rollout to 20% --- .../web/app/coffee/Features/Project/ProjectController.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/app/coffee/Features/Project/ProjectController.coffee b/services/web/app/coffee/Features/Project/ProjectController.coffee index 61e3caa02d..cc592092da 100644 --- a/services/web/app/coffee/Features/Project/ProjectController.coffee +++ b/services/web/app/coffee/Features/Project/ProjectController.coffee @@ -259,7 +259,7 @@ module.exports = ProjectController = timestamp = parseInt(user_id.toString().substring(0, 8), 16) counter = parseInt(user_id.toString().substring(18, 24), 16) - rolloutPercentage = 10 # Percentage of users to roll out to + rolloutPercentage = 20 # Percentage of users to roll out to if counter % 100 > rolloutPercentage # Don't show if user is not part of roll out return cb(null, false) From ebd28a8353a63b37dfd8804e7eee7c553fc9796b Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 30 Oct 2017 15:40:56 +0000 Subject: [PATCH 202/206] upgrade ioredis vis redis sharelatex --- services/web/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/package.json b/services/web/package.json index 80cba81a5f..9a553c746f 100644 --- a/services/web/package.json +++ b/services/web/package.json @@ -57,7 +57,7 @@ "passport-saml": "^0.15.0", "pug": "^2.0.0-beta6", "redis": "0.10.1", - "redis-sharelatex": "git+https://github.com/sharelatex/redis-sharelatex.git#v1.0.2", + "redis-sharelatex": "git+https://github.com/sharelatex/redis-sharelatex.git#v1.0.4", "request": "^2.69.0", "requests": "^0.1.7", "rimraf": "2.2.6", From e474344204ce0de910410d06e810fdd1f8e30ae5 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 30 Oct 2017 15:41:09 +0000 Subject: [PATCH 203/206] remove unnecessary ioredis package --- services/web/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/services/web/package.json b/services/web/package.json index 9a553c746f..d8ed272119 100644 --- a/services/web/package.json +++ b/services/web/package.json @@ -29,7 +29,6 @@ "heapdump": "^0.3.7", "helmet": "^3.8.1", "http-proxy": "^1.8.1", - "ioredis": "^2.4.0", "jade": "~1.3.1", "jsonwebtoken": "^8.0.1", "ldapjs": "^0.7.1", From 41819db7c5ab9b72b51df80b79b315c5557d205d Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 30 Oct 2017 15:41:32 +0000 Subject: [PATCH 204/206] remove unnecessary redis package --- services/web/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/services/web/package.json b/services/web/package.json index d8ed272119..e831d929e1 100644 --- a/services/web/package.json +++ b/services/web/package.json @@ -55,7 +55,6 @@ "passport-oauth2-refresh": "^1.0.0", "passport-saml": "^0.15.0", "pug": "^2.0.0-beta6", - "redis": "0.10.1", "redis-sharelatex": "git+https://github.com/sharelatex/redis-sharelatex.git#v1.0.4", "request": "^2.69.0", "requests": "^0.1.7", From 248dfdeeb5924d9e96d999eb20a1f92ac6124899 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Tue, 31 Oct 2017 09:18:14 +0000 Subject: [PATCH 205/206] Increase autocompile rollout to 40% --- .../web/app/coffee/Features/Project/ProjectController.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/app/coffee/Features/Project/ProjectController.coffee b/services/web/app/coffee/Features/Project/ProjectController.coffee index cc592092da..2e9216e693 100644 --- a/services/web/app/coffee/Features/Project/ProjectController.coffee +++ b/services/web/app/coffee/Features/Project/ProjectController.coffee @@ -259,7 +259,7 @@ module.exports = ProjectController = timestamp = parseInt(user_id.toString().substring(0, 8), 16) counter = parseInt(user_id.toString().substring(18, 24), 16) - rolloutPercentage = 20 # Percentage of users to roll out to + rolloutPercentage = 40 # Percentage of users to roll out to if counter % 100 > rolloutPercentage # Don't show if user is not part of roll out return cb(null, false) From 6dc0561fa737f5ae23b73a84a0622213d72c9ee7 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Tue, 31 Oct 2017 17:00:41 +0000 Subject: [PATCH 206/206] Fix bug where dismissing onboarding would hide autocompile switch --- .../Features/Project/ProjectController.coffee | 13 +++++++------ services/web/app/views/project/editor.pug | 1 + services/web/app/views/project/editor/pdf.pug | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/services/web/app/coffee/Features/Project/ProjectController.coffee b/services/web/app/coffee/Features/Project/ProjectController.coffee index 2e9216e693..7822b5048f 100644 --- a/services/web/app/coffee/Features/Project/ProjectController.coffee +++ b/services/web/app/coffee/Features/Project/ProjectController.coffee @@ -262,21 +262,21 @@ module.exports = ProjectController = rolloutPercentage = 40 # Percentage of users to roll out to if counter % 100 > rolloutPercentage # Don't show if user is not part of roll out - return cb(null, false) + return cb(null, { enabled: false, showOnboarding: false }) userSignupDate = new Date(timestamp * 1000) if userSignupDate > new Date("2017-10-16") # Don't show for users who registered after it was released - return cb(null, false) + return cb(null, { enabled: true, showOnboarding: false }) timeout = setTimeout cb, 500 AnalyticsManager.getLastOccurance user_id, "shown-autocompile-onboarding", (error, event) -> clearTimeout timeout if error? - return cb(null, false) + return cb(null, { enabled: true, showOnboarding: false }) else if event? - return cb(null, false) + return cb(null, { enabled: true, showOnboarding: false }) else logger.log { user_id, event }, "autocompile onboarding not shown yet to this user" - return cb(null, true) + return cb(null, { enabled: true, showOnboarding: true }) }, (err, results)-> if err? logger.err err:err, "error getting details for project page" @@ -329,7 +329,8 @@ module.exports = ProjectController = trackChangesState: project.track_changes showTrackChangesOnboarding: !!showTrackChangesOnboarding showPerUserTCNotice: !!showPerUserTCNotice - showAutoCompileOnboarding: !!showAutoCompileOnboarding + autoCompileEnabled: !!showAutoCompileOnboarding?.enabled + showAutoCompileOnboarding: !!showAutoCompileOnboarding?.showOnboarding privilegeLevel: privilegeLevel chatUrl: Settings.apis.chat.url anonymous: anonymous diff --git a/services/web/app/views/project/editor.pug b/services/web/app/views/project/editor.pug index 1065941815..83ee097e1c 100644 --- a/services/web/app/views/project/editor.pug +++ b/services/web/app/views/project/editor.pug @@ -122,6 +122,7 @@ block requirejs window.trackChangesState = data.trackChangesState; window.showTrackChangesOnboarding = #{!!showTrackChangesOnboarding}; window.showPerUserTCNotice = #{!!showPerUserTCNotice}; + window.autoCompileEnabled = #{!!autoCompileEnabled}; window.showAutoCompileOnboarding = #{!!showAutoCompileOnboarding} window.wikiEnabled = #{!!(settings.apis.wiki && settings.apis.wiki.url)}; window.requirejs = { diff --git a/services/web/app/views/project/editor/pdf.pug b/services/web/app/views/project/editor/pdf.pug index 429fa41c6f..eb2b4bfefd 100644 --- a/services/web/app/views/project/editor/pdf.pug +++ b/services/web/app/views/project/editor/pdf.pug @@ -27,7 +27,7 @@ div.full-size.pdf(ng-controller="PdfController") span.caret ul.dropdown-menu.dropdown-menu-left // Only show if on beta program or part of rollout - if user.betaProgram || showAutoCompileOnboarding + if user.betaProgram || autoCompileEnabled li.dropdown-header #{translate("auto_compile")} li a(href, ng-click="autocompile_enabled = true")