diff --git a/services/web/app/coffee/Features/Project/ProjectController.coffee b/services/web/app/coffee/Features/Project/ProjectController.coffee index 389d623bbd..666fdefe6d 100644 --- a/services/web/app/coffee/Features/Project/ProjectController.coffee +++ b/services/web/app/coffee/Features/Project/ProjectController.coffee @@ -293,6 +293,7 @@ module.exports = ProjectController = theme : user.ace.theme fontSize : user.ace.fontSize autoComplete: user.ace.autoComplete + autoPairDelimiters: user.ace.autoPairDelimiters pdfViewer : user.ace.pdfViewer syntaxValidation: user.ace.syntaxValidation } diff --git a/services/web/app/coffee/Features/Subscription/SubscriptionRouter.coffee b/services/web/app/coffee/Features/Subscription/SubscriptionRouter.coffee index c5d54a9c49..9a3e34ce5a 100644 --- a/services/web/app/coffee/Features/Subscription/SubscriptionRouter.coffee +++ b/services/web/app/coffee/Features/Subscription/SubscriptionRouter.coffee @@ -4,7 +4,7 @@ SubscriptionGroupController = require './SubscriptionGroupController' Settings = require "settings-sharelatex" module.exports = - apply: (webRouter, apiRouter) -> + apply: (webRouter, privateApiRouter, publicApiRouter) -> return unless Settings.enableSubscriptions webRouter.get '/user/subscription/plans', SubscriptionController.plansPage @@ -35,7 +35,7 @@ module.exports = webRouter.get '/user/subscription/:subscription_id/group/successful-join', AuthenticationController.requireLogin(), SubscriptionGroupController.renderSuccessfulJoinPage #recurly callback - apiRouter.post '/user/subscription/callback', SubscriptionController.recurlyNotificationParser, SubscriptionController.recurlyCallback + publicApiRouter.post '/user/subscription/callback', SubscriptionController.recurlyNotificationParser, SubscriptionController.recurlyCallback #user changes their account state webRouter.post '/user/subscription/create', AuthenticationController.requireLogin(), SubscriptionController.createSubscription diff --git a/services/web/app/coffee/Features/User/UserController.coffee b/services/web/app/coffee/Features/User/UserController.coffee index 65000985e1..9954294577 100644 --- a/services/web/app/coffee/Features/User/UserController.coffee +++ b/services/web/app/coffee/Features/User/UserController.coffee @@ -74,6 +74,8 @@ module.exports = UserController = user.ace.fontSize = req.body.fontSize if req.body.autoComplete? user.ace.autoComplete = req.body.autoComplete + if req.body.autoPairDelimiters? + user.ace.autoPairDelimiters = req.body.autoPairDelimiters if req.body.spellCheckLanguage? user.ace.spellCheckLanguage = req.body.spellCheckLanguage if req.body.pdfViewer? diff --git a/services/web/app/coffee/models/User.coffee b/services/web/app/coffee/models/User.coffee index 099b9ef8e2..fce4b18bc0 100644 --- a/services/web/app/coffee/models/User.coffee +++ b/services/web/app/coffee/models/User.coffee @@ -24,6 +24,7 @@ UserSchema = new Schema theme : {type : String, default: 'textmate'} fontSize : {type : Number, default:'12'} autoComplete: {type : Boolean, default: true} + autoPairDelimiters: {type : Boolean, default: true} spellCheckLanguage : {type : String, default: "en"} pdfViewer : {type : String, default: "pdfjs"} syntaxValidation : {type : Boolean} diff --git a/services/web/app/coffee/router.coffee b/services/web/app/coffee/router.coffee index 28b42cc5c1..2ebde53748 100644 --- a/services/web/app/coffee/router.coffee +++ b/services/web/app/coffee/router.coffee @@ -69,7 +69,7 @@ module.exports = class Router EditorRouter.apply(webRouter, privateApiRouter) CollaboratorsRouter.apply(webRouter, privateApiRouter) - SubscriptionRouter.apply(webRouter, privateApiRouter) + SubscriptionRouter.apply(webRouter, privateApiRouter, publicApiRouter) UploadsRouter.apply(webRouter, privateApiRouter) PasswordResetRouter.apply(webRouter, privateApiRouter) StaticPagesRouter.apply(webRouter, privateApiRouter) @@ -94,7 +94,14 @@ module.exports = class Router SudoModeMiddlewear.protectPage, UserPagesController.settingsPage webRouter.post '/user/settings', AuthenticationController.requireLogin(), UserController.updateUserSettings - webRouter.post '/user/password/update', AuthenticationController.requireLogin(), UserController.changePassword + webRouter.post '/user/password/update', + AuthenticationController.requireLogin(), + RateLimiterMiddlewear.rateLimit({ + endpointName: "change-password" + maxRequests: 10 + timeInterval: 60 + }), + UserController.changePassword webRouter.get '/user/sessions', AuthenticationController.requireLogin(), diff --git a/services/web/app/views/project/editor/editor.pug b/services/web/app/views/project/editor/editor.pug index ebeeee6ceb..8b58c98bcd 100644 --- a/services/web/app/views/project/editor/editor.pug +++ b/services/web/app/views/project/editor/editor.pug @@ -37,6 +37,7 @@ div.full-size( keybindings="settings.mode", font-size="settings.fontSize", auto-complete="settings.autoComplete", + auto-pair-delimiters="settings.autoPairDelimiters", spell-check="!anonymous", spell-check-language="project.spellCheckLanguage" highlights="onlineUserCursorHighlights[editor.open_doc_id]" diff --git a/services/web/app/views/project/editor/left-menu.pug b/services/web/app/views/project/editor/left-menu.pug index fd5b7bdd90..bf7b9ba331 100644 --- a/services/web/app/views/project/editor/left-menu.pug +++ b/services/web/app/views/project/editor/left-menu.pug @@ -105,6 +105,14 @@ aside#left-menu.full-size( ng-options="o.v as o.n for o in [{ n: 'On', v: true }, { n: 'Off', v: false }]" ) + .form-controls + label(for="autoPairDelimiters") #{translate("auto_close_brackets")} + select( + name="autoPairDelimiters" + ng-model="settings.autoPairDelimiters" + ng-options="o.v as o.n for o in [{ n: 'On', v: true }, { n: 'Off', v: false }]" + ) + .form-controls.code-check-setting label(for="syntaxValidation") #{translate("syntax_validation")} select( diff --git a/services/web/app/views/project/list/side-bar.pug b/services/web/app/views/project/list/side-bar.pug index b3a3f11948..5429045afb 100644 --- a/services/web/app/views/project/list/side-bar.pug +++ b/services/web/app/views/project/list/side-bar.pug @@ -48,7 +48,7 @@ li h2 #{translate("folders")} li.tag( - ng-repeat="tag in tags | orderBy:name", + ng-repeat="tag in tags | orderBy:'name'", ng-class="{active: tag.selected}", ng-cloak, ng-click="selectTag(tag)" diff --git a/services/web/app/views/sentry.pug b/services/web/app/views/sentry.pug index 6406302e0e..cf10c6a329 100644 --- a/services/web/app/views/sentry.pug +++ b/services/web/app/views/sentry.pug @@ -72,6 +72,8 @@ // whitelistUrls: ['example.com/scripts/'] }).install(); } + }, function(err) { + console.log(">> error loading raven", err); }) - if (user && typeof(user) != "undefined" && typeof (user.email) != "undefined") script(type="text/javascript"). diff --git a/services/web/npm-shrinkwrap.json b/services/web/npm-shrinkwrap.json index f22ecf0219..a8357be7fd 100644 --- a/services/web/npm-shrinkwrap.json +++ b/services/web/npm-shrinkwrap.json @@ -54,6 +54,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.1", "from": "aproba@>=1.0.3 <2.0.0", @@ -145,6 +151,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.41.0", "from": "aws-sdk@>=2.2.36 <3.0.0", @@ -287,25 +299,17 @@ "from": "brace-expansion@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz" }, + "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", "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.4.tgz" }, - "bson-ext": { - "version": "0.1.13", - "from": "bson-ext@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/bson-ext/-/bson-ext-0.1.13.tgz", - "optional": true, - "dependencies": { - "nan": { - "version": "2.0.9", - "from": "nan@>=2.0.9 <2.1.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.0.9.tgz", - "optional": true - } - } - }, "buffer": { "version": "4.9.1", "from": "buffer@4.9.1", @@ -372,6 +376,12 @@ "from": "camelcase@>=1.0.2 <2.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz" }, + "caniuse-db": { + "version": "1.0.30000708", + "from": "caniuse-db@>=1.0.30000634 <2.0.0", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000708.tgz", + "dev": true + }, "caseless": { "version": "0.12.0", "from": "caseless@>=0.12.0 <0.13.0", @@ -392,6 +402,20 @@ "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", @@ -545,6 +569,12 @@ "from": "cookie-signature@1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" }, + "cookies": { + "version": "0.7.0", + "from": "cookies@>=0.2.2", + "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.7.0.tgz", + "dev": true + }, "core-js": { "version": "1.2.7", "from": "core-js@>=1.2.0 <2.0.0", @@ -639,6 +669,13 @@ } } }, + "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 + }, "dashdash": { "version": "1.14.1", "from": "dashdash@>=1.12.0 <2.0.0", @@ -730,6 +767,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 + }, "dottie": { "version": "1.1.1", "from": "dottie@>=1.0.0 <2.0.0", @@ -761,6 +830,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.16", + "from": "electron-to-chromium@>=1.2.7 <2.0.0", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.16.tgz", + "dev": true + }, "encoding": { "version": "0.1.12", "from": "encoding@>=0.1.0 <0.2.0", @@ -778,6 +853,18 @@ "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 + }, + "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", @@ -887,6 +974,12 @@ "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", @@ -960,6 +1053,12 @@ "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.0", "from": "forwarded@>=0.1.0 <0.2.0", @@ -1029,6 +1128,12 @@ "from": "gauge@>=2.7.1 <2.8.0", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.3.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", @@ -1063,6 +1168,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", @@ -1130,6 +1255,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", @@ -1142,11 +1287,145 @@ } } }, + "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-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", @@ -1198,6 +1477,84 @@ } } }, + "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.1", + "from": "rimraf@>=2.5.2 <3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.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 + }, "hang": { "version": "1.0.0", "from": "hang@>=1.0.0 <1.1.0", @@ -1218,6 +1575,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", @@ -1244,9 +1619,41 @@ "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" }, "hooks-fixed": { - "version": "1.1.0", - "from": "hooks-fixed@1.1.0", - "resolved": "https://registry.npmjs.org/hooks-fixed/-/hooks-fixed-1.1.0.tgz" + "version": "2.0.0", + "from": "hooks-fixed@2.0.0", + "resolved": "https://registry.npmjs.org/hooks-fixed/-/hooks-fixed-2.0.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 + }, + "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", + "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.1", @@ -1263,6 +1670,12 @@ "from": "http-signature@>=1.1.0 <1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz" }, + "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", @@ -1285,7 +1698,7 @@ }, "inherits": { "version": "2.0.3", - "from": "inherits@>=2.0.0 <3.0.0", + "from": "inherits@>=2.0.1 <2.1.0", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, "ini": { @@ -1398,6 +1811,12 @@ "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz", "optional": true }, + "js-base64": { + "version": "2.1.9", + "from": "js-base64@>=2.1.9 <3.0.0", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "dev": true + }, "js-stringify": { "version": "1.0.2", "from": "js-stringify@>=1.0.1 <2.0.0", @@ -1429,6 +1848,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", @@ -1469,23 +1894,15 @@ } }, "kareem": { - "version": "1.0.1", - "from": "kareem@1.0.1", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-1.0.1.tgz" + "version": "1.5.0", + "from": "kareem@1.5.0", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-1.5.0.tgz" }, - "kerberos": { - "version": "0.0.23", - "from": "kerberos@>=0.0.0 <0.1.0", - "resolved": "https://registry.npmjs.org/kerberos/-/kerberos-0.0.23.tgz", - "optional": true, - "dependencies": { - "nan": { - "version": "2.5.1", - "from": "nan@>=2.5.1 <2.6.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.5.1.tgz", - "optional": true - } - } + "keygrip": { + "version": "1.0.1", + "from": "keygrip@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.0.1.tgz", + "dev": true }, "kind-of": { "version": "3.1.0", @@ -1628,6 +2045,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", @@ -1650,6 +2103,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", @@ -1862,11 +2321,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", @@ -2090,46 +2561,54 @@ } }, "mongoose": { - "version": "4.1.0", - "from": "mongoose@4.1.0", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-4.1.0.tgz", + "version": "4.11.4", + "from": "mongoose@4.11.4", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-4.11.4.tgz", "dependencies": { "async": { - "version": "0.9.0", - "from": "async@0.9.0", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz" + "version": "2.1.4", + "from": "async@2.1.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.1.4.tgz" }, - "bson": { - "version": "0.3.2", - "from": "bson@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-0.3.2.tgz" + "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" + }, + "isarray": { + "version": "1.0.0", + "from": "isarray@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" }, "mongodb": { - "version": "2.0.34", - "from": "mongodb@2.0.34", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.0.34.tgz" + "version": "2.2.27", + "from": "mongodb@2.2.27", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.27.tgz" }, "mongodb-core": { - "version": "1.2.0", - "from": "mongodb-core@1.2.0", - "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-1.2.0.tgz", - "dependencies": { - "bson": { - "version": "0.4.23", - "from": "bson@>=0.4.0 <0.5.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-0.4.23.tgz" - } - } + "version": "2.1.11", + "from": "mongodb-core@2.1.11", + "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.11.tgz" }, "ms": { - "version": "0.1.0", - "from": "ms@0.1.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.1.0.tgz" + "version": "2.0.0", + "from": "ms@2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" }, "readable-stream": { - "version": "1.0.31", - "from": "readable-stream@1.0.31", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.31.tgz" + "version": "2.2.7", + "from": "readable-stream@2.2.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz" + }, + "safe-buffer": { + "version": "5.1.1", + "from": "safe-buffer@>=5.1.0 <5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + }, + "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" } } }, @@ -2139,34 +2618,39 @@ "resolved": "https://registry.npmjs.org/monocle/-/monocle-1.1.51.tgz" }, "mpath": { - "version": "0.1.1", - "from": "mpath@0.1.1", - "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.1.1.tgz" + "version": "0.3.0", + "from": "mpath@0.3.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.3.0.tgz" }, "mpromise": { - "version": "0.5.4", - "from": "mpromise@0.5.4", - "resolved": "https://registry.npmjs.org/mpromise/-/mpromise-0.5.4.tgz" + "version": "0.5.5", + "from": "mpromise@0.5.5", + "resolved": "https://registry.npmjs.org/mpromise/-/mpromise-0.5.5.tgz" }, "mquery": { - "version": "1.6.1", - "from": "mquery@1.6.1", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-1.6.1.tgz", + "version": "2.3.1", + "from": "mquery@2.3.1", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-2.3.1.tgz", "dependencies": { "bluebird": { - "version": "2.9.26", - "from": "bluebird@2.9.26", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.9.26.tgz" + "version": "2.10.2", + "from": "bluebird@2.10.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.10.2.tgz" }, "debug": { - "version": "2.2.0", - "from": "debug@2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz" + "version": "2.6.8", + "from": "debug@2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz" }, "ms": { - "version": "0.7.1", - "from": "ms@0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + "version": "2.0.0", + "from": "ms@2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + }, + "sliced": { + "version": "0.0.5", + "from": "sliced@0.0.5", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-0.0.5.tgz" } } }, @@ -2208,9 +2692,9 @@ } }, "muri": { - "version": "1.0.0", - "from": "muri@1.0.0", - "resolved": "https://registry.npmjs.org/muri/-/muri-1.0.0.tgz" + "version": "1.2.2", + "from": "muri@1.2.2", + "resolved": "https://registry.npmjs.org/muri/-/muri-1.2.2.tgz" }, "mv": { "version": "0.0.5", @@ -2311,16 +2795,48 @@ "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.x", + "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 + }, "npmlog": { "version": "4.0.2", "from": "npmlog@>=4.0.0 <5.0.0", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.0.2.tgz" }, + "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", @@ -2356,6 +2872,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", @@ -2479,6 +3143,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.6", + "from": "source-map@>=0.5.6 <0.6.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.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", @@ -2935,11 +3619,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", @@ -2967,6 +3671,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", @@ -3035,6 +3745,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", @@ -3195,15 +3925,21 @@ "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", "resolved": "https://registry.npmjs.org/sixpack-client/-/sixpack-client-1.0.0.tgz" }, "sliced": { - "version": "0.0.5", - "from": "sliced@0.0.5", - "resolved": "https://registry.npmjs.org/sliced/-/sliced-0.0.5.tgz" + "version": "1.0.1", + "from": "sliced@1.0.1", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz" }, "smtp-connection": { "version": "2.0.1", @@ -3287,6 +4023,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 + }, "tar": { "version": "2.2.1", "from": "tar@>=2.2.0 <2.3.0", @@ -3369,6 +4111,78 @@ "from": "timekeeper@", "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-1.0.0.tgz" }, + "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", @@ -3406,6 +4220,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#c2bcc5c8b39034d3dfa2f4509fcc8e1e0be6ed75", + "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", @@ -3634,6 +4462,18 @@ "from": "void-elements@>=2.0.1 <3.0.0", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz" }, + "websocket-driver": { + "version": "0.6.5", + "from": "websocket-driver@>=0.5.1", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", + "dev": true + }, + "websocket-extensions": { + "version": "0.1.1", + "from": "websocket-extensions@>=0.1.1", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.1.tgz", + "dev": true + }, "which": { "version": "1.0.9", "from": "which@>=1.0.5 <1.1.0", @@ -3759,6 +4599,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 9c7c7b58de..eac07a06e5 100644 --- a/services/web/package.json +++ b/services/web/package.json @@ -30,8 +30,8 @@ "ioredis": "^2.4.0", "jade": "~1.3.1", "ldapjs": "^0.7.1", - "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#master", "lodash": "^4.13.1", + "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#master", "lynx": "0.1.1", "marked": "^0.3.5", "method-override": "^2.3.3", @@ -39,7 +39,7 @@ "mimelib": "0.2.14", "mocha": "1.17.1", "mongojs": "2.4.0", - "mongoose": "4.1.0", + "mongoose": "4.11.4", "multer": "^0.1.8", "nodemailer": "2.1.0", "nodemailer-sendgrid-transport": "^0.2.0", @@ -48,23 +48,23 @@ "passport": "^0.3.2", "passport-ldapauth": "^0.6.0", "passport-local": "^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.2", "request": "^2.69.0", "requests": "^0.1.7", "rimraf": "2.2.6", + "rolling-rate-limiter": "git+https://github.com/ShaneKilkelly/rolling-rate-limiter.git#master", "sanitizer": "0.1.1", "sequelize": "^3.2.0", "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", "sixpack-client": "^1.0.0", "temp": "^0.8.3", "underscore": "1.6.0", - "v8-profiler": "^5.2.3", - "xml2js": "0.2.0", - "passport-saml": "^0.15.0", - "pug": "^2.0.0-beta6", "uuid": "^3.0.1", - "rolling-rate-limiter": "git+https://github.com/ShaneKilkelly/rolling-rate-limiter.git#master" + "v8-profiler": "^5.2.3", + "xml2js": "0.2.0" }, "devDependencies": { "autoprefixer": "^6.6.1", diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee index 6c2b81edc4..af633ea369 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee @@ -44,6 +44,7 @@ define [ keybindings: "=" fontSize: "=" autoComplete: "=" + autoPairDelimiters: "=" sharejsDoc: "=" spellCheck: "=" spellCheckLanguage: "=" @@ -78,10 +79,16 @@ define [ editor = ace.edit(element.find(".ace-editor-body")[0]) editor.$blockScrolling = Infinity - # disable auto insertion of brackets and quotes - editor.setOption('behavioursEnabled', false) + # auto-insertion of braces, brackets, dollars + editor.setOption('behavioursEnabled', scope.autoPairDelimiters || false) editor.setOption('wrapBehavioursEnabled', false) + scope.$watch "autoPairDelimiters", (autoPairDelimiters) => + if autoPairDelimiters + editor.setOption('behavioursEnabled', true) + else + editor.setOption('behavioursEnabled', false) + window.editors ||= [] window.editors.push editor 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 cbcdc2eac7..c320266a5a 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 @@ -5,6 +5,7 @@ define [ "ace/ext-language_tools" ], (SuggestionManager, SnippetManager) -> Range = ace.require("ace/range").Range + aceSnippetManager = ace.require('ace/snippets').snippetManager getLastCommandFragment = (lineUpToCursor) -> if m = lineUpToCursor.match(/(\\[^\\]+)$/) @@ -12,6 +13,9 @@ define [ else return null + getCommandNameFromFragment = (commandFragment) -> + commandFragment?.match(/\\(\w+)\{/)?[1] + class AutoCompleteManager constructor: (@$scope, @editor, @element, @labelsManager) -> @suggestionManager = new SuggestionManager() @@ -59,14 +63,14 @@ define [ caption: "\\#{commandName}{}", snippet: "\\#{commandName}{}", meta: "cross-reference", - score: 11000 + score: 60 } for label in labelsManager.getAllLabels() result.push { caption: "\\#{commandName}{#{label}#{if needsClosingBrace then '}' else ''}", value: "\\#{commandName}{#{label}#{if needsClosingBrace then '}' else ''}", meta: "cross-reference", - score: 10000 + score: 50 } callback null, result @@ -93,7 +97,7 @@ define [ caption: "\\#{commandName}{}", snippet: "\\#{commandName}{}", meta: "reference", - score: 11000 + score: 60 } if references.keys and references.keys.length > 0 references.keys.forEach (key) -> @@ -102,7 +106,7 @@ define [ caption: "\\#{commandName}{#{previousArgsCaption}#{key}#{if needsClosingBrace then '}' else ''}", value: "\\#{commandName}{#{previousArgs}#{key}#{if needsClosingBrace then '}' else ''}", meta: "reference", - score: 10000 + score: 50 }) callback null, result else @@ -121,7 +125,18 @@ define [ end = change.end range = new Range(end.row, 0, end.row, end.column) lineUpToCursor = @editor.getSession().getTextRange(range) + if lineUpToCursor.match(/.*%.*/) + return + lastCharIsBackslash = lineUpToCursor.slice(-1) == "\\" + lastTwoChars = lineUpToCursor.slice(-2) + # Don't offer autocomplete on double-backslash, backslash-colon, etc + 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 # (Cursor is still one place behind) # NOTE: this is also the case when a user backspaces over a highlighted region @@ -130,7 +145,7 @@ define [ end.row == cursorPosition.row and end.column == cursorPosition.column + 1 ) - if commandFragment? and commandFragment.length > 2 + if (commandFragment? and commandFragment.length > 2) or lastCharIsBackslash setTimeout () => @editor.execCommand("startAutocomplete") , 0 @@ -154,6 +169,70 @@ define [ if this.completions.filterText.match(/^\\begin\{/) and nextChar == "}" editor.session.remove(range) + # Provide our own `insertMatch` implementation. + # See the `insertMatch` method of Autocomplete in `ext-language_tools.js`. + # We need this to account for editing existing commands, particularly when + # adding a prefix. + # We fix this by detecting when the cursor is in the middle of an existing + # command, and adjusting the insertions/deletions accordingly. + # Example: + # when changing `\ref{}` to `\href{}`, ace default behaviour + # is likely to end up with `\href{}ref{}` + if !data? + completions = this.completions + popup = editor.completer.popup + data = popup.getData(popup.getRow()) + data.completer = + insertMatch: (editor, matchData) -> + for range in editor.selection.getAllRanges() + leftRange = _.clone(range) + rightRange = _.clone(range) + # trim to left of cursor + lineUpToCursor = editor.getSession().getTextRange( + new Range( + range.start.row, + 0, + range.start.row, + range.start.column, + ) + ) + # Delete back to last backslash, as appropriate + lastBackslashIndex = lineUpToCursor.lastIndexOf('\\') + if lastBackslashIndex != -1 + leftRange.start.column = lastBackslashIndex + else + leftRange.start.column -= completions.filterText.length + editor.session.remove(leftRange) + # look at text after cursor + lineBeyondCursor = editor.getSession().getTextRange( + new Range( + rightRange.start.row, + rightRange.start.column, + rightRange.end.row, + 99999 + ) + ) + if lineBeyondCursor + if partialCommandMatch = lineBeyondCursor.match(/^([a-z0-9]+)\{/) + # We've got a partial command after the cursor + commandTail = partialCommandMatch[1] + # remove rest of the partial command, right of cursor + rightRange.end.column += commandTail.length - completions.filterText.length + editor.session.remove(rightRange); + # trim the completion text to just the command, without braces or brackets + # example: '\cite{}' -> '\cite' + if matchData.snippet? + matchData.snippet = matchData.snippet.replace(/[{\[].*[}\]]/, '') + if matchData.caption? + matchData.caption = matchData.caption.replace(/[{\[].*[}\]]/, '') + if matchData.value? + matchData.value = matchData.value.replace(/[{\[].*[}\]]/, '') + # finally, insert the match + if matchData.snippet + aceSnippetManager.insertSnippet(editor, matchData.snippet); + else + editor.execCommand("insertstring", matchData.value || matchData); + Autocomplete::_insertMatch.call this, data # Overwrite this to set autoInsert = false and set font size diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/SuggestionManager.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/SuggestionManager.coffee index 97241a90ce..250590e4b4 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/SuggestionManager.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/SuggestionManager.coffee @@ -1,7 +1,7 @@ define [], () -> class Parser - constructor: (@doc) -> + constructor: (@doc, @prefix) -> parse: () -> # Safari regex is super slow, freezes browser for minutes on end, @@ -10,13 +10,17 @@ define [], () -> if window?._ide?.browserIsSafari limit = 100 - commands = [] + # fully formed commands + realCommands = [] + # commands which match the prefix exactly, + # and could be partially typed or malformed + incidentalCommands = [] seen = {} iterations = 0 while command = @nextCommand() iterations += 1 if limit && iterations > limit - return commands + return realCommands docState = @doc @@ -29,14 +33,23 @@ define [], () -> args++ commandHash = "#{command}\\#{optionalArgs}\\#{args}" - if !seen[commandHash]? - seen[commandHash] = true - commands.push [command, optionalArgs, args] + + if @prefix? && "\\#{command}" == @prefix + incidentalCommands.push [command, optionalArgs, args] + else + if !seen[commandHash]? + seen[commandHash] = true + realCommands.push [command, optionalArgs, args] # Reset to before argument to handle nested commands @doc = docState - return commands + # check incidentals, see if we should pluck out a match + if incidentalCommands.length > 1 + bestMatch = incidentalCommands.sort((a, b) => a[1]+a[2] < b[1]+b[2])[0] + realCommands.push bestMatch + + return realCommands # Ignore single letter commands since auto complete is moot then. commandRegex: /\\([a-zA-Z][a-zA-Z]+)/ @@ -78,12 +91,12 @@ define [], () -> class SuggestionManager getCompletions: (editor, session, pos, prefix, callback) -> doc = session.getValue() - parser = new Parser(doc) + parser = new Parser(doc, prefix) commands = parser.parse() - completions = [] for command in commands caption = "\\#{command[0]}" + score = if caption == prefix then 99 else 50 snippet = caption i = 1 _.times command[1], () -> @@ -94,12 +107,12 @@ define [], () -> snippet += "{${#{i}}}" caption += "{}" i++ - unless caption == prefix - completions.push { - caption: caption - snippet: snippet - meta: "cmd" - } + completions.push { + caption: caption + snippet: snippet + meta: "cmd" + score: score + } callback null, completions diff --git a/services/web/public/coffee/ide/settings/controllers/SettingsController.coffee b/services/web/public/coffee/ide/settings/controllers/SettingsController.coffee index b42d07467c..26520d76e0 100644 --- a/services/web/public/coffee/ide/settings/controllers/SettingsController.coffee +++ b/services/web/public/coffee/ide/settings/controllers/SettingsController.coffee @@ -29,6 +29,10 @@ define [ if autoComplete != oldAutoComplete settings.saveSettings({autoComplete: autoComplete}) + $scope.$watch "settings.autoPairDelimiters", (autoPairDelimiters, oldAutoPairDelimiters) => + if autoPairDelimiters != oldAutoPairDelimiters + settings.saveSettings({autoPairDelimiters: autoPairDelimiters}) + $scope.$watch "settings.pdfViewer", (pdfViewer, oldPdfViewer) => if pdfViewer != oldPdfViewer settings.saveSettings({pdfViewer: pdfViewer}) @@ -66,4 +70,4 @@ define [ $scope.$apply () => $scope.project.spellCheckLanguage = languageCode delete @ignoreUpdates - ] \ No newline at end of file + ] diff --git a/services/web/public/js/ace-1.2.5/mode-latex.js b/services/web/public/js/ace-1.2.5/mode-latex.js index 8e7bbe4802..ede29b9661 100644 --- a/services/web/public/js/ace-1.2.5/mode-latex.js +++ b/services/web/public/js/ace-1.2.5/mode-latex.js @@ -195,7 +195,282 @@ oop.inherits(FoldMode, BaseFoldMode); }); -ace.define("ace/mode/latex",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/latex_highlight_rules","ace/mode/folding/latex","ace/range","ace/worker/worker_client"], function(require, exports, module) { +ace.define("ace/mode/behaviour/latex",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"], function(require, exports, module) { +"use strict"; + +var oop = require("../../lib/oop"); +var Behaviour = require("../behaviour").Behaviour; +var TokenIterator = require("../../token_iterator").TokenIterator; +var lang = require("../../lib/lang"); + +var SAFE_INSERT_IN_TOKENS = + ["text", "paren.rparen", "punctuation.operator"]; +var SAFE_INSERT_BEFORE_TOKENS = + ["text", "paren.rparen", "punctuation.operator", "comment"]; + +var context; +var contextCache = {}; +var initContext = function(editor) { + var id = -1; + if (editor.multiSelect) { + id = editor.selection.index; + if (contextCache.rangeCount != editor.multiSelect.rangeCount) + contextCache = {rangeCount: editor.multiSelect.rangeCount}; + } + if (contextCache[id]) + return context = contextCache[id]; + context = contextCache[id] = { + autoInsertedBrackets: 0, + autoInsertedRow: -1, + autoInsertedLineEnd: "", + maybeInsertedBrackets: 0, + maybeInsertedRow: -1, + maybeInsertedLineStart: "", + maybeInsertedLineEnd: "" + }; +}; + +var getWrapped = function(selection, selected, opening, closing) { + var rowDiff = selection.end.row - selection.start.row; + return { + text: opening + selected + closing, + selection: [ + 0, + selection.start.column + 1, + rowDiff, + selection.end.column + (rowDiff ? 0 : 1) + ] + }; +}; + +var LatexBehaviour = function() { + this.add("braces", "insertion", function(state, action, editor, session, text) { + if (editor.completer && editor.completer.popup && editor.completer.popup.isOpen) { + return; + } + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + var lastChar = line[cursor.column-1]; + if (lastChar === '\\') { + return; + } + if (text == '{') { + initContext(editor); + var selection = editor.getSelectionRange(); + var selected = session.doc.getTextRange(selection); + if (selected !== "" && editor.getWrapBehavioursEnabled()) { + return getWrapped(selection, selected, '{', '}'); + } else if (LatexBehaviour.isSaneInsertion(editor, session)) { + LatexBehaviour.recordAutoInsert(editor, session, "}"); + return { + text: '{}', + selection: [1, 1] + }; + } + } else if (text == '}') { + initContext(editor); + var rightChar = line.substring(cursor.column, cursor.column + 1); + if (rightChar == '}') { + var matching = session.$findOpeningBracket('}', {column: cursor.column + 1, row: cursor.row}); + if (matching !== null && LatexBehaviour.isAutoInsertedClosing(cursor, line, text)) { + LatexBehaviour.popAutoInsertedClosing(); + return { + text: '', + selection: [1, 1] + }; + } + } + } + }); + + this.add("braces", "deletion", function(state, action, editor, session, range) { + if (editor.completer && editor.completer.popup && editor.completer.popup.isOpen) { + return; + } + var selected = session.doc.getTextRange(range); + if (!range.isMultiLine() && selected == '{') { + initContext(editor); + var line = session.doc.getLine(range.start.row); + var rightChar = line.substring(range.start.column + 1, range.start.column + 2); + if (rightChar == '}') { + range.end.column++; + return range; + } + } + }); + + this.add("brackets", "insertion", function(state, action, editor, session, text) { + if (editor.completer && editor.completer.popup && editor.completer.popup.isOpen) { + return; + } + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + var lastChar = line[cursor.column-1]; + if (lastChar === '\\') { + return; + } + if (text == '[') { + initContext(editor); + var selection = editor.getSelectionRange(); + var selected = session.doc.getTextRange(selection); + if (selected !== "" && editor.getWrapBehavioursEnabled()) { + return getWrapped(selection, selected, '[', ']'); + } else if (LatexBehaviour.isSaneInsertion(editor, session)) { + LatexBehaviour.recordAutoInsert(editor, session, "]"); + return { + text: '[]', + selection: [1, 1] + }; + } + } else if (text == ']') { + initContext(editor); + var rightChar = line.substring(cursor.column, cursor.column + 1); + if (rightChar == ']') { + var matching = session.$findOpeningBracket(']', {column: cursor.column + 1, row: cursor.row}); + if (matching !== null && LatexBehaviour.isAutoInsertedClosing(cursor, line, text)) { + LatexBehaviour.popAutoInsertedClosing(); + return { + text: '', + selection: [1, 1] + }; + } + } + } + }); + + this.add("brackets", "deletion", function(state, action, editor, session, range) { + if (editor.completer && editor.completer.popup && editor.completer.popup.isOpen) { + return; + } + var selected = session.doc.getTextRange(range); + if (!range.isMultiLine() && selected == '[') { + initContext(editor); + var line = session.doc.getLine(range.start.row); + var rightChar = line.substring(range.start.column + 1, range.start.column + 2); + if (rightChar == ']') { + range.end.column++; + return range; + } + } + }); + + this.add("dollars", "insertion", function(state, action, editor, session, text) { + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + var lastChar = line[cursor.column-1]; + if (lastChar === '\\') { + return; + } + if (text == '$') { + if (this.lineCommentStart && this.lineCommentStart.indexOf(text) != -1) + return; + initContext(editor); + var quote = text; + var selection = editor.getSelectionRange(); + var selected = session.doc.getTextRange(selection); + if (selected !== "" && selected !== "$" && editor.getWrapBehavioursEnabled()) { + return getWrapped(selection, selected, quote, quote); + } else if (!selected) { + var leftChar = line.substring(cursor.column-1, cursor.column); + var rightChar = line.substring(cursor.column, cursor.column + 1); + + var token = session.getTokenAt(cursor.row, cursor.column); + var rightToken = session.getTokenAt(cursor.row, cursor.column + 1); + + var stringBefore = token && /string|escape/.test(token.type); + var stringAfter = !rightToken || /string|escape/.test(rightToken.type); + + var pair; + if (rightChar == quote) { + pair = stringBefore !== stringAfter; + if (pair && /string\.end/.test(rightToken.type)) + pair = false; + } else { + if (stringBefore && !stringAfter) + return null; // wrap string with different quote + if (stringBefore && stringAfter) + return null; // do not pair quotes inside strings + var wordRe = session.$mode.tokenRe; + wordRe.lastIndex = 0; + var isWordBefore = wordRe.test(leftChar); + wordRe.lastIndex = 0; + var isWordAfter = wordRe.test(leftChar); + if (isWordBefore || isWordAfter) + return null; // before or after alphanumeric + if (rightChar && !/[\s;,.})\]\\]/.test(rightChar)) + return null; // there is rightChar and it isn't closing + pair = true; + } + return { + text: pair ? quote + quote : "", + selection: [1,1] + }; + } + } + }); + + this.add("dollars", "deletion", function(state, action, editor, session, range) { + var selected = session.doc.getTextRange(range); + if (!range.isMultiLine() && (selected == '$')) { + initContext(editor); + var line = session.doc.getLine(range.start.row); + var rightChar = line.substring(range.start.column + 1, range.start.column + 2); + if (rightChar == selected) { + range.end.column++; + return range; + } + } + }); + +}; + + +LatexBehaviour.isSaneInsertion = function(editor, session) { + var cursor = editor.getCursorPosition(); + var iterator = new TokenIterator(session, cursor.row, cursor.column); + if (!this.$matchTokenType(iterator.getCurrentToken() || "text", SAFE_INSERT_IN_TOKENS)) { + var iterator2 = new TokenIterator(session, cursor.row, cursor.column + 1); + if (!this.$matchTokenType(iterator2.getCurrentToken() || "text", SAFE_INSERT_IN_TOKENS)) + return false; + } + iterator.stepForward(); + return iterator.getCurrentTokenRow() !== cursor.row || + this.$matchTokenType(iterator.getCurrentToken() || "text", SAFE_INSERT_BEFORE_TOKENS); +}; + +LatexBehaviour.$matchTokenType = function(token, types) { + return types.indexOf(token.type || token) > -1; +}; + +LatexBehaviour.recordAutoInsert = function(editor, session, bracket) { + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + if (!this.isAutoInsertedClosing(cursor, line, context.autoInsertedLineEnd[0])) + context.autoInsertedBrackets = 0; + context.autoInsertedRow = cursor.row; + context.autoInsertedLineEnd = bracket + line.substr(cursor.column); + context.autoInsertedBrackets++; +}; + +LatexBehaviour.isAutoInsertedClosing = function(cursor, line, bracket) { + return context.autoInsertedBrackets > 0 && + cursor.row === context.autoInsertedRow && + bracket === context.autoInsertedLineEnd[0] && + line.substr(cursor.column) === context.autoInsertedLineEnd; +}; + +LatexBehaviour.popAutoInsertedClosing = function() { + context.autoInsertedLineEnd = context.autoInsertedLineEnd.substr(1); + context.autoInsertedBrackets--; +}; + + +oop.inherits(LatexBehaviour, Behaviour); + +exports.LatexBehaviour = LatexBehaviour; +}); + +ace.define("ace/mode/latex",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/latex_highlight_rules","ace/mode/folding/latex","ace/range","ace/worker/worker_client","ace/mode/behaviour/latex"], function(require, exports, module) { "use strict"; var oop = require("../lib/oop"); @@ -204,6 +479,7 @@ var LatexHighlightRules = require("./latex_highlight_rules").LatexHighlightRules var LatexFoldMode = require("./folding/latex").FoldMode; var Range = require("../range").Range; var WorkerClient = require("ace/worker/worker_client").WorkerClient; +var LatexBehaviour = require("./behaviour/latex").LatexBehaviour; var createLatexWorker = function (session) { var doc = session.getDocument(); @@ -361,6 +637,7 @@ var createLatexWorker = function (session) { var Mode = function() { this.HighlightRules = LatexHighlightRules; this.foldingRules = new LatexFoldMode(); + this.$behaviour = new LatexBehaviour(); this.createWorker = createLatexWorker; }; oop.inherits(Mode, TextMode); diff --git a/services/web/public/js/ace-1.2.5/mode-latex_beta.js b/services/web/public/js/ace-1.2.5/mode-latex_beta.js index 1a98491951..d170f0ade5 100644 --- a/services/web/public/js/ace-1.2.5/mode-latex_beta.js +++ b/services/web/public/js/ace-1.2.5/mode-latex_beta.js @@ -195,7 +195,282 @@ oop.inherits(FoldMode, BaseFoldMode); }); -ace.define("ace/mode/latex_beta",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/latex_highlight_rules","ace/mode/folding/latex","ace/range","ace/worker/worker_client"], function(require, exports, module) { +ace.define("ace/mode/behaviour/latex",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"], function(require, exports, module) { +"use strict"; + +var oop = require("../../lib/oop"); +var Behaviour = require("../behaviour").Behaviour; +var TokenIterator = require("../../token_iterator").TokenIterator; +var lang = require("../../lib/lang"); + +var SAFE_INSERT_IN_TOKENS = + ["text", "paren.rparen", "punctuation.operator"]; +var SAFE_INSERT_BEFORE_TOKENS = + ["text", "paren.rparen", "punctuation.operator", "comment"]; + +var context; +var contextCache = {}; +var initContext = function(editor) { + var id = -1; + if (editor.multiSelect) { + id = editor.selection.index; + if (contextCache.rangeCount != editor.multiSelect.rangeCount) + contextCache = {rangeCount: editor.multiSelect.rangeCount}; + } + if (contextCache[id]) + return context = contextCache[id]; + context = contextCache[id] = { + autoInsertedBrackets: 0, + autoInsertedRow: -1, + autoInsertedLineEnd: "", + maybeInsertedBrackets: 0, + maybeInsertedRow: -1, + maybeInsertedLineStart: "", + maybeInsertedLineEnd: "" + }; +}; + +var getWrapped = function(selection, selected, opening, closing) { + var rowDiff = selection.end.row - selection.start.row; + return { + text: opening + selected + closing, + selection: [ + 0, + selection.start.column + 1, + rowDiff, + selection.end.column + (rowDiff ? 0 : 1) + ] + }; +}; + +var LatexBehaviour = function() { + this.add("braces", "insertion", function(state, action, editor, session, text) { + if (editor.completer && editor.completer.popup && editor.completer.popup.isOpen) { + return; + } + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + var lastChar = line[cursor.column-1]; + if (lastChar === '\\') { + return; + } + if (text == '{') { + initContext(editor); + var selection = editor.getSelectionRange(); + var selected = session.doc.getTextRange(selection); + if (selected !== "" && editor.getWrapBehavioursEnabled()) { + return getWrapped(selection, selected, '{', '}'); + } else if (LatexBehaviour.isSaneInsertion(editor, session)) { + LatexBehaviour.recordAutoInsert(editor, session, "}"); + return { + text: '{}', + selection: [1, 1] + }; + } + } else if (text == '}') { + initContext(editor); + var rightChar = line.substring(cursor.column, cursor.column + 1); + if (rightChar == '}') { + var matching = session.$findOpeningBracket('}', {column: cursor.column + 1, row: cursor.row}); + if (matching !== null && LatexBehaviour.isAutoInsertedClosing(cursor, line, text)) { + LatexBehaviour.popAutoInsertedClosing(); + return { + text: '', + selection: [1, 1] + }; + } + } + } + }); + + this.add("braces", "deletion", function(state, action, editor, session, range) { + if (editor.completer && editor.completer.popup && editor.completer.popup.isOpen) { + return; + } + var selected = session.doc.getTextRange(range); + if (!range.isMultiLine() && selected == '{') { + initContext(editor); + var line = session.doc.getLine(range.start.row); + var rightChar = line.substring(range.start.column + 1, range.start.column + 2); + if (rightChar == '}') { + range.end.column++; + return range; + } + } + }); + + this.add("brackets", "insertion", function(state, action, editor, session, text) { + if (editor.completer && editor.completer.popup && editor.completer.popup.isOpen) { + return; + } + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + var lastChar = line[cursor.column-1]; + if (lastChar === '\\') { + return; + } + if (text == '[') { + initContext(editor); + var selection = editor.getSelectionRange(); + var selected = session.doc.getTextRange(selection); + if (selected !== "" && editor.getWrapBehavioursEnabled()) { + return getWrapped(selection, selected, '[', ']'); + } else if (LatexBehaviour.isSaneInsertion(editor, session)) { + LatexBehaviour.recordAutoInsert(editor, session, "]"); + return { + text: '[]', + selection: [1, 1] + }; + } + } else if (text == ']') { + initContext(editor); + var rightChar = line.substring(cursor.column, cursor.column + 1); + if (rightChar == ']') { + var matching = session.$findOpeningBracket(']', {column: cursor.column + 1, row: cursor.row}); + if (matching !== null && LatexBehaviour.isAutoInsertedClosing(cursor, line, text)) { + LatexBehaviour.popAutoInsertedClosing(); + return { + text: '', + selection: [1, 1] + }; + } + } + } + }); + + this.add("brackets", "deletion", function(state, action, editor, session, range) { + if (editor.completer && editor.completer.popup && editor.completer.popup.isOpen) { + return; + } + var selected = session.doc.getTextRange(range); + if (!range.isMultiLine() && selected == '[') { + initContext(editor); + var line = session.doc.getLine(range.start.row); + var rightChar = line.substring(range.start.column + 1, range.start.column + 2); + if (rightChar == ']') { + range.end.column++; + return range; + } + } + }); + + this.add("dollars", "insertion", function(state, action, editor, session, text) { + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + var lastChar = line[cursor.column-1]; + if (lastChar === '\\') { + return; + } + if (text == '$') { + if (this.lineCommentStart && this.lineCommentStart.indexOf(text) != -1) + return; + initContext(editor); + var quote = text; + var selection = editor.getSelectionRange(); + var selected = session.doc.getTextRange(selection); + if (selected !== "" && selected !== "$" && editor.getWrapBehavioursEnabled()) { + return getWrapped(selection, selected, quote, quote); + } else if (!selected) { + var leftChar = line.substring(cursor.column-1, cursor.column); + var rightChar = line.substring(cursor.column, cursor.column + 1); + + var token = session.getTokenAt(cursor.row, cursor.column); + var rightToken = session.getTokenAt(cursor.row, cursor.column + 1); + + var stringBefore = token && /string|escape/.test(token.type); + var stringAfter = !rightToken || /string|escape/.test(rightToken.type); + + var pair; + if (rightChar == quote) { + pair = stringBefore !== stringAfter; + if (pair && /string\.end/.test(rightToken.type)) + pair = false; + } else { + if (stringBefore && !stringAfter) + return null; // wrap string with different quote + if (stringBefore && stringAfter) + return null; // do not pair quotes inside strings + var wordRe = session.$mode.tokenRe; + wordRe.lastIndex = 0; + var isWordBefore = wordRe.test(leftChar); + wordRe.lastIndex = 0; + var isWordAfter = wordRe.test(leftChar); + if (isWordBefore || isWordAfter) + return null; // before or after alphanumeric + if (rightChar && !/[\s;,.})\]\\]/.test(rightChar)) + return null; // there is rightChar and it isn't closing + pair = true; + } + return { + text: pair ? quote + quote : "", + selection: [1,1] + }; + } + } + }); + + this.add("dollars", "deletion", function(state, action, editor, session, range) { + var selected = session.doc.getTextRange(range); + if (!range.isMultiLine() && (selected == '$')) { + initContext(editor); + var line = session.doc.getLine(range.start.row); + var rightChar = line.substring(range.start.column + 1, range.start.column + 2); + if (rightChar == selected) { + range.end.column++; + return range; + } + } + }); + +}; + + +LatexBehaviour.isSaneInsertion = function(editor, session) { + var cursor = editor.getCursorPosition(); + var iterator = new TokenIterator(session, cursor.row, cursor.column); + if (!this.$matchTokenType(iterator.getCurrentToken() || "text", SAFE_INSERT_IN_TOKENS)) { + var iterator2 = new TokenIterator(session, cursor.row, cursor.column + 1); + if (!this.$matchTokenType(iterator2.getCurrentToken() || "text", SAFE_INSERT_IN_TOKENS)) + return false; + } + iterator.stepForward(); + return iterator.getCurrentTokenRow() !== cursor.row || + this.$matchTokenType(iterator.getCurrentToken() || "text", SAFE_INSERT_BEFORE_TOKENS); +}; + +LatexBehaviour.$matchTokenType = function(token, types) { + return types.indexOf(token.type || token) > -1; +}; + +LatexBehaviour.recordAutoInsert = function(editor, session, bracket) { + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + if (!this.isAutoInsertedClosing(cursor, line, context.autoInsertedLineEnd[0])) + context.autoInsertedBrackets = 0; + context.autoInsertedRow = cursor.row; + context.autoInsertedLineEnd = bracket + line.substr(cursor.column); + context.autoInsertedBrackets++; +}; + +LatexBehaviour.isAutoInsertedClosing = function(cursor, line, bracket) { + return context.autoInsertedBrackets > 0 && + cursor.row === context.autoInsertedRow && + bracket === context.autoInsertedLineEnd[0] && + line.substr(cursor.column) === context.autoInsertedLineEnd; +}; + +LatexBehaviour.popAutoInsertedClosing = function() { + context.autoInsertedLineEnd = context.autoInsertedLineEnd.substr(1); + context.autoInsertedBrackets--; +}; + + +oop.inherits(LatexBehaviour, Behaviour); + +exports.LatexBehaviour = LatexBehaviour; +}); + +ace.define("ace/mode/latex_beta",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/latex_highlight_rules","ace/mode/folding/latex","ace/range","ace/worker/worker_client","ace/mode/behaviour/latex"], function(require, exports, module) { "use strict"; var oop = require("../lib/oop"); @@ -204,6 +479,7 @@ var LatexHighlightRules = require("./latex_highlight_rules").LatexHighlightRules var LatexFoldMode = require("./folding/latex").FoldMode; var Range = require("../range").Range; var WorkerClient = require("ace/worker/worker_client").WorkerClient; +var LatexBehaviour = require("./behaviour/latex").LatexBehaviour; var createLatexWorker = function (session) { var doc = session.getDocument(); @@ -361,6 +637,7 @@ var createLatexWorker = function (session) { var Mode = function() { this.HighlightRules = LatexHighlightRules; this.foldingRules = new LatexFoldMode(); + this.$behaviour = new LatexBehaviour(); this.createWorker = createLatexWorker; }; oop.inherits(Mode, TextMode); diff --git a/services/web/public/stylesheets/app/editor/left-menu.less b/services/web/public/stylesheets/app/editor/left-menu.less index 8172c44b90..438ef9eb77 100644 --- a/services/web/public/stylesheets/app/editor/left-menu.less +++ b/services/web/public/stylesheets/app/editor/left-menu.less @@ -68,27 +68,33 @@ form.settings { label { - float: left; font-weight: normal; color: @gray-dark; + flex: 1 0 50%; margin-bottom: 0; + margin-top: 9px; + padding-right: 5px; + white-space: nowrap; } select { - float: right; width: 50%; - // height: 30px; - // margin: 2px 0; - // background: none; - // border: none; - // box-shadow: none; - // color: @link-color; - // cursor: pointer; - // font-size: 14px; - // font-weight: 700; + flex: 0 0 50%; + margin: 9px 0; } .form-controls { clear: both; - padding: (@line-height-computed / 4); + padding: 0 9px; + display: flex; + flex-wrap: wrap; + align-items: baseline; + justify-content: flex-end; + border-bottom: solid 1px rgba(0, 0, 0, 0.07); + &:first-child { + margin-top: -9px; + } + &:last-child { + border-bottom: 0; + } &:hover { background-color: @link-color; // select.form-control {