mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge branch 'master' into sk-fully-hide-trackchanges
This commit is contained in:
commit
05c5b4f9c5
20 changed files with 1196 additions and 145 deletions
|
@ -29,8 +29,12 @@ module.exports = AuthenticationManager =
|
||||||
callback null, null
|
callback null, null
|
||||||
|
|
||||||
setUserPassword: (user_id, password, callback = (error) ->) ->
|
setUserPassword: (user_id, password, callback = (error) ->) ->
|
||||||
if Settings.passwordStrengthOptions?.length?.max? and Settings.passwordStrengthOptions?.length?.max < password.length
|
if (Settings.passwordStrengthOptions?.length?.max? and
|
||||||
|
Settings.passwordStrengthOptions?.length?.max < password.length)
|
||||||
return callback("password is too long")
|
return callback("password is too long")
|
||||||
|
if (Settings.passwordStrengthOptions?.length?.min? and
|
||||||
|
Settings.passwordStrengthOptions?.length?.min > password.length)
|
||||||
|
return callback("password is too short")
|
||||||
|
|
||||||
bcrypt.genSalt BCRYPT_ROUNDS, (error, salt) ->
|
bcrypt.genSalt BCRYPT_ROUNDS, (error, salt) ->
|
||||||
return callback(error) if error?
|
return callback(error) if error?
|
||||||
|
|
|
@ -62,6 +62,15 @@ module.exports = SubscriptionUpdater =
|
||||||
invited_emails: email
|
invited_emails: email
|
||||||
}, callback
|
}, callback
|
||||||
|
|
||||||
|
deleteSubscription: (subscription_id, callback = (error) ->) ->
|
||||||
|
SubscriptionLocator.getSubscription subscription_id, (err, subscription) ->
|
||||||
|
return callback(err) if err?
|
||||||
|
affected_user_ids = [subscription.admin_id].concat(subscription.member_ids or [])
|
||||||
|
logger.log {subscription_id, affected_user_ids}, "deleting subscription and downgrading users"
|
||||||
|
Subscription.remove {_id: ObjectId(subscription_id)}, (err) ->
|
||||||
|
return callback(err) if err?
|
||||||
|
async.mapSeries affected_user_ids, SubscriptionUpdater._setUsersMinimumFeatures, callback
|
||||||
|
|
||||||
_createNewSubscription: (adminUser_id, callback)->
|
_createNewSubscription: (adminUser_id, callback)->
|
||||||
logger.log adminUser_id:adminUser_id, "creating new subscription"
|
logger.log adminUser_id:adminUser_id, "creating new subscription"
|
||||||
subscription = new Subscription(admin_id:adminUser_id)
|
subscription = new Subscription(admin_id:adminUser_id)
|
||||||
|
|
|
@ -74,6 +74,8 @@ module.exports = UserController =
|
||||||
user.ace.fontSize = req.body.fontSize
|
user.ace.fontSize = req.body.fontSize
|
||||||
if req.body.autoComplete?
|
if req.body.autoComplete?
|
||||||
user.ace.autoComplete = req.body.autoComplete
|
user.ace.autoComplete = req.body.autoComplete
|
||||||
|
if req.body.autoPairDelimiters?
|
||||||
|
user.ace.autoPairDelimiters = req.body.autoPairDelimiters
|
||||||
if req.body.spellCheckLanguage?
|
if req.body.spellCheckLanguage?
|
||||||
user.ace.spellCheckLanguage = req.body.spellCheckLanguage
|
user.ace.spellCheckLanguage = req.body.spellCheckLanguage
|
||||||
if req.body.pdfViewer?
|
if req.body.pdfViewer?
|
||||||
|
|
|
@ -11,6 +11,7 @@ async = require("async")
|
||||||
Modules = require "./Modules"
|
Modules = require "./Modules"
|
||||||
Url = require "url"
|
Url = require "url"
|
||||||
PackageVersions = require "./PackageVersions"
|
PackageVersions = require "./PackageVersions"
|
||||||
|
htmlEncoder = new require("node-html-encoder").Encoder("numerical")
|
||||||
fingerprints = {}
|
fingerprints = {}
|
||||||
Path = require 'path'
|
Path = require 'path'
|
||||||
|
|
||||||
|
@ -151,9 +152,10 @@ module.exports = (app, webRouter, privateApiRouter, publicApiRouter)->
|
||||||
next()
|
next()
|
||||||
|
|
||||||
webRouter.use (req, res, next)->
|
webRouter.use (req, res, next)->
|
||||||
res.locals.translate = (key, vars = {}) ->
|
res.locals.translate = (key, vars = {}, htmlEncode = false) ->
|
||||||
vars.appName = Settings.appName
|
vars.appName = Settings.appName
|
||||||
req.i18n.translate(key, vars)
|
str = req.i18n.translate(key, vars)
|
||||||
|
if htmlEncode then htmlEncoder.htmlEncode(str) else str
|
||||||
# Don't include the query string parameters, otherwise Google
|
# Don't include the query string parameters, otherwise Google
|
||||||
# treats ?nocdn=true as the canonical version
|
# treats ?nocdn=true as the canonical version
|
||||||
res.locals.currentUrl = Url.parse(req.originalUrl).pathname
|
res.locals.currentUrl = Url.parse(req.originalUrl).pathname
|
||||||
|
|
|
@ -7,11 +7,15 @@ script(type='text/ng-template', id='supportModalTemplate')
|
||||||
) ×
|
) ×
|
||||||
h3 #{translate("contact_us")}
|
h3 #{translate("contact_us")}
|
||||||
.modal-body.contact-us-modal
|
.modal-body.contact-us-modal
|
||||||
|
form(name="contactForm")
|
||||||
span(ng-show="sent == false")
|
span(ng-show="sent == false")
|
||||||
|
.alert.alert-danger(ng-show="error") Something went wrong sending your request :(
|
||||||
label
|
label
|
||||||
| #{translate("subject")}
|
| #{translate("subject")}
|
||||||
.form-group
|
.form-group
|
||||||
input.field.text.medium.span8.form-control(
|
input.field.text.medium.span8.form-control(
|
||||||
|
name="subject",
|
||||||
|
required
|
||||||
ng-model="form.subject",
|
ng-model="form.subject",
|
||||||
ng-model-options="{ updateOn: 'default blur', debounce: {'default': 350, 'blur': 0} }"
|
ng-model-options="{ updateOn: 'default blur', debounce: {'default': 350, 'blur': 0} }"
|
||||||
maxlength='255',
|
maxlength='255',
|
||||||
|
@ -27,7 +31,15 @@ script(type='text/ng-template', id='supportModalTemplate')
|
||||||
label.desc(ng-show="'"+getUserEmail()+"'.length < 1")
|
label.desc(ng-show="'"+getUserEmail()+"'.length < 1")
|
||||||
| #{translate("email")}
|
| #{translate("email")}
|
||||||
.form-group(ng-show="'"+getUserEmail()+"'.length < 1")
|
.form-group(ng-show="'"+getUserEmail()+"'.length < 1")
|
||||||
input.field.text.medium.span8.form-control(ng-model="form.email", ng-init="form.email = '"+getUserEmail()+"'", type='email', spellcheck='false', value='', maxlength='255', tabindex='2')
|
input.field.text.medium.span8.form-control(
|
||||||
|
name="email",
|
||||||
|
required
|
||||||
|
ng-model="form.email",
|
||||||
|
ng-init="form.email = '"+getUserEmail()+"'",
|
||||||
|
type='email', spellcheck='false',
|
||||||
|
value='',
|
||||||
|
maxlength='255',
|
||||||
|
tabindex='2')
|
||||||
label#title12.desc
|
label#title12.desc
|
||||||
| #{translate("project_url")} (#{translate("optional")})
|
| #{translate("project_url")} (#{translate("optional")})
|
||||||
.form-group
|
.form-group
|
||||||
|
@ -35,8 +47,21 @@ script(type='text/ng-template', id='supportModalTemplate')
|
||||||
label.desc
|
label.desc
|
||||||
| #{translate("contact_message_label")}
|
| #{translate("contact_message_label")}
|
||||||
.form-group
|
.form-group
|
||||||
textarea.field.text.medium.span8.form-control(ng-model="form.message",type='text', value='', tabindex='4', onkeyup='')
|
textarea.field.text.medium.span8.form-control(
|
||||||
|
name="body",
|
||||||
|
required
|
||||||
|
ng-model="form.message",
|
||||||
|
type='text',
|
||||||
|
value='',
|
||||||
|
tabindex='4',
|
||||||
|
onkeyup=''
|
||||||
|
)
|
||||||
.form-group.text-center
|
.form-group.text-center
|
||||||
input.btn-success.btn.btn-lg(type='submit', ng-disabled="sending", ng-click="contactUs()" value=translate("contact_us"))
|
input.btn-success.btn.btn-lg(
|
||||||
|
type='submit',
|
||||||
|
ng-disabled="contactForm.$invalid || sending",
|
||||||
|
ng-click="contactUs()"
|
||||||
|
value=translate("contact_us")
|
||||||
|
)
|
||||||
span(ng-show="sent")
|
span(ng-show="sent")
|
||||||
p #{translate("request_sent_thank_you")}
|
p #{translate("request_sent_thank_you")}
|
||||||
|
|
|
@ -82,7 +82,7 @@ div.full-size(
|
||||||
i.fa.fa-long-arrow-right
|
i.fa.fa-long-arrow-right
|
||||||
br
|
br
|
||||||
a.btn.btn-default.btn-xs(
|
a.btn.btn-default.btn-xs(
|
||||||
tooltip-html="'"+translate('go_to_pdf_location_in_code')+"'"
|
tooltip-html="'"+translate('go_to_pdf_location_in_code', {}, true)+"'"
|
||||||
tooltip-placement="right"
|
tooltip-placement="right"
|
||||||
tooltip-append-to-body="true"
|
tooltip-append-to-body="true"
|
||||||
ng-click="syncToCode()"
|
ng-click="syncToCode()"
|
||||||
|
|
|
@ -226,8 +226,8 @@ module.exports = settings =
|
||||||
# passwordStrengthOptions:
|
# passwordStrengthOptions:
|
||||||
# pattern: "aA$3"
|
# pattern: "aA$3"
|
||||||
# length:
|
# length:
|
||||||
# min: 1
|
# min: 6
|
||||||
# max: 10
|
# max: 128
|
||||||
|
|
||||||
# Email support
|
# Email support
|
||||||
# -------------
|
# -------------
|
||||||
|
|
1025
services/web/npm-shrinkwrap.json
generated
1025
services/web/npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load diff
|
@ -30,8 +30,8 @@
|
||||||
"ioredis": "^2.4.0",
|
"ioredis": "^2.4.0",
|
||||||
"jade": "~1.3.1",
|
"jade": "~1.3.1",
|
||||||
"ldapjs": "^0.7.1",
|
"ldapjs": "^0.7.1",
|
||||||
"logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#master",
|
|
||||||
"lodash": "^4.13.1",
|
"lodash": "^4.13.1",
|
||||||
|
"logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#master",
|
||||||
"lynx": "0.1.1",
|
"lynx": "0.1.1",
|
||||||
"marked": "^0.3.5",
|
"marked": "^0.3.5",
|
||||||
"method-override": "^2.3.3",
|
"method-override": "^2.3.3",
|
||||||
|
@ -39,8 +39,9 @@
|
||||||
"mimelib": "0.2.14",
|
"mimelib": "0.2.14",
|
||||||
"mocha": "1.17.1",
|
"mocha": "1.17.1",
|
||||||
"mongojs": "2.4.0",
|
"mongojs": "2.4.0",
|
||||||
"mongoose": "4.1.0",
|
"mongoose": "4.11.4",
|
||||||
"multer": "^0.1.8",
|
"multer": "^0.1.8",
|
||||||
|
"node-html-encoder": "0.0.2",
|
||||||
"nodemailer": "2.1.0",
|
"nodemailer": "2.1.0",
|
||||||
"nodemailer-sendgrid-transport": "^0.2.0",
|
"nodemailer-sendgrid-transport": "^0.2.0",
|
||||||
"nodemailer-ses-transport": "^1.3.0",
|
"nodemailer-ses-transport": "^1.3.0",
|
||||||
|
@ -48,23 +49,23 @@
|
||||||
"passport": "^0.3.2",
|
"passport": "^0.3.2",
|
||||||
"passport-ldapauth": "^0.6.0",
|
"passport-ldapauth": "^0.6.0",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
|
"passport-saml": "^0.15.0",
|
||||||
|
"pug": "^2.0.0-beta6",
|
||||||
"redis": "0.10.1",
|
"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.2",
|
||||||
"request": "^2.69.0",
|
"request": "^2.69.0",
|
||||||
"requests": "^0.1.7",
|
"requests": "^0.1.7",
|
||||||
"rimraf": "2.2.6",
|
"rimraf": "2.2.6",
|
||||||
|
"rolling-rate-limiter": "git+https://github.com/ShaneKilkelly/rolling-rate-limiter.git#master",
|
||||||
"sanitizer": "0.1.1",
|
"sanitizer": "0.1.1",
|
||||||
"sequelize": "^3.2.0",
|
"sequelize": "^3.2.0",
|
||||||
"settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0",
|
"settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0",
|
||||||
"sixpack-client": "^1.0.0",
|
"sixpack-client": "^1.0.0",
|
||||||
"temp": "^0.8.3",
|
"temp": "^0.8.3",
|
||||||
"underscore": "1.6.0",
|
"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",
|
"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": {
|
"devDependencies": {
|
||||||
"autoprefixer": "^6.6.1",
|
"autoprefixer": "^6.6.1",
|
||||||
|
|
|
@ -103,8 +103,8 @@ define [
|
||||||
defaultPasswordOpts =
|
defaultPasswordOpts =
|
||||||
pattern: ""
|
pattern: ""
|
||||||
length:
|
length:
|
||||||
min: 1
|
min: 6
|
||||||
max: 50
|
max: 128
|
||||||
allowEmpty: false
|
allowEmpty: false
|
||||||
allowAnyChars: false
|
allowAnyChars: false
|
||||||
isMasked: true
|
isMasked: true
|
||||||
|
@ -127,8 +127,6 @@ define [
|
||||||
[asyncFormCtrl, ngModelCtrl] = ctrl
|
[asyncFormCtrl, ngModelCtrl] = ctrl
|
||||||
|
|
||||||
ngModelCtrl.$parsers.unshift (modelValue) ->
|
ngModelCtrl.$parsers.unshift (modelValue) ->
|
||||||
|
|
||||||
|
|
||||||
isValid = passField.validatePass()
|
isValid = passField.validatePass()
|
||||||
email = asyncFormCtrl.getEmail() || window.usersEmail
|
email = asyncFormCtrl.getEmail() || window.usersEmail
|
||||||
if !isValid
|
if !isValid
|
||||||
|
@ -141,5 +139,8 @@ define [
|
||||||
if opts.length.max? and modelValue.length == opts.length.max
|
if opts.length.max? and modelValue.length == opts.length.max
|
||||||
isValid = false
|
isValid = false
|
||||||
scope.complexPasswordErrorMessage = "Maximum password length #{opts.length.max} reached"
|
scope.complexPasswordErrorMessage = "Maximum password length #{opts.length.max} reached"
|
||||||
|
if opts.length.min? and modelValue.length < opts.length.min
|
||||||
|
isValid = false
|
||||||
|
scope.complexPasswordErrorMessage = "Password too short, minimum #{opts.length.min}"
|
||||||
ngModelCtrl.$setValidity('complexPassword', isValid)
|
ngModelCtrl.$setValidity('complexPassword', isValid)
|
||||||
return modelValue
|
return modelValue
|
||||||
|
|
|
@ -11,6 +11,8 @@ define [
|
||||||
"ide/editor/directives/aceEditor/track-changes/TrackChangesManager"
|
"ide/editor/directives/aceEditor/track-changes/TrackChangesManager"
|
||||||
"ide/editor/directives/aceEditor/labels/LabelsManager"
|
"ide/editor/directives/aceEditor/labels/LabelsManager"
|
||||||
"ide/labels/services/labels"
|
"ide/labels/services/labels"
|
||||||
|
"ide/graphics/services/graphics"
|
||||||
|
"ide/preamble/services/preamble"
|
||||||
], (App, Ace, SearchBox, ModeList, UndoManager, AutoCompleteManager, SpellCheckManager, HighlightsManager, CursorPositionManager, TrackChangesManager, LabelsManager) ->
|
], (App, Ace, SearchBox, ModeList, UndoManager, AutoCompleteManager, SpellCheckManager, HighlightsManager, CursorPositionManager, TrackChangesManager, LabelsManager) ->
|
||||||
EditSession = ace.require('ace/edit_session').EditSession
|
EditSession = ace.require('ace/edit_session').EditSession
|
||||||
ModeList = ace.require('ace/ext/modelist')
|
ModeList = ace.require('ace/ext/modelist')
|
||||||
|
@ -33,10 +35,9 @@ define [
|
||||||
url = ace.config._moduleUrl(args...) + "?fingerprint=#{window.aceFingerprint}"
|
url = ace.config._moduleUrl(args...) + "?fingerprint=#{window.aceFingerprint}"
|
||||||
return url
|
return url
|
||||||
|
|
||||||
App.directive "aceEditor", ($timeout, $compile, $rootScope, event_tracking, localStorage, $cacheFactory, labels) ->
|
App.directive "aceEditor", ($timeout, $compile, $rootScope, event_tracking, localStorage, $cacheFactory, labels, graphics, preamble) ->
|
||||||
monkeyPatchSearch($rootScope, $compile)
|
monkeyPatchSearch($rootScope, $compile)
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
scope: {
|
scope: {
|
||||||
theme: "="
|
theme: "="
|
||||||
|
@ -102,7 +103,7 @@ define [
|
||||||
cursorPositionManager = new CursorPositionManager(scope, editor, element, localStorage)
|
cursorPositionManager = new CursorPositionManager(scope, editor, element, localStorage)
|
||||||
trackChangesManager = new TrackChangesManager(scope, editor, element)
|
trackChangesManager = new TrackChangesManager(scope, editor, element)
|
||||||
labelsManager = new LabelsManager(scope, editor, element, labels)
|
labelsManager = new LabelsManager(scope, editor, element, labels)
|
||||||
autoCompleteManager = new AutoCompleteManager(scope, editor, element, labelsManager)
|
autoCompleteManager = new AutoCompleteManager(scope, editor, element, labelsManager, graphics, preamble)
|
||||||
|
|
||||||
|
|
||||||
# Prevert Ctrl|Cmd-S from triggering save dialog
|
# Prevert Ctrl|Cmd-S from triggering save dialog
|
||||||
|
|
|
@ -17,7 +17,7 @@ define [
|
||||||
commandFragment?.match(/\\(\w+)\{/)?[1]
|
commandFragment?.match(/\\(\w+)\{/)?[1]
|
||||||
|
|
||||||
class AutoCompleteManager
|
class AutoCompleteManager
|
||||||
constructor: (@$scope, @editor, @element, @labelsManager) ->
|
constructor: (@$scope, @editor, @element, @labelsManager, @graphics, @preamble) ->
|
||||||
@suggestionManager = new SuggestionManager()
|
@suggestionManager = new SuggestionManager()
|
||||||
|
|
||||||
@monkeyPatchAutocomplete()
|
@monkeyPatchAutocomplete()
|
||||||
|
@ -44,6 +44,37 @@ define [
|
||||||
|
|
||||||
SnippetCompleter = new SnippetManager()
|
SnippetCompleter = new SnippetManager()
|
||||||
|
|
||||||
|
Graphics = @graphics
|
||||||
|
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)
|
||||||
|
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()
|
||||||
|
result = []
|
||||||
|
for graphic in Graphics.getGraphicsFiles()
|
||||||
|
path = graphic.path
|
||||||
|
for graphicsPath in graphicsPaths
|
||||||
|
if path.indexOf(graphicsPath) == 0
|
||||||
|
path = path.slice(graphicsPath.length)
|
||||||
|
break
|
||||||
|
result.push {
|
||||||
|
caption: "\\#{commandName}{#{path}#{if needsClosingBrace then '}' else ''}",
|
||||||
|
value: "\\#{commandName}{#{path}#{if needsClosingBrace then '}' else ''}",
|
||||||
|
meta: "graphic",
|
||||||
|
score: 50
|
||||||
|
}
|
||||||
|
callback null, result
|
||||||
|
|
||||||
labelsManager = @labelsManager
|
labelsManager = @labelsManager
|
||||||
LabelsCompleter =
|
LabelsCompleter =
|
||||||
getCompletions: (editor, session, pos, prefix, callback) ->
|
getCompletions: (editor, session, pos, prefix, callback) ->
|
||||||
|
@ -112,7 +143,13 @@ define [
|
||||||
else
|
else
|
||||||
callback null, result
|
callback null, result
|
||||||
|
|
||||||
@editor.completers = [@suggestionManager, SnippetCompleter, ReferencesCompleter, LabelsCompleter]
|
@editor.completers = [
|
||||||
|
@suggestionManager,
|
||||||
|
SnippetCompleter,
|
||||||
|
ReferencesCompleter,
|
||||||
|
LabelsCompleter,
|
||||||
|
GraphicsCompleter
|
||||||
|
]
|
||||||
|
|
||||||
disable: () ->
|
disable: () ->
|
||||||
@editor.setOptions({
|
@editor.setOptions({
|
||||||
|
@ -245,7 +282,22 @@ define [
|
||||||
editor.completer.autoSelect = true
|
editor.completer.autoSelect = true
|
||||||
editor.completer.showPopup(editor)
|
editor.completer.showPopup(editor)
|
||||||
editor.completer.cancelContextMenu()
|
editor.completer.cancelContextMenu()
|
||||||
$(editor.completer.popup?.container).css({'font-size': @$scope.fontSize + 'px'})
|
container = $(editor.completer.popup?.container)
|
||||||
|
container.css({'font-size': @$scope.fontSize + 'px'})
|
||||||
|
# Dynamically set width of autocomplete popup
|
||||||
|
if filtered = editor?.completer?.completions?.filtered
|
||||||
|
longestCaption = _.max(filtered.map( (c) -> c.caption.length ))
|
||||||
|
longestMeta = _.max(filtered.map( (c) -> c.meta.length ))
|
||||||
|
charWidth = editor.renderer.characterWidth
|
||||||
|
# between 280 and 700 px
|
||||||
|
width = Math.max(
|
||||||
|
Math.min(
|
||||||
|
Math.round(longestCaption*charWidth + longestMeta*charWidth + 5*charWidth),
|
||||||
|
700
|
||||||
|
),
|
||||||
|
280
|
||||||
|
)
|
||||||
|
container.css({width: "#{width}px"})
|
||||||
if editor.completer?.completions?.filtered?.length == 0
|
if editor.completer?.completions?.filtered?.length == 0
|
||||||
editor.completer.detach()
|
editor.completer.detach()
|
||||||
bindKey: "Ctrl-Space|Ctrl-Shift-Space|Alt-Space"
|
bindKey: "Ctrl-Space|Ctrl-Shift-Space|Alt-Space"
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
define [
|
||||||
|
"base"
|
||||||
|
], (App) ->
|
||||||
|
|
||||||
|
App.factory 'graphics', (ide) ->
|
||||||
|
|
||||||
|
Graphics =
|
||||||
|
getGraphicsFiles: () ->
|
||||||
|
graphicsFiles = []
|
||||||
|
ide.fileTreeManager.forEachEntity (entity, folder, path) ->
|
||||||
|
if entity.type == 'file' && entity?.name?.match?(/.*\.(png|jpg|jpeg|pdf|eps)/)
|
||||||
|
cloned = _.clone(entity)
|
||||||
|
cloned.path = path
|
||||||
|
graphicsFiles.push cloned
|
||||||
|
return graphicsFiles
|
||||||
|
|
||||||
|
return Graphics
|
|
@ -0,0 +1,22 @@
|
||||||
|
define [
|
||||||
|
"base"
|
||||||
|
], (App) ->
|
||||||
|
|
||||||
|
App.factory 'preamble', (ide) ->
|
||||||
|
|
||||||
|
Preamble =
|
||||||
|
getPreambleText: () ->
|
||||||
|
text = ide.editorManager.getCurrentDocValue().slice(0, 5000)
|
||||||
|
preamble = text.match(/([^]*)^\\begin\{document\}/m)?[1] || ""
|
||||||
|
return preamble
|
||||||
|
|
||||||
|
getGraphicsPaths: () ->
|
||||||
|
preamble = Preamble.getPreambleText()
|
||||||
|
graphicsPathsArgs = preamble.match(/\\graphicspath\{(.*)\}/)?[1] || ""
|
||||||
|
paths = []
|
||||||
|
re = /\{([^}]*)\}/g
|
||||||
|
while match = re.exec(graphicsPathsArgs)
|
||||||
|
paths.push(match[1])
|
||||||
|
return paths
|
||||||
|
|
||||||
|
return Preamble
|
|
@ -30,7 +30,7 @@ define [
|
||||||
$scope.suggestions = suggestions
|
$scope.suggestions = suggestions
|
||||||
|
|
||||||
$scope.contactUs = ->
|
$scope.contactUs = ->
|
||||||
if !$scope.form.email?
|
if !$scope.form.email? or $scope.form.email == ""
|
||||||
console.log "email not set"
|
console.log "email not set"
|
||||||
return
|
return
|
||||||
$scope.sending = true
|
$scope.sending = true
|
||||||
|
@ -46,7 +46,15 @@ define [
|
||||||
about: "<div>browser: #{platform?.name} #{platform?.version}</div>
|
about: "<div>browser: #{platform?.name} #{platform?.version}</div>
|
||||||
<div>os: #{platform?.os?.family} #{platform?.os?.version}</div>"
|
<div>os: #{platform?.os?.family} #{platform?.os?.version}</div>"
|
||||||
|
|
||||||
Groove.createTicket params, (err, json)->
|
Groove.createTicket params, (response)->
|
||||||
|
$scope.sending = false
|
||||||
|
if response.responseText == "" # Blocked request or similar
|
||||||
|
$scope.error = true
|
||||||
|
else
|
||||||
|
data = JSON.parse(response.responseText)
|
||||||
|
if data.errors?
|
||||||
|
$scope.error = true
|
||||||
|
else
|
||||||
$scope.sent = true
|
$scope.sent = true
|
||||||
$scope.$apply()
|
$scope.$apply()
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ define [
|
||||||
, 10
|
, 10
|
||||||
|
|
||||||
$scope.$watch((
|
$scope.$watch((
|
||||||
() -> $scope.projects.filter((project) -> !project.tags? or project.tags.length == 0).length
|
() -> $scope.projects.filter((project) -> (!project.tags? or project.tags.length == 0) and !project.archived).length
|
||||||
), (newVal) -> $scope.nUntagged = newVal)
|
), (newVal) -> $scope.nUntagged = newVal)
|
||||||
|
|
||||||
storedUIOpts = JSON.parse(localStorage("project_list"))
|
storedUIOpts = JSON.parse(localStorage("project_list"))
|
||||||
|
|
|
@ -504,3 +504,4 @@
|
||||||
height: auto;
|
height: auto;
|
||||||
border-bottom: 1px solid @modal-header-border-color;
|
border-bottom: 1px solid @modal-header-border-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -225,6 +225,10 @@
|
||||||
|
|
||||||
// Hide tabbable panes to start, show them when `.active`
|
// Hide tabbable panes to start, show them when `.active`
|
||||||
.tab-content {
|
.tab-content {
|
||||||
|
background-color: @nav-tabs-active-link-hover-bg;
|
||||||
|
border: 1px solid @nav-tabs-border-color;
|
||||||
|
border-top: none;
|
||||||
|
padding: @line-height-computed / 2;
|
||||||
> .tab-pane {
|
> .tab-pane {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,6 +116,24 @@ describe "AuthenticationManager", ->
|
||||||
expect(err).to.exist
|
expect(err).to.exist
|
||||||
done()
|
done()
|
||||||
|
|
||||||
|
it "should not start the bcrypt process", (done)->
|
||||||
|
@AuthenticationManager.setUserPassword @user_id, @password, (err)=>
|
||||||
|
@bcrypt.genSalt.called.should.equal false
|
||||||
|
@bcrypt.hash.called.should.equal false
|
||||||
|
done()
|
||||||
|
|
||||||
|
describe "too short", ->
|
||||||
|
beforeEach ->
|
||||||
|
@settings.passwordStrengthOptions =
|
||||||
|
length:
|
||||||
|
max:10
|
||||||
|
min:6
|
||||||
|
@password = "dsd"
|
||||||
|
|
||||||
|
it "should return and error", (done)->
|
||||||
|
@AuthenticationManager.setUserPassword @user_id, @password, (err)->
|
||||||
|
expect(err).to.exist
|
||||||
|
done()
|
||||||
|
|
||||||
it "should not start the bcrypt process", (done)->
|
it "should not start the bcrypt process", (done)->
|
||||||
@AuthenticationManager.setUserPassword @user_id, @password, (err)=>
|
@AuthenticationManager.setUserPassword @user_id, @password, (err)=>
|
||||||
|
|
|
@ -37,6 +37,7 @@ describe "SubscriptionUpdater", ->
|
||||||
constructor: (opts)->
|
constructor: (opts)->
|
||||||
subscription.admin_id = opts.admin_id
|
subscription.admin_id = opts.admin_id
|
||||||
return subscription
|
return subscription
|
||||||
|
@remove: sinon.stub().yields()
|
||||||
@SubscriptionModel.update = @updateStub
|
@SubscriptionModel.update = @updateStub
|
||||||
@SubscriptionModel.findAndModify = @findAndModifyStub
|
@SubscriptionModel.findAndModify = @findAndModifyStub
|
||||||
|
|
||||||
|
@ -230,3 +231,35 @@ describe "SubscriptionUpdater", ->
|
||||||
@ReferalAllocator.assignBonus.calledWith(@adminuser_id).should.equal true
|
@ReferalAllocator.assignBonus.calledWith(@adminuser_id).should.equal true
|
||||||
done()
|
done()
|
||||||
|
|
||||||
|
describe "deleteSubscription", ->
|
||||||
|
beforeEach (done) ->
|
||||||
|
@subscription_id = ObjectId().toString()
|
||||||
|
@subscription = {
|
||||||
|
"mock": "subscription",
|
||||||
|
admin_id: ObjectId(),
|
||||||
|
member_ids: [ ObjectId(), ObjectId(), ObjectId() ]
|
||||||
|
}
|
||||||
|
@SubscriptionLocator.getSubscription = sinon.stub().yields(null, @subscription)
|
||||||
|
@SubscriptionUpdater._setUsersMinimumFeatures = sinon.stub().yields()
|
||||||
|
@SubscriptionUpdater.deleteSubscription @subscription_id, done
|
||||||
|
|
||||||
|
it "should look up the subscription", ->
|
||||||
|
@SubscriptionLocator.getSubscription
|
||||||
|
.calledWith(@subscription_id)
|
||||||
|
.should.equal true
|
||||||
|
|
||||||
|
it "should remove the subscription", ->
|
||||||
|
@SubscriptionModel.remove
|
||||||
|
.calledWith({_id: ObjectId(@subscription_id)})
|
||||||
|
.should.equal true
|
||||||
|
|
||||||
|
it "should downgrade the admin_id", ->
|
||||||
|
@SubscriptionUpdater._setUsersMinimumFeatures
|
||||||
|
.calledWith(@subscription.admin_id)
|
||||||
|
.should.equal true
|
||||||
|
|
||||||
|
it "should downgrade all of the members", ->
|
||||||
|
for user_id in @subscription.member_ids
|
||||||
|
@SubscriptionUpdater._setUsersMinimumFeatures
|
||||||
|
.calledWith(user_id)
|
||||||
|
.should.equal true
|
||||||
|
|
Loading…
Reference in a new issue