Merge pull request #856 from sharelatex/ns-use-regex-test

Use regex test instead of string match
This commit is contained in:
Shane Kilkelly 2018-08-29 09:21:40 +01:00 committed by GitHub
commit 6ee2a83885
20 changed files with 117 additions and 122 deletions

View file

@ -217,8 +217,8 @@ module.exports = AuthenticationController =
value = if Object.keys(req.query).length > 0 then "#{req.path}?#{querystring.stringify(req.query)}" else "#{req.path}" value = if Object.keys(req.query).length > 0 then "#{req.path}?#{querystring.stringify(req.query)}" else "#{req.path}"
if ( if (
req.session? && req.session? &&
!value.match(new RegExp('^\/(socket.io|js|stylesheets|img)\/.*$')) && !/^\/(socket.io|js|stylesheets|img)\/.*$/.test(value) &&
!value.match(new RegExp('^.*\.(png|jpeg|svg)$')) !/^.*\.(png|jpeg|svg)$/.test(value)
) )
req.session.postLoginRedirect = value req.session.postLoginRedirect = value

View file

@ -262,7 +262,7 @@ module.exports = CompileController =
if req.query?.pdfng if req.query?.pdfng
newHeaders = {} newHeaders = {}
for h, v of req.headers for h, v of req.headers
newHeaders[h] = req.headers[h] if h.match /^(If-|Range)/i newHeaders[h] = req.headers[h] if /^(If-|Range)/i.test(h)
options.headers = newHeaders options.headers = newHeaders
proxy = request(options) proxy = request(options)
proxy.pipe(res) proxy.pipe(res)

View file

@ -80,7 +80,7 @@ module.exports = ProjectFileAgent = {
path: source_entity_path path: source_entity_path
}, (err, entity, type) -> }, (err, entity, type) ->
if err? if err?
if err.toString().match(/^not found.*/) if /^not found.*/.test(err.toString())
err = new SourceFileNotFoundError() err = new SourceFileNotFoundError()
return callback(err) return callback(err)
callback(null, project, entity, type) callback(null, project, entity, type)

View file

@ -466,6 +466,6 @@ THEME_LIST = []
do generateThemeList = () -> do generateThemeList = () ->
files = fs.readdirSync __dirname + '/../../../../public/js/' + PackageVersions.lib('ace') files = fs.readdirSync __dirname + '/../../../../public/js/' + PackageVersions.lib('ace')
for file in files for file in files
if file.slice(-2) == "js" and file.match(/^theme-/) if file.slice(-2) == "js" and /^theme-/.test(file)
cleanName = file.slice(0,-3).slice(6) cleanName = file.slice(0,-3).slice(6)
THEME_LIST.push cleanName THEME_LIST.push cleanName

View file

@ -294,7 +294,7 @@ module.exports = ProjectEntityMongoUpdateHandler = self =
# in the destination folder # in the destination folder
self._checkValidElementName destEntity, entity.name, (err)-> self._checkValidElementName destEntity, entity.name, (err)->
return callback(err) if err? return callback(err) if err?
if entityType.match(/folder/) if /folder/.test(entityType)
logger.log destFolderPath: destFolderPath.fileSystem, folderPath: entityPath.fileSystem, "checking folder is not moving into child folder" logger.log destFolderPath: destFolderPath.fileSystem, folderPath: entityPath.fileSystem, "checking folder is not moving into child folder"
isNestedFolder = destFolderPath.fileSystem.slice(0, entityPath.fileSystem.length) == entityPath.fileSystem isNestedFolder = destFolderPath.fileSystem.slice(0, entityPath.fileSystem.length) == entityPath.fileSystem
if isNestedFolder if isNestedFolder

View file

@ -20,8 +20,8 @@ module.exports = ProjectRootDocManager =
# Previously /.*\\documentclass/ would totally lock up on lines of 500kb (data text files :() # Previously /.*\\documentclass/ would totally lock up on lines of 500kb (data text files :()
# This regex will only look from the start of the line, including whitespace so will return quickly # This regex will only look from the start of the line, including whitespace so will return quickly
# regardless of line length. # regardless of line length.
match = line.match /^\s*\\documentclass/ match = /^\s*\\documentclass/.test(line)
isRootDoc = Path.extname(path).match(/\.R?tex$/) and match isRootDoc = /\.R?tex$/.test(Path.extname(path)) and match
if isRootDoc if isRootDoc
rootDocId = doc?._id rootDocId = doc?._id
cb(rootDocId) cb(rootDocId)
@ -31,4 +31,3 @@ module.exports = ProjectRootDocManager =
ProjectEntityUpdateHandler.setRootDoc project_id, root_doc_id, callback ProjectEntityUpdateHandler.setRootDoc project_id, root_doc_id, callback
else else
callback() callback()

View file

@ -1,5 +1,5 @@
# This file is shared between the frontend and server code of web, so that # This file is shared between the frontend and server code of web, so that
# filename validation is the same in both implementations. # filename validation is the same in both implementations.
# Both copies must be kept in sync: # Both copies must be kept in sync:
# app/coffee/Features/Project/SafePath.coffee # app/coffee/Features/Project/SafePath.coffee
# public/coffee/ide/directives/SafePath.coffee # public/coffee/ide/directives/SafePath.coffee
@ -55,7 +55,7 @@ load = () ->
clean: (filename) -> clean: (filename) ->
filename = filename.replace BADCHAR_RX, '_' filename = filename.replace BADCHAR_RX, '_'
# for BADFILE_RX replace any matches with an equal number of underscores # for BADFILE_RX replace any matches with an equal number of underscores
filename = filename.replace BADFILE_RX, (match) -> filename = filename.replace BADFILE_RX, (match) ->
return new Array(match.length + 1).join("_") return new Array(match.length + 1).join("_")
# replace blocked filenames 'prototype' with '@prototype' # replace blocked filenames 'prototype' with '@prototype'
filename = filename.replace BLOCKEDFILE_RX, "@$1" filename = filename.replace BLOCKEDFILE_RX, "@$1"
@ -63,9 +63,9 @@ load = () ->
isCleanFilename: (filename) -> isCleanFilename: (filename) ->
return SafePath.isAllowedLength(filename) && return SafePath.isAllowedLength(filename) &&
not filename.match(BADCHAR_RX) && !BADCHAR_RX.test(filename) &&
not filename.match(BADFILE_RX) && !BADFILE_RX.test(filename) &&
not filename.match(BLOCKEDFILE_RX) !BLOCKEDFILE_RX.test(filename)
isAllowedLength: (pathname) -> isAllowedLength: (pathname) ->
return pathname.length > 0 && pathname.length <= MAX_PATH return pathname.length > 0 && pathname.length <= MAX_PATH
@ -73,4 +73,4 @@ load = () ->
if define? if define?
define [], load define [], load
else else
module.exports = load() module.exports = load()

View file

@ -22,9 +22,9 @@ module.exports = UserController =
getPersonalInfo: (req, res, next = (error) ->) -> getPersonalInfo: (req, res, next = (error) ->) ->
{user_id} = req.params {user_id} = req.params
if user_id.match(/^\d+$/) if /^\d+$/.test(user_id)
query = { "overleaf.id": parseInt(user_id, 10) } query = { "overleaf.id": parseInt(user_id, 10) }
else if user_id.match(/^[a-f0-9]{24}$/) else if /^[a-f0-9]{24}$/.test(user_id)
query = { _id: ObjectId(user_id) } query = { _id: ObjectId(user_id) }
else else
return res.send(400) return res.send(400)

View file

@ -198,9 +198,9 @@ define [
userAgent = navigator.userAgent userAgent = navigator.userAgent
ide.browserIsSafari = ( ide.browserIsSafari = (
userAgent && userAgent &&
userAgent.match(/.*Safari\/.*/) && /.*Safari\/.*/.test(userAgent) &&
!userAgent.match(/.*Chrome\/.*/) && !/.*Chrome\/.*/.test(userAgent) &&
!userAgent.match(/.*Chromium\/.*/) !/.*Chromium\/.*/.test(userAgent)
) )
catch err catch err
console.error err console.error err

View file

@ -1,5 +1,5 @@
# This file is shared between the frontend and server code of web, so that # This file is shared between the frontend and server code of web, so that
# filename validation is the same in both implementations. # filename validation is the same in both implementations.
# Both copies must be kept in sync: # Both copies must be kept in sync:
# app/coffee/Features/Project/SafePath.coffee # app/coffee/Features/Project/SafePath.coffee
# public/coffee/ide/directives/SafePath.coffee # public/coffee/ide/directives/SafePath.coffee
@ -55,17 +55,17 @@ load = () ->
clean: (filename) -> clean: (filename) ->
filename = filename.replace BADCHAR_RX, '_' filename = filename.replace BADCHAR_RX, '_'
# for BADFILE_RX replace any matches with an equal number of underscores # for BADFILE_RX replace any matches with an equal number of underscores
filename = filename.replace BADFILE_RX, (match) -> filename = filename.replace BADFILE_RX, (match) ->
return new Array(match.length + 1).join("_") return new Array(match.length + 1).join("_")
# replace blocked filenames 'prototype' with '@prototype' # replace blocked filenames 'prototype' with '@prototype'
filename = filename.replace BLOCKEDFILE_RX, "@$1" filename = filename.replace BLOCKEDFILE_RX, "@$1"
return filename return filename
isCleanFilename: (filename) -> isCleanFilename: (filename) ->
return SafePath.isAllowedLength(filename) && return SafePath.isAllowedLength(filename) and
not filename.match(BADCHAR_RX) && not BADCHAR_RX.test(filename) and
not filename.match(BADFILE_RX) && not BADFILE_RX.test(filename) and
not filename.match(BLOCKEDFILE_RX) not BLOCKEDFILE_RX.test(filename)
isAllowedLength: (pathname) -> isAllowedLength: (pathname) ->
return pathname.length > 0 && pathname.length <= MAX_PATH return pathname.length > 0 && pathname.length <= MAX_PATH
@ -73,4 +73,4 @@ load = () ->
if define? if define?
define [], load define [], load
else else
module.exports = load() module.exports = load()

View file

@ -36,7 +36,7 @@ define [
@$scope.$on "flush-changes", () => @$scope.$on "flush-changes", () =>
Document.flushAll() Document.flushAll()
@$scope.$watch "editor.wantTrackChanges", (value) => @$scope.$watch "editor.wantTrackChanges", (value) =>
return if !value? return if !value?
@_syncTrackChangesState(@$scope.editor.sharejs_doc) @_syncTrackChangesState(@$scope.editor.sharejs_doc)
@ -47,7 +47,7 @@ define [
@localStorage("editor.mode.#{@$scope.project_id}") == 'rich-text' @localStorage("editor.mode.#{@$scope.project_id}") == 'rich-text'
autoOpenDoc: () -> autoOpenDoc: () ->
open_doc_id = open_doc_id =
@ide.localStorage("doc.open_id.#{@$scope.project_id}") or @ide.localStorage("doc.open_id.#{@$scope.project_id}") or
@$scope.project.rootDoc_id @$scope.project.rootDoc_id
return if !open_doc_id? return if !open_doc_id?
@ -76,7 +76,7 @@ define [
setTimeout () => setTimeout () =>
@$scope.$broadcast "editor:gotoOffset", options.gotoOffset @$scope.$broadcast "editor:gotoOffset", options.gotoOffset
, 0 , 0
if doc.id == @$scope.editor.open_doc_id and !options.forceReopen if doc.id == @$scope.editor.open_doc_id and !options.forceReopen
@$scope.$apply () => @$scope.$apply () =>
@ -97,7 +97,7 @@ define [
"Sorry, something went wrong opening this document. Please try again." "Sorry, something went wrong opening this document. Please try again."
) )
return return
@_syncTrackChangesState(sharejs_doc) @_syncTrackChangesState(sharejs_doc)
@$scope.$broadcast "doc:opened" @$scope.$broadcast "doc:opened"
@ -131,12 +131,12 @@ define [
message = error message = error
else else
message = "" message = ""
if message.match "maxDocLength" if /maxDocLength/.test(message)
@ide.showGenericMessageModal( @ide.showGenericMessageModal(
"Document Too Long" "Document Too Long"
"Sorry, this file is too long to be edited manually. Please upload it directly." "Sorry, this file is too long to be edited manually. Please upload it directly."
) )
else if message.match "too many comments or tracked changes" else if /too many comments or tracked changes/.test(message)
@ide.showGenericMessageModal( @ide.showGenericMessageModal(
"Too many comments or tracked changes" "Too many comments or tracked changes"
"Sorry, this file has too many comments or tracked changes. Please try accepting or rejecting some existing changes, or resolving and deleting some comments." "Sorry, this file has too many comments or tracked changes. Please try accepting or rejecting some existing changes, or resolving and deleting some comments."
@ -165,13 +165,13 @@ define [
getCurrentDocId: () -> getCurrentDocId: () ->
@$scope.editor.open_doc_id @$scope.editor.open_doc_id
startIgnoringExternalUpdates: () -> startIgnoringExternalUpdates: () ->
@_ignoreExternalUpdates = true @_ignoreExternalUpdates = true
stopIgnoringExternalUpdates: () -> stopIgnoringExternalUpdates: () ->
@_ignoreExternalUpdates = false @_ignoreExternalUpdates = false
_syncTimeout: null _syncTimeout: null
_syncTrackChangesState: (doc) -> _syncTrackChangesState: (doc) ->
return if !doc? return if !doc?

View file

@ -416,10 +416,10 @@ define [
# see if we can lookup a suitable mode from ace # see if we can lookup a suitable mode from ace
# but fall back to text by default # but fall back to text by default
try try
if scope.fileName.match(/\.(Rtex|bbl)$/i) if /\.(Rtex|bbl)$/i.test(scope.fileName)
# recognise Rtex and bbl as latex # recognise Rtex and bbl as latex
mode = "ace/mode/latex" mode = "ace/mode/latex"
else if scope.fileName.match(/\.(sty|cls|clo)$/) else if /\.(sty|cls|clo)$/.test(scope.fileName)
# recognise some common files as tex # recognise some common files as tex
mode = "ace/mode/tex" mode = "ace/mode/tex"
else else
@ -437,7 +437,7 @@ define [
session.setUseWrapMode(true) session.setUseWrapMode(true)
# use syntax validation only when explicitly set # use syntax validation only when explicitly set
if scope.syntaxValidation? and syntaxValidationEnabled and !scope.fileName.match(/\.bib$/) if scope.syntaxValidation? and syntaxValidationEnabled and !/\.bib$/.test(scope.fileName)
session.setOption("useWorker", scope.syntaxValidation); session.setOption("useWorker", scope.syntaxValidation);
# now attach session to editor # now attach session to editor

View file

@ -163,12 +163,11 @@ define [
end = change.end end = change.end
{lineUpToCursor, commandFragment} = Helpers.getContext(@editor, end) {lineUpToCursor, commandFragment} = Helpers.getContext(@editor, end)
if ((i = lineUpToCursor.indexOf('%')) > -1 and lineUpToCursor[i-1] != '\\') if ((i = lineUpToCursor.indexOf('%')) > -1 and lineUpToCursor[i-1] != '\\')
console.log lineUpToCursor, i
return return
lastCharIsBackslash = lineUpToCursor.slice(-1) == "\\" lastCharIsBackslash = lineUpToCursor.slice(-1) == "\\"
lastTwoChars = lineUpToCursor.slice(-2) lastTwoChars = lineUpToCursor.slice(-2)
# Don't offer autocomplete on double-backslash, backslash-colon, etc # Don't offer autocomplete on double-backslash, backslash-colon, etc
if lastTwoChars.match(/^\\[^a-zA-Z]$/) if /^\\[^a-zA-Z]$/.test(lastTwoChars)
@editor?.completer?.detach?() @editor?.completer?.detach?()
return return
# Check that this change was made by us, not a collaborator # Check that this change was made by us, not a collaborator
@ -185,8 +184,8 @@ define [
, 0 , 0
if ( if (
change.action == "insert" and change.action == "insert" and
change.lines[0].match(/\\(\w+){}/)?[1].match( /(begin|end|[a-z]*ref|usepackage|[a-z]*cite[a-z]*|input|include)/.test(
/(begin|end|[a-z]*ref|usepackage|[a-z]*cite[a-z]*|input|include)/ change.lines[0].match(/\\(\w+){}/)?[1]
) )
) )
setTimeout () => setTimeout () =>
@ -209,7 +208,7 @@ define [
# If we are in \begin{it|}, then we need to remove the trailing } # If we are in \begin{it|}, then we need to remove the trailing }
# since it will be adding in with the autocomplete of \begin{item}... # since it will be adding in with the autocomplete of \begin{item}...
if this.completions.filterText.match(/^\\\w+{/) and nextChar == "}" if /^\\\w+{/.test(this.completions.filterText) and nextChar == "}"
editor.session.remove(range) editor.session.remove(range)
# Provide our own `insertMatch` implementation. # Provide our own `insertMatch` implementation.

View file

@ -6,41 +6,41 @@ define [
], (_, EventEmitter, ColorManager, AceShareJsCodec) -> ], (_, EventEmitter, ColorManager, AceShareJsCodec) ->
class TrackChangesManager class TrackChangesManager
Range = ace.require("ace/range").Range Range = ace.require("ace/range").Range
constructor: (@$scope, @editor, @element) -> constructor: (@$scope, @editor, @element) ->
window.trackChangesManager ?= @ window.trackChangesManager ?= @
@$scope.$watch "trackChanges", (track_changes) => @$scope.$watch "trackChanges", (track_changes) =>
return if !track_changes? return if !track_changes?
@setTrackChanges(track_changes) @setTrackChanges(track_changes)
@$scope.$watch "sharejsDoc", (doc, oldDoc) => @$scope.$watch "sharejsDoc", (doc, oldDoc) =>
return if !doc? return if !doc?
if oldDoc? if oldDoc?
@disconnectFromDoc(oldDoc) @disconnectFromDoc(oldDoc)
@connectToDoc(doc) @connectToDoc(doc)
@$scope.$on "comment:add", (e, thread_id, offset, length) => @$scope.$on "comment:add", (e, thread_id, offset, length) =>
@addCommentToSelection(thread_id, offset, length) @addCommentToSelection(thread_id, offset, length)
@$scope.$on "comment:select_line", (e) => @$scope.$on "comment:select_line", (e) =>
@selectLineIfNoSelection() @selectLineIfNoSelection()
@$scope.$on "changes:accept", (e, change_ids) => @$scope.$on "changes:accept", (e, change_ids) =>
@acceptChangeIds(change_ids) @acceptChangeIds(change_ids)
@$scope.$on "changes:reject", (e, change_ids) => @$scope.$on "changes:reject", (e, change_ids) =>
@rejectChangeIds(change_ids) @rejectChangeIds(change_ids)
@$scope.$on "comment:remove", (e, comment_id) => @$scope.$on "comment:remove", (e, comment_id) =>
@removeCommentId(comment_id) @removeCommentId(comment_id)
@$scope.$on "comment:resolve_threads", (e, thread_ids) => @$scope.$on "comment:resolve_threads", (e, thread_ids) =>
@hideCommentsByThreadIds(thread_ids) @hideCommentsByThreadIds(thread_ids)
@$scope.$on "comment:unresolve_thread", (e, thread_id) => @$scope.$on "comment:unresolve_thread", (e, thread_id) =>
@showCommentByThreadId(thread_id) @showCommentByThreadId(thread_id)
@$scope.$on "review-panel:recalculate-screen-positions", () => @$scope.$on "review-panel:recalculate-screen-positions", () =>
@recalculateReviewEntriesScreenPositions() @recalculateReviewEntriesScreenPositions()
@ -53,7 +53,7 @@ define [
@$scope.$evalAsync () => @$scope.$evalAsync () =>
changingSelection = false changingSelection = false
@updateFocus() @updateFocus()
onResize = () => onResize = () =>
@recalculateReviewEntriesScreenPositions() @recalculateReviewEntriesScreenPositions()
@ -99,7 +99,7 @@ define [
bindToAce() bindToAce()
else else
unbindFromAce() unbindFromAce()
disconnectFromDoc: (doc) -> disconnectFromDoc: (doc) ->
@changeIdToMarkerIdMap = {} @changeIdToMarkerIdMap = {}
doc.off "ranges:clear" doc.off "ranges:clear"
@ -111,18 +111,18 @@ define [
@$scope.sharejsDoc?.track_changes_as = window.user.id or "anonymous" @$scope.sharejsDoc?.track_changes_as = window.user.id or "anonymous"
else else
@$scope.sharejsDoc?.track_changes_as = null @$scope.sharejsDoc?.track_changes_as = null
connectToDoc: (doc) -> connectToDoc: (doc) ->
@rangesTracker = doc.ranges @rangesTracker = doc.ranges
@setTrackChanges(@$scope.trackChanges) @setTrackChanges(@$scope.trackChanges)
doc.on "ranges:dirty", () => doc.on "ranges:dirty", () =>
@updateAnnotations() @updateAnnotations()
doc.on "ranges:clear", () => doc.on "ranges:clear", () =>
@clearAnnotations() @clearAnnotations()
doc.on "ranges:redraw", () => doc.on "ranges:redraw", () =>
@redrawAnnotations() @redrawAnnotations()
clearAnnotations: () -> clearAnnotations: () ->
session = @editor.getSession() session = @editor.getSession()
for change_id, markers of @changeIdToMarkerIdMap for change_id, markers of @changeIdToMarkerIdMap
@ -139,9 +139,9 @@ define [
for comment in @rangesTracker.comments for comment in @rangesTracker.comments
@_onCommentAdded(comment) @_onCommentAdded(comment)
@broadcastChange() @broadcastChange()
_doneUpdateThisLoop: false _doneUpdateThisLoop: false
_pendingUpdates: false _pendingUpdates: false
updateAnnotations: () -> updateAnnotations: () ->
@ -161,9 +161,9 @@ define [
_doUpdateAnnotations: () -> _doUpdateAnnotations: () ->
dirty = @rangesTracker.getDirtyState() dirty = @rangesTracker.getDirtyState()
updateMarkers = false updateMarkers = false
for id, change of dirty.change.added for id, change of dirty.change.added
if change.op.i? if change.op.i?
@_onInsertAdded(change) @_onInsertAdded(change)
@ -177,7 +177,7 @@ define [
for id, change of dirty.change.moved for id, change of dirty.change.moved
updateMarkers = true updateMarkers = true
@_onChangeMoved(change) @_onChangeMoved(change)
for id, comment of dirty.comment.added for id, comment of dirty.comment.added
@_onCommentAdded(comment) @_onCommentAdded(comment)
for id, comment of dirty.comment.removed for id, comment of dirty.comment.removed
@ -185,7 +185,7 @@ define [
for id, comment of dirty.comment.moved for id, comment of dirty.comment.moved
updateMarkers = true updateMarkers = true
@_onCommentMoved(comment) @_onCommentMoved(comment)
@rangesTracker.resetDirtyState() @rangesTracker.resetDirtyState()
if updateMarkers if updateMarkers
@editor.renderer.updateBackMarkers() @editor.renderer.updateBackMarkers()
@ -195,18 +195,18 @@ define [
op = { c: content, p: offset, t: thread_id } op = { c: content, p: offset, t: thread_id }
# @rangesTracker.applyOp op # Will apply via sharejs # @rangesTracker.applyOp op # Will apply via sharejs
@$scope.sharejsDoc.submitOp op @$scope.sharejsDoc.submitOp op
addCommentToSelection: (thread_id, offset, length) -> addCommentToSelection: (thread_id, offset, length) ->
start = @_shareJsOffsetToAcePosition(offset) start = @_shareJsOffsetToAcePosition(offset)
end = @_shareJsOffsetToAcePosition(offset + length) end = @_shareJsOffsetToAcePosition(offset + length)
range = new Range(start.row, start.column, end.row, end.column) range = new Range(start.row, start.column, end.row, end.column)
content = @editor.session.getTextRange(range) content = @editor.session.getTextRange(range)
@addComment(offset, content, thread_id) @addComment(offset, content, thread_id)
selectLineIfNoSelection: () -> selectLineIfNoSelection: () ->
if @editor.selection.isEmpty() if @editor.selection.isEmpty()
@editor.selection.selectLine() @editor.selection.selectLine()
acceptChangeIds: (change_ids) -> acceptChangeIds: (change_ids) ->
@rangesTracker.removeChangeIds(change_ids) @rangesTracker.removeChangeIds(change_ids)
@updateAnnotations() @updateAnnotations()
@ -225,12 +225,12 @@ define [
# #
# foo quux baz # foo quux baz
# |--| -> insertion of "quux", op 1, at position 4 # |--| -> insertion of "quux", op 1, at position 4
# | -> deletion of "bar", op 2, pushed forward by "quux" to position 8 # | -> deletion of "bar", op 2, pushed forward by "quux" to position 8
# #
# When rejecting these changes at once, if the insertion is rejected first, we get unexpected # When rejecting these changes at once, if the insertion is rejected first, we get unexpected
# results. What happens is: # results. What happens is:
# #
# 1) Rejecting the insertion deletes the added word "quux", i.e., it removes 4 chars # 1) Rejecting the insertion deletes the added word "quux", i.e., it removes 4 chars
# starting from position 4; # starting from position 4;
# #
# "foo quux baz" -> "foo baz" # "foo quux baz" -> "foo baz"
@ -247,27 +247,27 @@ define [
# "foo bazbar" (note "bar" readded at position 8) # "foo bazbar" (note "bar" readded at position 8)
# #
# The issue happens because of step 1. To revert the insertion of "quux", 4 characters are deleted # The issue happens because of step 1. To revert the insertion of "quux", 4 characters are deleted
# from position 4. This includes the position where the deletion exists; when that position is # from position 4. This includes the position where the deletion exists; when that position is
# cleared, the RangesTracker considers that the deletion is gone and stops tracking/updating it. # cleared, the RangesTracker considers that the deletion is gone and stops tracking/updating it.
# As we still hold a reference to it, the code tries to revert it by readding the deleted text, but # As we still hold a reference to it, the code tries to revert it by readding the deleted text, but
# does so at the outdated position (position 8, which was valid when "quux" was present). # does so at the outdated position (position 8, which was valid when "quux" was present).
# #
# To avoid this kind of problem, we need to make sure that reverting operations doesn't affect # To avoid this kind of problem, we need to make sure that reverting operations doesn't affect
# subsequent operations that come after. Reverse sorting the operations based on position will # subsequent operations that come after. Reverse sorting the operations based on position will
# achieve it; in the case above, it makes sure that the the deletion is reverted first: # achieve it; in the case above, it makes sure that the the deletion is reverted first:
# #
# 1) Rejecting the deletion adds the deleted word "bar" at position 8 # 1) Rejecting the deletion adds the deleted word "bar" at position 8
# #
# "foo quux baz" -> "foo quuxbar baz" # "foo quux baz" -> "foo quuxbar baz"
# | -> deletion of "bar" is reverted by # | -> deletion of "bar" is reverted by
# reinserting "bar" at position 8 # reinserting "bar" at position 8
# #
# 2) Rejecting the insertion deletes the added word "quux", i.e., it removes 4 chars # 2) Rejecting the insertion deletes the added word "quux", i.e., it removes 4 chars
# starting from position 4 and achieves the expected result: # starting from position 4 and achieves the expected result:
# #
# "foo quuxbar baz" -> "foo bar baz" # "foo quuxbar baz" -> "foo bar baz"
# |--| -> 4 characters to be removed # |--| -> 4 characters to be removed
changes.sort((a, b) -> b.op.p - a.op.p) changes.sort((a, b) -> b.op.p - a.op.p)
session = @editor.getSession() session = @editor.getSession()
@ -303,7 +303,7 @@ define [
if resolve_ids[comment.op.t] if resolve_ids[comment.op.t]
@_onCommentRemoved(comment) @_onCommentRemoved(comment)
@broadcastChange() @broadcastChange()
showCommentByThreadId: (thread_id) -> showCommentByThreadId: (thread_id) ->
for comment in @rangesTracker?.comments or [] for comment in @rangesTracker?.comments or []
if comment.op.t == thread_id if comment.op.t == thread_id
@ -339,7 +339,7 @@ define [
return if change.action != "insert" return if change.action != "insert"
pasted_text = change.lines.join("\n") pasted_text = change.lines.join("\n")
paste_offset = @_aceRangeToShareJs(change.start) paste_offset = @_aceRangeToShareJs(change.start)
# We have to wait until the change has been processed by the range tracker, # We have to wait until the change has been processed by the range tracker,
# since if we move the ops into place beforehand, they will be moved again # since if we move the ops into place beforehand, they will be moved again
# when the changes are processed by the range tracker. This ranges:dirty # when the changes are processed by the range tracker. This ranges:dirty
# event is fired after the doc has applied the changes to the range tracker. # event is fired after the doc has applied the changes to the range tracker.
@ -374,7 +374,7 @@ define [
end = start end = start
expected_markers.push { marker_id: background_marker_id, start, end } expected_markers.push { marker_id: background_marker_id, start, end }
expected_markers.push { marker_id: callout_marker_id, start, end: start } expected_markers.push { marker_id: callout_marker_id, start, end: start }
for comment in @rangesTracker.comments for comment in @rangesTracker.comments
if @changeIdToMarkerIdMap[comment.id]? if @changeIdToMarkerIdMap[comment.id]?
{background_marker_id, callout_marker_id} = @changeIdToMarkerIdMap[comment.id] {background_marker_id, callout_marker_id} = @changeIdToMarkerIdMap[comment.id]
@ -382,7 +382,7 @@ define [
end = @_shareJsOffsetToAcePosition(comment.op.p + comment.op.c.length) end = @_shareJsOffsetToAcePosition(comment.op.p + comment.op.c.length)
expected_markers.push { marker_id: background_marker_id, start, end } expected_markers.push { marker_id: background_marker_id, start, end }
expected_markers.push { marker_id: callout_marker_id, start, end: start } expected_markers.push { marker_id: callout_marker_id, start, end: start }
for {marker_id, start, end} in expected_markers for {marker_id, start, end} in expected_markers
marker = markers[marker_id] marker = markers[marker_id]
delete markers[marker_id] delete markers[marker_id]
@ -391,11 +391,11 @@ define [
marker.range.end.row != end.row or marker.range.end.row != end.row or
marker.range.end.column != end.column marker.range.end.column != end.column
console.error "Change doesn't match marker anymore", {change, marker, start, end} console.error "Change doesn't match marker anymore", {change, marker, start, end}
for marker_id, marker of markers for marker_id, marker of markers
if marker.clazz.match("track-changes") if /track-changes/.test(marker.clazz)
console.error "Orphaned ace marker", marker console.error "Orphaned ace marker", marker
updateFocus: () -> updateFocus: () ->
selection = @editor.getSelectionRange() selection = @editor.getSelectionRange()
selection_start = @_aceRangeToShareJs(selection.start) selection_start = @_aceRangeToShareJs(selection.start)
@ -403,10 +403,10 @@ define [
entries = @_getCurrentDocEntries() entries = @_getCurrentDocEntries()
is_selection = (selection_start != selection_end) is_selection = (selection_start != selection_end)
@$scope.$emit "editor:focus:changed", selection_start, selection_end, is_selection @$scope.$emit "editor:focus:changed", selection_start, selection_end, is_selection
broadcastChange: () -> broadcastChange: () ->
@$scope.$emit "editor:track-changes:changed", @$scope.docId @$scope.$emit "editor:track-changes:changed", @$scope.docId
recalculateReviewEntriesScreenPositions: () -> recalculateReviewEntriesScreenPositions: () ->
session = @editor.getSession() session = @editor.getSession()
renderer = @editor.renderer renderer = @editor.renderer
@ -455,7 +455,7 @@ define [
first_row > @end.row or last_row < @start.row first_row > @end.row or last_row < @start.row
return @ return @
return ace_range return ace_range
_createCalloutMarker: (position, klass) -> _createCalloutMarker: (position, klass) ->
session = @editor.getSession() session = @editor.getSession()
callout_range = @_makeZeroWidthRange(position) callout_range = @_makeZeroWidthRange(position)
@ -486,21 +486,21 @@ define [
callout_marker_id = @_createCalloutMarker(position, "track-changes-deleted-marker-callout") callout_marker_id = @_createCalloutMarker(position, "track-changes-deleted-marker-callout")
@changeIdToMarkerIdMap[change.id] = { background_marker_id, callout_marker_id } @changeIdToMarkerIdMap[change.id] = { background_marker_id, callout_marker_id }
_onInsertRemoved: (change) -> _onInsertRemoved: (change) ->
{background_marker_id, callout_marker_id} = @changeIdToMarkerIdMap[change.id] {background_marker_id, callout_marker_id} = @changeIdToMarkerIdMap[change.id]
delete @changeIdToMarkerIdMap[change.id] delete @changeIdToMarkerIdMap[change.id]
session = @editor.getSession() session = @editor.getSession()
session.removeMarker background_marker_id session.removeMarker background_marker_id
session.removeMarker callout_marker_id session.removeMarker callout_marker_id
_onDeleteRemoved: (change) -> _onDeleteRemoved: (change) ->
{background_marker_id, callout_marker_id} = @changeIdToMarkerIdMap[change.id] {background_marker_id, callout_marker_id} = @changeIdToMarkerIdMap[change.id]
delete @changeIdToMarkerIdMap[change.id] delete @changeIdToMarkerIdMap[change.id]
session = @editor.getSession() session = @editor.getSession()
session.removeMarker background_marker_id session.removeMarker background_marker_id
session.removeMarker callout_marker_id session.removeMarker callout_marker_id
_onCommentAdded: (comment) -> _onCommentAdded: (comment) ->
if @rangesTracker.resolvedThreadIds[comment.op.t] if @rangesTracker.resolvedThreadIds[comment.op.t]
# Comment is resolved so shouldn't be displayed. # Comment is resolved so shouldn't be displayed.
@ -515,7 +515,7 @@ define [
background_marker_id = session.addMarker background_range, "track-changes-marker track-changes-comment-marker", "text" background_marker_id = session.addMarker background_range, "track-changes-marker track-changes-comment-marker", "text"
callout_marker_id = @_createCalloutMarker(start, "track-changes-comment-marker-callout") callout_marker_id = @_createCalloutMarker(start, "track-changes-comment-marker-callout")
@changeIdToMarkerIdMap[comment.id] = { background_marker_id, callout_marker_id } @changeIdToMarkerIdMap[comment.id] = { background_marker_id, callout_marker_id }
_onCommentRemoved: (comment) -> _onCommentRemoved: (comment) ->
if @changeIdToMarkerIdMap[comment.id]? if @changeIdToMarkerIdMap[comment.id]?
# Resolved comments may not have marker ids # Resolved comments may not have marker ids
@ -532,11 +532,11 @@ define [
_aceChangeToShareJs: (delta) -> _aceChangeToShareJs: (delta) ->
lines = @editor.getSession().getDocument().getLines 0, delta.start.row lines = @editor.getSession().getDocument().getLines 0, delta.start.row
return AceShareJsCodec.aceChangeToShareJs(delta, lines) return AceShareJsCodec.aceChangeToShareJs(delta, lines)
_shareJsOffsetToAcePosition: (offset) -> _shareJsOffsetToAcePosition: (offset) ->
lines = @editor.getSession().getDocument().getAllLines() lines = @editor.getSession().getDocument().getAllLines()
return AceShareJsCodec.shareJsOffsetToAcePosition(offset, lines) return AceShareJsCodec.shareJsOffsetToAcePosition(offset, lines)
_onChangeMoved: (change) -> _onChangeMoved: (change) ->
start = @_shareJsOffsetToAcePosition(change.op.p) start = @_shareJsOffsetToAcePosition(change.op.p)
if change.op.i? if change.op.i?
@ -544,12 +544,12 @@ define [
else else
end = start end = start
@_updateMarker(change.id, start, end) @_updateMarker(change.id, start, end)
_onCommentMoved: (comment) -> _onCommentMoved: (comment) ->
start = @_shareJsOffsetToAcePosition(comment.op.p) start = @_shareJsOffsetToAcePosition(comment.op.p)
end = @_shareJsOffsetToAcePosition(comment.op.p + comment.op.c.length) end = @_shareJsOffsetToAcePosition(comment.op.p + comment.op.c.length)
@_updateMarker(comment.id, start, end) @_updateMarker(comment.id, start, end)
_updateMarker: (change_id, start, end) -> _updateMarker: (change_id, start, end) ->
return if !@changeIdToMarkerIdMap[change_id]? return if !@changeIdToMarkerIdMap[change_id]?
session = @editor.getSession() session = @editor.getSession()
@ -563,4 +563,3 @@ define [
callout_marker = markers[callout_marker_id] callout_marker = markers[callout_marker_id]
callout_marker.range.start = start callout_marker.range.start = start
callout_marker.range.end = start callout_marker.range.end = start

View file

@ -88,7 +88,7 @@ module.exports = PgDb = (options) ->
client.query sql, values, (error, result) -> client.query sql, values, (error, result) ->
if !error? if !error?
callback?() callback?()
else if error.toString().match "duplicate key value violates unique constraint" else if /duplicate key value violates unique constraint/.test(error.toString())
callback? "Document already exists" callback? "Document already exists"
else else
callback? error?.message callback? error?.message

View file

@ -177,7 +177,7 @@ module.exports = Model = (db, options) ->
# The callback is called with the version of the document at which the op was applied. # The callback is called with the version of the document at which the op was applied.
# This is the op.v after transformation, and its doc.v - 1. # This is the op.v after transformation, and its doc.v - 1.
callback null, opData.v callback null, opData.v
# I need a decent strategy here for deciding whether or not to save the snapshot. # I need a decent strategy here for deciding whether or not to save the snapshot.
# #
# The 'right' strategy looks something like "Store the snapshot whenever the snapshot # The 'right' strategy looks something like "Store the snapshot whenever the snapshot
@ -219,13 +219,13 @@ module.exports = Model = (db, options) ->
dbMeta: dbMeta dbMeta: dbMeta
doc.opQueue = makeOpQueue docName, doc doc.opQueue = makeOpQueue docName, doc
refreshReapingTimeout docName refreshReapingTimeout docName
model.emit 'add', docName, data model.emit 'add', docName, data
callback null, doc for callback in callbacks if callbacks callback null, doc for callback in callbacks if callbacks
doc doc
# This is a little helper wrapper around db.getOps. It does two things: # This is a little helper wrapper around db.getOps. It does two things:
# #
# - If there's no database set, it returns an error to the callback # - If there's no database set, it returns an error to the callback
@ -371,7 +371,7 @@ module.exports = Model = (db, options) ->
@create = (docName, type, meta, callback) -> @create = (docName, type, meta, callback) ->
[meta, callback] = [{}, meta] if typeof meta is 'function' [meta, callback] = [{}, meta] if typeof meta is 'function'
return callback? 'Invalid document name' if docName.match /\// return callback? 'Invalid document name' if /\//.test(docName)
return callback? 'Document already exists' if docs[docName] return callback? 'Document already exists' if docs[docName]
type = types[type] if typeof type == 'string' type = types[type] if typeof type == 'string'
@ -400,7 +400,7 @@ module.exports = Model = (db, options) ->
# Perminantly deletes the specified document. # Perminantly deletes the specified document.
# If listeners are attached, they are removed. # If listeners are attached, they are removed.
# #
# The callback is called with (error) if there was an error. If error is null / undefined, the # The callback is called with (error) if there was an error. If error is null / undefined, the
# document was deleted. # document was deleted.
# #
@ -484,7 +484,7 @@ module.exports = Model = (db, options) ->
# Apply an op to the specified document. # Apply an op to the specified document.
# The callback is passed (error, applied version #) # The callback is passed (error, applied version #)
# opData = {op:op, v:v, meta:metadata} # opData = {op:op, v:v, meta:metadata}
# #
# Ops are queued before being applied so that the following code applies op C before op B: # Ops are queued before being applied so that the following code applies op C before op B:
# model.applyOp 'doc', OPA, -> model.applyOp 'doc', OPB # model.applyOp 'doc', OPA, -> model.applyOp 'doc', OPB
# model.applyOp 'doc', OPC # model.applyOp 'doc', OPC
@ -501,7 +501,7 @@ module.exports = Model = (db, options) ->
# TODO: op and meta should be combineable in the op that gets sent # TODO: op and meta should be combineable in the op that gets sent
@applyMetaOp = (docName, metaOpData, callback) -> @applyMetaOp = (docName, metaOpData, callback) ->
{path, value} = metaOpData.meta {path, value} = metaOpData.meta
return callback? "path should be an array" unless isArray path return callback? "path should be an array" unless isArray path
load docName, (error, doc) -> load docName, (error, doc) ->
@ -522,7 +522,7 @@ module.exports = Model = (db, options) ->
# #
# The callback is called once the listener is attached, but before any ops have been passed # The callback is called once the listener is attached, but before any ops have been passed
# to the listener. # to the listener.
# #
# This will _not_ edit the document metadata. # This will _not_ edit the document metadata.
# #
# If there are any listeners, we don't purge the document from the cache. But be aware, this behaviour # If there are any listeners, we don't purge the document from the cache. But be aware, this behaviour
@ -600,4 +600,3 @@ module.exports = Model = (db, options) ->
# Model inherits from EventEmitter. # Model inherits from EventEmitter.
Model:: = new EventEmitter Model:: = new EventEmitter

View file

@ -356,7 +356,7 @@ define [
qs.clsiserverid = response.clsiServerId qs.clsiserverid = response.clsiServerId
for file in response.outputFiles for file in response.outputFiles
if IGNORE_FILES.indexOf(file.path) == -1 if IGNORE_FILES.indexOf(file.path) == -1
isOutputFile = file.path.match(/^output\./) isOutputFile = /^output\./.test(file.path)
$scope.pdf.outputFiles.push { $scope.pdf.outputFiles.push {
# Turn 'output.blg' into 'blg file'. # Turn 'output.blg' into 'blg file'.
name: if isOutputFile then "#{file.path.replace(/^output\./, "")} file" else file.path name: if isOutputFile then "#{file.path.replace(/^output\./, "")} file" else file.path
@ -489,8 +489,7 @@ define [
doc = ide.editorManager.getCurrentDocValue() doc = ide.editorManager.getCurrentDocValue()
return null if !doc? return null if !doc?
for line in doc.split("\n") for line in doc.split("\n")
match = line.match /^[^%]*\\documentclass/ if /^[^%]*\\documentclass/.test(line)
if match
return ide.editorManager.getCurrentDocId() return ide.editorManager.getCurrentDocId()
return null return null

View file

@ -28,7 +28,7 @@ define [
# TODO need a proper url manipulation library to add to query string # TODO need a proper url manipulation library to add to query string
url = $scope.pdfSrc url = $scope.pdfSrc
# add 'pdfng=true' to show that we are using the angular pdfjs viewer # add 'pdfng=true' to show that we are using the angular pdfjs viewer
queryStringExists = url.match(/\?/) queryStringExists = /\?/.test(url)
url = url + (if not queryStringExists then '?' else '&') + 'pdfng=true' url = url + (if not queryStringExists then '?' else '&') + 'pdfng=true'
# for isolated compiles, load the pdf on-demand because nobody will overwrite it # for isolated compiles, load the pdf on-demand because nobody will overwrite it
onDemandLoading = true onDemandLoading = true
@ -307,7 +307,7 @@ define [
return unless scale? return unless scale?
origposition = angular.copy scope.position origposition = angular.copy scope.position
# console.log 'origposition', origposition # console.log 'origposition', origposition
if not spinnerTimer? if not spinnerTimer?
spinnerTimer = setTimeout () -> spinnerTimer = setTimeout () ->
spinner.add(element) spinner.add(element)
@ -384,7 +384,7 @@ define [
return if error == 'cancelled' return if error == 'cancelled'
# check if too many retries or file is missing # check if too many retries or file is missing
message = error?.message or error message = error?.message or error
if scope.loadCount > 3 || message.match(/^Missing PDF/i) || message.match(/^loading/i) if scope.loadCount > 3 || /^Missing PDF/i.test(message) || /^loading/i.test(message)
scope.$emit 'pdf:error:display' scope.$emit 'pdf:error:display'
return return
if scope.loadSuccess if scope.loadSuccess
@ -408,12 +408,12 @@ define [
element.scrollTop(currentScrollTop + delta) element.scrollTop(currentScrollTop + delta)
element.on 'mousedown', (e) -> element.on 'mousedown', (e) ->
# We're checking that the event target isn't the directive root element # We're checking that the event target isn't the directive root element
# to make sure that the click was within a PDF page - no point in showing # to make sure that the click was within a PDF page - no point in showing
# the text layer when the click is outside. # the text layer when the click is outside.
# If the user clicks a PDF page, the mousedown target will be the canvas # If the user clicks a PDF page, the mousedown target will be the canvas
# element (or the text layer one). Alternatively, if the event target is # element (or the text layer one). Alternatively, if the event target is
# the root element, we can assume that the user has clicked either the # the root element, we can assume that the user has clicked either the
# grey background area or the scrollbars. # grey background area or the scrollbars.
if e.target != element[0] and !_hasSelection() if e.target != element[0] and !_hasSelection()
element.addClass 'pdfjs-viewer-show-text' element.addClass 'pdfjs-viewer-show-text'
@ -444,7 +444,7 @@ define [
_hasSelection = () -> _hasSelection = () ->
selection = window.getSelection?() selection = window.getSelection?()
# check the selection collapsed state in preference to # check the selection collapsed state in preference to
# using selection.toString() as the latter is "" when # using selection.toString() as the latter is "" when
# the selection is hidden (e.g. while viewing logs) # the selection is hidden (e.g. while viewing logs)
return selection? and _isSelectionWithinPDF(selection) and !selection.isCollapsed return selection? and _isSelectionWithinPDF(selection) and !selection.isCollapsed

View file

@ -28,9 +28,9 @@ define [
# Only show the lines that have a highlighted match # Only show the lines that have a highlighted match
matching_lines = [] matching_lines = []
for line in lines for line in lines
if !line.match(/^\[edit\]/) if !/^\[edit\]/.test(line)
content += line + "\n" content += line + "\n"
if line.match(/<em>/) if /<em>/.test(line)
matching_lines.push line matching_lines.push line
content = matching_lines.join("\n...\n") content = matching_lines.join("\n...\n")
result = result =
@ -38,7 +38,7 @@ define [
url :"/learn/#{page_underscored}##{section_underscored}" url :"/learn/#{page_underscored}##{section_underscored}"
content: content content: content
return result return result
updateHits = (hits)-> updateHits = (hits)->
$scope.safeApply -> $scope.safeApply ->
$scope.hits = hits $scope.hits = hits
@ -48,7 +48,7 @@ define [
if !query? or query.length == 0 if !query? or query.length == 0
updateHits [] updateHits []
return return
algoliaSearch.searchWiki query, (err, response)-> algoliaSearch.searchWiki query, (err, response)->
if response.hits.length == 0 if response.hits.length == 0
updateHits [] updateHits []
@ -56,4 +56,4 @@ define [
hits = _.map response.hits, buildHitViewModel hits = _.map response.hits, buildHitViewModel
updateHits hits updateHits hits
App.controller "LearnController", () -> App.controller "LearnController", () ->

View file

@ -27,9 +27,9 @@ define [
# Only show the lines that have a highlighted match # Only show the lines that have a highlighted match
matching_lines = [] matching_lines = []
for line in lines for line in lines
if !line.match(/^\[edit\]/) if !/^\[edit\]/.test(line)
content += line + "\n" content += line + "\n"
if line.match(/<em>/) if /<em>/.test(line)
matching_lines.push line matching_lines.push line
content = matching_lines.join("\n...\n") content = matching_lines.join("\n...\n")
result = result =
@ -53,4 +53,4 @@ define [
updateHits [] updateHits []
else else
hits = _.map response.hits, buildHitViewModel hits = _.map response.hits, buildHitViewModel
updateHits hits updateHits hits