mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge branch 'master' into ja-review-panel
Conflicts: app/views/project/editor/editor.jade public/coffee/ide.coffee public/coffee/ide/editor/directives/aceEditor.coffee
This commit is contained in:
commit
8368577867
215 changed files with 1658 additions and 815 deletions
|
@ -11,27 +11,6 @@ module.exports = CollaboratorsEmailHandler =
|
||||||
"user_first_name=#{encodeURIComponent(project.owner_ref.first_name)}"
|
"user_first_name=#{encodeURIComponent(project.owner_ref.first_name)}"
|
||||||
].join("&")
|
].join("&")
|
||||||
|
|
||||||
notifyUserOfProjectShare: (project_id, email, callback)->
|
|
||||||
Project
|
|
||||||
.findOne(_id: project_id )
|
|
||||||
.select("name owner_ref")
|
|
||||||
.populate('owner_ref')
|
|
||||||
.exec (err, project)->
|
|
||||||
emailOptions =
|
|
||||||
to: email
|
|
||||||
replyTo: project.owner_ref.email
|
|
||||||
project:
|
|
||||||
name: project.name
|
|
||||||
url: "#{Settings.siteUrl}/project/#{project._id}?" + [
|
|
||||||
"project_name=#{encodeURIComponent(project.name)}"
|
|
||||||
"user_first_name=#{encodeURIComponent(project.owner_ref.first_name)}"
|
|
||||||
"new_email=#{encodeURIComponent(email)}"
|
|
||||||
"r=#{project.owner_ref.referal_id}" # Referal
|
|
||||||
"rs=ci" # referral source = collaborator invite
|
|
||||||
].join("&")
|
|
||||||
owner: project.owner_ref
|
|
||||||
EmailHandler.sendEmail "projectSharedWithYou", emailOptions, callback
|
|
||||||
|
|
||||||
notifyUserOfProjectInvite: (project_id, email, invite, callback)->
|
notifyUserOfProjectInvite: (project_id, email, invite, callback)->
|
||||||
Project
|
Project
|
||||||
.findOne(_id: project_id )
|
.findOne(_id: project_id )
|
||||||
|
|
|
@ -7,10 +7,18 @@ settings = require("settings-sharelatex")
|
||||||
|
|
||||||
templates = {}
|
templates = {}
|
||||||
|
|
||||||
|
|
||||||
templates.registered =
|
templates.registered =
|
||||||
subject: _.template "Activate your #{settings.appName} Account"
|
subject: _.template "Activate your #{settings.appName} Account"
|
||||||
layout: PersonalEmailLayout
|
layout: PersonalEmailLayout
|
||||||
type: "notification"
|
type: "notification"
|
||||||
|
plainTextTemplate: _.template """
|
||||||
|
Congratulations, you've just had an account created for you on #{settings.appName} with the email address "<%= to %>".
|
||||||
|
|
||||||
|
Click here to set your password and log in: <%= setNewPasswordUrl %>
|
||||||
|
|
||||||
|
If you have any questions or problems, please contact #{settings.adminEmail}
|
||||||
|
"""
|
||||||
compiledTemplate: _.template """
|
compiledTemplate: _.template """
|
||||||
<p>Congratulations, you've just had an account created for you on #{settings.appName} with the email address "<%= to %>".</p>
|
<p>Congratulations, you've just had an account created for you on #{settings.appName} with the email address "<%= to %>".</p>
|
||||||
|
|
||||||
|
@ -19,10 +27,24 @@ templates.registered =
|
||||||
<p>If you have any questions or problems, please contact <a href="mailto:#{settings.adminEmail}">#{settings.adminEmail}</a>.</p>
|
<p>If you have any questions or problems, please contact <a href="mailto:#{settings.adminEmail}">#{settings.adminEmail}</a>.</p>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
templates.canceledSubscription =
|
templates.canceledSubscription =
|
||||||
subject: _.template "ShareLaTeX thoughts"
|
subject: _.template "ShareLaTeX thoughts"
|
||||||
layout: PersonalEmailLayout
|
layout: PersonalEmailLayout
|
||||||
type:"lifecycle"
|
type:"lifecycle"
|
||||||
|
plainTextTemplate: _.template """
|
||||||
|
Hi <%= first_name %>,
|
||||||
|
|
||||||
|
I'm sorry to see you cancelled your ShareLaTeX premium account. Would you mind giving me some advice on what the site is lacking at the moment via this survey?:
|
||||||
|
|
||||||
|
https://sharelatex.typeform.com/to/f5lBiZ
|
||||||
|
|
||||||
|
Thank you in advance.
|
||||||
|
|
||||||
|
Henry
|
||||||
|
|
||||||
|
ShareLaTeX Co-founder
|
||||||
|
"""
|
||||||
compiledTemplate: _.template '''
|
compiledTemplate: _.template '''
|
||||||
<p>Hi <%= first_name %>,</p>
|
<p>Hi <%= first_name %>,</p>
|
||||||
|
|
||||||
|
@ -36,10 +58,26 @@ ShareLaTeX Co-founder
|
||||||
</p>
|
</p>
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
templates.passwordResetRequested =
|
templates.passwordResetRequested =
|
||||||
subject: _.template "Password Reset - #{settings.appName}"
|
subject: _.template "Password Reset - #{settings.appName}"
|
||||||
layout: NotificationEmailLayout
|
layout: NotificationEmailLayout
|
||||||
type:"notification"
|
type:"notification"
|
||||||
|
plainTextTemplate: _.template """
|
||||||
|
Password Reset
|
||||||
|
|
||||||
|
We got a request to reset your #{settings.appName} password.
|
||||||
|
|
||||||
|
Click this link to reset your password: <%= setNewPasswordUrl %>
|
||||||
|
|
||||||
|
If you ignore this message, your password won't be changed.
|
||||||
|
|
||||||
|
If you didn't request a password reset, let us know.
|
||||||
|
|
||||||
|
Thank you
|
||||||
|
|
||||||
|
#{settings.appName} - <%= siteUrl %>
|
||||||
|
"""
|
||||||
compiledTemplate: _.template """
|
compiledTemplate: _.template """
|
||||||
<h2>Password Reset</h2>
|
<h2>Password Reset</h2>
|
||||||
<p>
|
<p>
|
||||||
|
@ -66,26 +104,6 @@ If you didn't request a password reset, let us know.
|
||||||
<p> <a href="<%= siteUrl %>">#{settings.appName}</a></p>
|
<p> <a href="<%= siteUrl %>">#{settings.appName}</a></p>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
templates.projectSharedWithYou =
|
|
||||||
subject: _.template "<%= owner.email %> wants to share <%= project.name %> with you"
|
|
||||||
layout: NotificationEmailLayout
|
|
||||||
type:"notification"
|
|
||||||
compiledTemplate: _.template """
|
|
||||||
<p>Hi, <%= owner.email %> wants to share <a href="<%= project.url %>">'<%= project.name %>'</a> with you</p>
|
|
||||||
<center>
|
|
||||||
<div style="width:200px;background-color:#a93629;border:1px solid #e24b3b;border-radius:3px;padding:15px; margin:24px;">
|
|
||||||
<div style="padding-right:10px;padding-left:10px">
|
|
||||||
<a href="<%= project.url %>" style="text-decoration:none" target="_blank">
|
|
||||||
<span style= "font-size:16px;font-family:Helvetica,Arial;font-weight:400;color:#fff;white-space:nowrap;display:block; text-align:center">
|
|
||||||
View Project
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</center>
|
|
||||||
<p> Thank you</p>
|
|
||||||
<p> <a href="<%= siteUrl %>">#{settings.appName}</a></p>
|
|
||||||
"""
|
|
||||||
|
|
||||||
templates.projectInvite =
|
templates.projectInvite =
|
||||||
subject: _.template "<%= project.name %> - shared by <%= owner.email %>"
|
subject: _.template "<%= project.name %> - shared by <%= owner.email %>"
|
||||||
|
@ -113,10 +131,20 @@ Thank you
|
||||||
<p> <a href="<%= siteUrl %>">#{settings.appName}</a></p>
|
<p> <a href="<%= siteUrl %>">#{settings.appName}</a></p>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
templates.completeJoinGroupAccount =
|
templates.completeJoinGroupAccount =
|
||||||
subject: _.template "Verify Email to join <%= group_name %> group"
|
subject: _.template "Verify Email to join <%= group_name %> group"
|
||||||
layout: NotificationEmailLayout
|
layout: NotificationEmailLayout
|
||||||
type:"notification"
|
type:"notification"
|
||||||
|
plainTextTemplate: _.template """
|
||||||
|
Hi, please verify your email to join the <%= group_name %> and get your free premium account
|
||||||
|
|
||||||
|
Click this link to verify now: <%= completeJoinUrl %>
|
||||||
|
|
||||||
|
Thank You
|
||||||
|
|
||||||
|
#{settings.appName} - <%= siteUrl %>
|
||||||
|
"""
|
||||||
compiledTemplate: _.template """
|
compiledTemplate: _.template """
|
||||||
<p>Hi, please verify your email to join the <%= group_name %> and get your free premium account</p>
|
<p>Hi, please verify your email to join the <%= group_name %> and get your free premium account</p>
|
||||||
<center>
|
<center>
|
||||||
|
@ -134,6 +162,7 @@ templates.completeJoinGroupAccount =
|
||||||
<p> <a href="<%= siteUrl %>">#{settings.appName}</a></p>
|
<p> <a href="<%= siteUrl %>">#{settings.appName}</a></p>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
module.exports =
|
module.exports =
|
||||||
templates: templates
|
templates: templates
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,7 @@ Reporter = (res) ->
|
||||||
|
|
||||||
res.contentType("application/json")
|
res.contentType("application/json")
|
||||||
if failures.length > 0
|
if failures.length > 0
|
||||||
|
logger.err failures:failures, "health check failed"
|
||||||
res.send 500, JSON.stringify(results, null, 2)
|
res.send 500, JSON.stringify(results, null, 2)
|
||||||
else
|
else
|
||||||
res.send 200, JSON.stringify(results, null, 2)
|
res.send 200, JSON.stringify(results, null, 2)
|
||||||
|
|
|
@ -8,6 +8,7 @@ Settings = require 'settings-sharelatex'
|
||||||
logger = require('logger-sharelatex')
|
logger = require('logger-sharelatex')
|
||||||
GeoIpLookup = require("../../infrastructure/GeoIpLookup")
|
GeoIpLookup = require("../../infrastructure/GeoIpLookup")
|
||||||
SubscriptionDomainHandler = require("./SubscriptionDomainHandler")
|
SubscriptionDomainHandler = require("./SubscriptionDomainHandler")
|
||||||
|
UserGetter = require "../User/UserGetter"
|
||||||
|
|
||||||
module.exports = SubscriptionController =
|
module.exports = SubscriptionController =
|
||||||
|
|
||||||
|
@ -21,14 +22,25 @@ module.exports = SubscriptionController =
|
||||||
if req.query.v?
|
if req.query.v?
|
||||||
viewName = "#{viewName}_#{req.query.v}"
|
viewName = "#{viewName}_#{req.query.v}"
|
||||||
logger.log viewName:viewName, "showing plans page"
|
logger.log viewName:viewName, "showing plans page"
|
||||||
|
currentUser = null
|
||||||
GeoIpLookup.getCurrencyCode req.query?.ip || req.ip, (err, recomendedCurrency)->
|
GeoIpLookup.getCurrencyCode req.query?.ip || req.ip, (err, recomendedCurrency)->
|
||||||
return next(err) if err?
|
return next(err) if err?
|
||||||
|
render = () ->
|
||||||
res.render viewName,
|
res.render viewName,
|
||||||
title: "plans_and_pricing"
|
title: "plans_and_pricing"
|
||||||
plans: plans
|
plans: plans
|
||||||
baseUrl: baseUrl
|
baseUrl: baseUrl
|
||||||
gaExperiments: Settings.gaExperiments.plansPage
|
gaExperiments: Settings.gaExperiments.plansPage
|
||||||
recomendedCurrency:recomendedCurrency
|
recomendedCurrency:recomendedCurrency
|
||||||
|
shouldABTestPlans: currentUser == null or (currentUser?.signUpDate? and currentUser.signUpDate >= (new Date('2016-10-27')))
|
||||||
|
user_id = AuthenticationController.getLoggedInUserId(req)
|
||||||
|
if user_id?
|
||||||
|
UserGetter.getUser user_id, {signUpDate: 1}, (err, user) ->
|
||||||
|
return next(err) if err?
|
||||||
|
currentUser = user
|
||||||
|
render()
|
||||||
|
else
|
||||||
|
render()
|
||||||
|
|
||||||
#get to show the recurly.js page
|
#get to show the recurly.js page
|
||||||
paymentPage: (req, res, next) ->
|
paymentPage: (req, res, next) ->
|
||||||
|
|
|
@ -68,4 +68,3 @@ module.exports =
|
||||||
!plan.groupPlan and plan.annual and plan.planCode.indexOf("student") == -1
|
!plan.groupPlan and plan.annual and plan.planCode.indexOf("student") == -1
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
|
@ -15,10 +15,24 @@ settings = require "settings-sharelatex"
|
||||||
|
|
||||||
module.exports = UserController =
|
module.exports = UserController =
|
||||||
|
|
||||||
deleteUser: (req, res)->
|
tryDeleteUser: (req, res, next) ->
|
||||||
user_id = AuthenticationController.getLoggedInUserId(req)
|
user_id = AuthenticationController.getLoggedInUserId(req)
|
||||||
|
password = req.body.password
|
||||||
|
logger.log {user_id}, "trying to delete user account"
|
||||||
|
if !password? or password == ''
|
||||||
|
logger.err {user_id}, 'no password supplied for attempt to delete account'
|
||||||
|
return res.sendStatus(403)
|
||||||
|
AuthenticationManager.authenticate {_id: user_id}, password, (err, user) ->
|
||||||
|
if err?
|
||||||
|
logger.err {user_id}, 'error authenticating during attempt to delete account'
|
||||||
|
return next(err)
|
||||||
|
if !user
|
||||||
|
logger.err {user_id}, 'auth failed during attempt to delete account'
|
||||||
|
return res.sendStatus(403)
|
||||||
UserDeleter.deleteUser user_id, (err) ->
|
UserDeleter.deleteUser user_id, (err) ->
|
||||||
if !err?
|
if err?
|
||||||
|
logger.err {user_id}, "error while deleting user account"
|
||||||
|
return next(err)
|
||||||
req.session?.destroy()
|
req.session?.destroy()
|
||||||
res.sendStatus(200)
|
res.sendStatus(200)
|
||||||
|
|
||||||
|
@ -143,7 +157,7 @@ module.exports = UserController =
|
||||||
type:'success'
|
type:'success'
|
||||||
text:'Your password has been changed'
|
text:'Your password has been changed'
|
||||||
else
|
else
|
||||||
logger.log user: user, "current password wrong"
|
logger.log user_id: user_id, "current password wrong"
|
||||||
res.send
|
res.send
|
||||||
message:
|
message:
|
||||||
type:'error'
|
type:'error'
|
||||||
|
|
|
@ -39,7 +39,7 @@ pathList = [
|
||||||
["#{jsPath}ide.js"]
|
["#{jsPath}ide.js"]
|
||||||
["#{jsPath}main.js"]
|
["#{jsPath}main.js"]
|
||||||
["#{jsPath}libs.js"]
|
["#{jsPath}libs.js"]
|
||||||
["#{jsPath}#{ace}/ace.js","#{jsPath}#{ace}/mode-latex.js", "#{jsPath}#{ace}/snippets/latex.js"]
|
["#{jsPath}#{ace}/ace.js","#{jsPath}#{ace}/mode-latex.js","#{jsPath}#{ace}/worker-latex.js","#{jsPath}#{ace}/snippets/latex.js"]
|
||||||
["#{jsPath}libs/#{pdfjs}/pdf.js"]
|
["#{jsPath}libs/#{pdfjs}/pdf.js"]
|
||||||
["#{jsPath}libs/#{pdfjs}/pdf.worker.js"]
|
["#{jsPath}libs/#{pdfjs}/pdf.worker.js"]
|
||||||
["#{jsPath}libs/#{pdfjs}/compatibility.js"]
|
["#{jsPath}libs/#{pdfjs}/compatibility.js"]
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
module.exports =
|
module.exports =
|
||||||
user: (user) ->
|
user: (user) ->
|
||||||
|
if !user?
|
||||||
|
return null
|
||||||
if !user._id?
|
if !user._id?
|
||||||
user = {_id : user}
|
user = {_id : user}
|
||||||
return {
|
return {
|
||||||
|
@ -10,6 +12,8 @@ module.exports =
|
||||||
}
|
}
|
||||||
|
|
||||||
project: (project) ->
|
project: (project) ->
|
||||||
|
if !project?
|
||||||
|
return null
|
||||||
if !project._id?
|
if !project._id?
|
||||||
project = {_id: project}
|
project = {_id: project}
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -92,7 +92,7 @@ module.exports = class Router
|
||||||
webRouter.post '/user/sessions/clear', AuthenticationController.requireLogin(), UserController.clearSessions
|
webRouter.post '/user/sessions/clear', AuthenticationController.requireLogin(), UserController.clearSessions
|
||||||
|
|
||||||
webRouter.delete '/user/newsletter/unsubscribe', AuthenticationController.requireLogin(), UserController.unsubscribe
|
webRouter.delete '/user/newsletter/unsubscribe', AuthenticationController.requireLogin(), UserController.unsubscribe
|
||||||
webRouter.delete '/user', AuthenticationController.requireLogin(), UserController.deleteUser
|
webRouter.post '/user/delete', AuthenticationController.requireLogin(), UserController.tryDeleteUser
|
||||||
|
|
||||||
webRouter.get '/user/personal_info', AuthenticationController.requireLogin(), UserInfoController.getLoggedInUsersPersonalInfo
|
webRouter.get '/user/personal_info', AuthenticationController.requireLogin(), UserInfoController.getLoggedInUsersPersonalInfo
|
||||||
apiRouter.get '/user/:user_id/personal_info', AuthenticationController.httpAuth, UserInfoController.getPersonalInfo
|
apiRouter.get '/user/:user_id/personal_info', AuthenticationController.httpAuth, UserInfoController.getPersonalInfo
|
||||||
|
|
|
@ -51,8 +51,6 @@ html(itemscope, itemtype='http://schema.org/Product')
|
||||||
script(type="text/javascript").
|
script(type="text/javascript").
|
||||||
window.csrfToken = "#{csrfToken}";
|
window.csrfToken = "#{csrfToken}";
|
||||||
|
|
||||||
block scripts
|
|
||||||
|
|
||||||
script(src=buildJsPath("libs/jquery-1.11.1.min.js", {fingerprint:false}))
|
script(src=buildJsPath("libs/jquery-1.11.1.min.js", {fingerprint:false}))
|
||||||
script(type="text/javascript").
|
script(type="text/javascript").
|
||||||
var noCdnKey = "nocdn=true"
|
var noCdnKey = "nocdn=true"
|
||||||
|
@ -61,6 +59,9 @@ html(itemscope, itemtype='http://schema.org/Product')
|
||||||
if (cdnBlocked && !noCdnAlreadyInUrl && navigator.userAgent.indexOf("Googlebot") == -1) {
|
if (cdnBlocked && !noCdnAlreadyInUrl && navigator.userAgent.indexOf("Googlebot") == -1) {
|
||||||
window.location.search += '&'+noCdnKey;
|
window.location.search += '&'+noCdnKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
block scripts
|
||||||
|
|
||||||
script(src=buildJsPath("libs/angular-1.3.15.min.js", {fingerprint:false}))
|
script(src=buildJsPath("libs/angular-1.3.15.min.js", {fingerprint:false}))
|
||||||
|
|
||||||
script.
|
script.
|
||||||
|
|
|
@ -118,6 +118,9 @@ block requirejs
|
||||||
"ace/ext-searchbox": {
|
"ace/ext-searchbox": {
|
||||||
"deps": ["ace/ace"]
|
"deps": ["ace/ace"]
|
||||||
},
|
},
|
||||||
|
"ace/ext-modelist": {
|
||||||
|
"deps": ["ace/ace"]
|
||||||
|
},
|
||||||
"ace/ext-language_tools": {
|
"ace/ext-language_tools": {
|
||||||
"deps": ["ace/ace"]
|
"deps": ["ace/ace"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ div.full-size(
|
||||||
resize-on="layout:main:resize,layout:pdf:resize,layout:review:resize,reviewPanel:toggle",
|
resize-on="layout:main:resize,layout:pdf:resize,layout:review:resize,reviewPanel:toggle",
|
||||||
annotations="pdf.logEntryAnnotations[editor.open_doc_id]",
|
annotations="pdf.logEntryAnnotations[editor.open_doc_id]",
|
||||||
read-only="!permissions.write",
|
read-only="!permissions.write",
|
||||||
|
file-name="editor.open_doc_name",
|
||||||
on-ctrl-enter="recompileViaKey",
|
on-ctrl-enter="recompileViaKey",
|
||||||
syntax-validation="settings.syntaxValidation",
|
syntax-validation="settings.syntaxValidation",
|
||||||
review-panel="reviewPanel",
|
review-panel="reviewPanel",
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
div#history(ng-show="ui.view == 'history'")
|
div#history(ng-show="ui.view == 'history'")
|
||||||
span(ng-controller="HistoryPremiumPopup")
|
span(ng-controller="HistoryPremiumPopup")
|
||||||
.upgrade-prompt(ng-show="!project.features.versioning")
|
.upgrade-prompt(ng-if="project.features.versioning === false && ui.view === 'history'")
|
||||||
.message(ng-show="project.owner._id == user.id")
|
|
||||||
|
div(sixpack-switch="teaser-history")
|
||||||
|
.message(
|
||||||
|
sixpack-default
|
||||||
|
ng-show="project.owner._id == user.id"
|
||||||
|
)
|
||||||
p.text-center: strong #{translate("upgrade_to_get_feature", {feature:"full Project History"})}
|
p.text-center: strong #{translate("upgrade_to_get_feature", {feature:"full Project History"})}
|
||||||
p.text-center.small(ng-show="startedFreeTrial") #{translate("refresh_page_after_starting_free_trial")}
|
p.text-center.small(ng-show="startedFreeTrial") #{translate("refresh_page_after_starting_free_trial")}
|
||||||
ul.list-unstyled
|
ul.list-unstyled
|
||||||
|
@ -28,14 +33,53 @@ div#history(ng-show="ui.view == 'history'")
|
||||||
li
|
li
|
||||||
i.fa.fa-check
|
i.fa.fa-check
|
||||||
|#{translate("compile_larger_projects")}
|
|#{translate("compile_larger_projects")}
|
||||||
|
|
||||||
p.text-center(ng-controller="FreeTrialModalController")
|
p.text-center(ng-controller="FreeTrialModalController")
|
||||||
a.btn.btn-success(
|
a.btn.btn-success(
|
||||||
href
|
href
|
||||||
ng-class="buttonClass"
|
ng-class="buttonClass"
|
||||||
ng-click="startFreeTrial('history')"
|
ng-click="startFreeTrial('history')"
|
||||||
|
sixpack-convert="teaser-history"
|
||||||
) #{translate("start_free_trial")}
|
) #{translate("start_free_trial")}
|
||||||
|
|
||||||
|
.message.message-wider(
|
||||||
|
sixpack-when="focused"
|
||||||
|
ng-show="project.owner._id == user.id"
|
||||||
|
)
|
||||||
|
header.message-header
|
||||||
|
h3 History
|
||||||
|
|
||||||
|
.message-body
|
||||||
|
h4.teaser-title See who changed what. Go back to previous versions.
|
||||||
|
img.teaser-img(
|
||||||
|
src="/img/teasers/history/teaser-history.png"
|
||||||
|
alt="History"
|
||||||
|
)
|
||||||
|
p.text-center.small(ng-show="startedFreeTrial") #{translate("refresh_page_after_starting_free_trial")}
|
||||||
|
.row
|
||||||
|
.col-md-8.col-md-offset-2
|
||||||
|
ul.list-unstyled
|
||||||
|
li
|
||||||
|
i.fa.fa-check
|
||||||
|
| Catch up with your collaborators changes
|
||||||
|
|
||||||
|
li
|
||||||
|
i.fa.fa-check
|
||||||
|
| See changes over any time period
|
||||||
|
|
||||||
|
li
|
||||||
|
i.fa.fa-check
|
||||||
|
| Revert your documents to previous versions
|
||||||
|
|
||||||
|
li
|
||||||
|
i.fa.fa-check
|
||||||
|
| Restore deleted files
|
||||||
|
p.text-center(ng-controller="FreeTrialModalController")
|
||||||
|
a.btn.btn-success(
|
||||||
|
href
|
||||||
|
ng-class="buttonClass"
|
||||||
|
ng-click="startFreeTrial('history')"
|
||||||
|
sixpack-convert="teaser-history"
|
||||||
|
) Try it for free
|
||||||
|
|
||||||
.message(ng-show="project.owner._id != user.id")
|
.message(ng-show="project.owner._id != user.id")
|
||||||
p #{translate("ask_proj_owner_to_upgrade_for_history")}
|
p #{translate("ask_proj_owner_to_upgrade_for_history")}
|
||||||
|
|
|
@ -12,8 +12,8 @@ block scripts
|
||||||
|
|
||||||
mixin printPlan(plan)
|
mixin printPlan(plan)
|
||||||
-if (!plan.hideFromUsers)
|
-if (!plan.hideFromUsers)
|
||||||
tr(ng-controller="ChangePlanFormController")
|
tr(ng-controller="ChangePlanFormController", ng-init="plan=#{JSON.stringify(plan)}", ng-show="shouldShowPlan(plan.planCode)")
|
||||||
td(ng-init="plan=#{JSON.stringify(plan)}")
|
td
|
||||||
strong #{plan.name}
|
strong #{plan.name}
|
||||||
td {{refreshPrice(plan.planCode)}}
|
td {{refreshPrice(plan.planCode)}}
|
||||||
-if (plan.annual)
|
-if (plan.annual)
|
||||||
|
@ -46,7 +46,7 @@ block content
|
||||||
|
|
|
|
||||||
| #{translate("your_billing_details_were_saved")}
|
| #{translate("your_billing_details_were_saved")}
|
||||||
.card(ng-if="view == 'overview'")
|
.card(ng-if="view == 'overview'")
|
||||||
.page-header
|
.page-header(x-current-plan="#{subscription.planCode}")
|
||||||
h1 #{translate("your_subscription")}
|
h1 #{translate("your_subscription")}
|
||||||
|
|
||||||
- if (subscription && user._id+'' == subscription.admin_id+'')
|
- if (subscription && user._id+'' == subscription.admin_id+'')
|
||||||
|
@ -56,6 +56,7 @@ block content
|
||||||
|
|
||||||
when "active"
|
when "active"
|
||||||
p !{translate("currently_subscribed_to_plan", {planName:"<strong>" + subscription.name + "</strong>"})}
|
p !{translate("currently_subscribed_to_plan", {planName:"<strong>" + subscription.name + "</strong>"})}
|
||||||
|
span(ng-show="!isNextGenPlan")
|
||||||
a(href, ng-click="changePlan = true") !{translate("change_plan")}.
|
a(href, ng-click="changePlan = true") !{translate("change_plan")}.
|
||||||
p !{translate("next_payment_of_x_collectected_on_y", {paymentAmmount:"<strong>" + subscription.price + "</strong>", collectionDate:"<strong>" + subscription.nextPaymentDueAt + "</strong>"})}
|
p !{translate("next_payment_of_x_collectected_on_y", {paymentAmmount:"<strong>" + subscription.price + "</strong>", collectionDate:"<strong>" + subscription.nextPaymentDueAt + "</strong>"})}
|
||||||
p.pull-right
|
p.pull-right
|
||||||
|
|
|
@ -3,6 +3,7 @@ block scripts
|
||||||
script(type='text/javascript').
|
script(type='text/javascript').
|
||||||
window.recomendedCurrency = '#{recomendedCurrency}'
|
window.recomendedCurrency = '#{recomendedCurrency}'
|
||||||
window.abCurrencyFlag = '#{abCurrencyFlag}'
|
window.abCurrencyFlag = '#{abCurrencyFlag}'
|
||||||
|
window.shouldABTestPlans = #{shouldABTestPlans || false}
|
||||||
|
|
||||||
script(type='text/javascript').
|
script(type='text/javascript').
|
||||||
(function() {var s=document.createElement('script'); s.type='text/javascript';s.async=true;
|
(function() {var s=document.createElement('script'); s.type='text/javascript';s.async=true;
|
||||||
|
@ -56,6 +57,7 @@ block content
|
||||||
ng-click="changeCurreny(currency)"
|
ng-click="changeCurreny(currency)"
|
||||||
) {{currency}} ({{value['symbol']}})
|
) {{currency}} ({{value['symbol']}})
|
||||||
|
|
||||||
|
div(ng-show="showPlans")
|
||||||
.row(ng-cloak)
|
.row(ng-cloak)
|
||||||
.col-md-10.col-md-offset-1
|
.col-md-10.col-md-offset-1
|
||||||
.row
|
.row
|
||||||
|
@ -89,7 +91,9 @@ block content
|
||||||
span.small /yr
|
span.small /yr
|
||||||
ul.list-unstyled
|
ul.list-unstyled
|
||||||
li
|
li
|
||||||
strong #{translate("collabs_per_proj", {collabcount:10})}
|
strong(ng-show="plansVariant == 'default'") #{translate("collabs_per_proj", {collabcount:10})}
|
||||||
|
strong(ng-show="plansVariant == 'heron'") #{translate("collabs_per_proj", {collabcount:8})}
|
||||||
|
strong(ng-show="plansVariant == 'ibis'") #{translate("collabs_per_proj", {collabcount:12})}
|
||||||
li #{translate("full_doc_history")}
|
li #{translate("full_doc_history")}
|
||||||
li #{translate("sync_to_dropbox")}
|
li #{translate("sync_to_dropbox")}
|
||||||
li #{translate("sync_to_github")}
|
li #{translate("sync_to_github")}
|
||||||
|
@ -97,7 +101,7 @@ block content
|
||||||
br
|
br
|
||||||
a.btn.btn-info(
|
a.btn.btn-info(
|
||||||
|
|
||||||
ng-href="#{baseUrl}/user/subscription/new?planCode=collaborator{{ ui.view == 'annual' && '-annual' || planQueryString}}¤cy={{currencyCode}}", ng-click="signUpNowClicked('collaborator')"
|
ng-href="#{baseUrl}/user/subscription/new?planCode=collaborator{{ (ui.view == 'annual' ? '-annual' : '') + (plansVariant == 'default' ? planQueryString : '_'+plansVariant)}}¤cy={{currencyCode}}", ng-click="signUpNowClicked('collaborator')"
|
||||||
)
|
)
|
||||||
span(ng-show="ui.view != 'annual'") #{translate("start_free_trial")}
|
span(ng-show="ui.view != 'annual'") #{translate("start_free_trial")}
|
||||||
span(ng-show="ui.view == 'annual'") #{translate("buy_now")}
|
span(ng-show="ui.view == 'annual'") #{translate("buy_now")}
|
||||||
|
@ -154,14 +158,17 @@ block content
|
||||||
span.small /mo
|
span.small /mo
|
||||||
ul.list-unstyled
|
ul.list-unstyled
|
||||||
li
|
li
|
||||||
strong #{translate("collabs_per_proj", {collabcount:6})}
|
strong(ng-show="plansVariant == 'default'") #{translate("collabs_per_proj", {collabcount:6})}
|
||||||
|
strong(ng-show="plansVariant == 'heron'") #{translate("collabs_per_proj", {collabcount:4})}
|
||||||
|
strong(ng-show="plansVariant == 'ibis'") #{translate("collabs_per_proj", {collabcount:8})}
|
||||||
li #{translate("full_doc_history")}
|
li #{translate("full_doc_history")}
|
||||||
li #{translate("sync_to_dropbox")}
|
li #{translate("sync_to_dropbox")}
|
||||||
li #{translate("sync_to_github")}
|
li #{translate("sync_to_github")}
|
||||||
li
|
li
|
||||||
br
|
br
|
||||||
a.btn.btn-info(
|
a.btn.btn-info(
|
||||||
ng-href="#{baseUrl}/user/subscription/new?planCode=student{{planQueryString}}¤cy={{currencyCode}}", ng-click="signUpNowClicked('student')"
|
ng-href="#{baseUrl}/user/subscription/new?planCode=student{{ plansVariant == 'default' ? planQueryString : '_'+plansVariant }}¤cy={{currencyCode}}",
|
||||||
|
ng-click="signUpNowClicked('student')"
|
||||||
) #{translate("start_free_trial")}
|
) #{translate("start_free_trial")}
|
||||||
|
|
||||||
.col-md-4
|
.col-md-4
|
||||||
|
@ -174,16 +181,21 @@ block content
|
||||||
span.small /yr
|
span.small /yr
|
||||||
ul.list-unstyled
|
ul.list-unstyled
|
||||||
li
|
li
|
||||||
strong #{translate("collabs_per_proj", {collabcount:6})}
|
strong(ng-show="plansVariant == 'default'") #{translate("collabs_per_proj", {collabcount:6})}
|
||||||
|
strong(ng-show="plansVariant == 'heron'") #{translate("collabs_per_proj", {collabcount:4})}
|
||||||
|
strong(ng-show="plansVariant == 'ibis'") #{translate("collabs_per_proj", {collabcount:8})}
|
||||||
li #{translate("full_doc_history")}
|
li #{translate("full_doc_history")}
|
||||||
li #{translate("sync_to_dropbox")}
|
li #{translate("sync_to_dropbox")}
|
||||||
li #{translate("sync_to_github")}
|
li #{translate("sync_to_github")}
|
||||||
li
|
li
|
||||||
br
|
br
|
||||||
a.btn.btn-info(
|
a.btn.btn-info(
|
||||||
ng-href="#{baseUrl}/user/subscription/new?planCode=student-annual¤cy={{currencyCode}}", ng-click="signUpNowClicked('student')"
|
ng-href="#{baseUrl}/user/subscription/new?planCode=student-annual{{ plansVariant == 'default' ? '' : '_'+plansVariant }}¤cy={{currencyCode}}",
|
||||||
|
ng-click="signUpNowClicked('student')"
|
||||||
) #{translate("buy_now")}
|
) #{translate("buy_now")}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.row.row-spaced(ng-cloak)
|
.row.row-spaced(ng-cloak)
|
||||||
p.text-centered #{translate("choose_plan_works_for_you", {len:'{{trial_len}}'})}
|
p.text-centered #{translate("choose_plan_works_for_you", {len:'{{trial_len}}'})}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ extends ../layout
|
||||||
|
|
||||||
block content
|
block content
|
||||||
.content.content-alt
|
.content.content-alt
|
||||||
.container
|
.container(ng-controller="SuccessfulSubscriptionController")
|
||||||
.row
|
.row
|
||||||
.col-md-8.col-md-offset-2
|
.col-md-8.col-md-offset-2
|
||||||
.card(ng-cloak)
|
.card(ng-cloak)
|
||||||
|
|
|
@ -150,16 +150,32 @@ block content
|
||||||
script(type='text/ng-template', id='deleteAccountModalTemplate')
|
script(type='text/ng-template', id='deleteAccountModalTemplate')
|
||||||
.modal-header
|
.modal-header
|
||||||
h3 #{translate("delete_account")}
|
h3 #{translate("delete_account")}
|
||||||
.modal-body
|
div.modal-body#delete-account-modal
|
||||||
p !{translate("delete_account_warning_message_2")}
|
p !{translate("delete_account_warning_message_3")}
|
||||||
form(novalidate, name="deleteAccountForm")
|
form(novalidate, name="deleteAccountForm")
|
||||||
|
label #{translate('email')}
|
||||||
input.form-control(
|
input.form-control(
|
||||||
type="text",
|
type="text",
|
||||||
|
autocomplete="off",
|
||||||
placeholder="",
|
placeholder="",
|
||||||
ng-model="state.deleteText",
|
ng-model="state.deleteText",
|
||||||
focus-on="open",
|
focus-on="open",
|
||||||
ng-keyup="checkValidation()"
|
ng-keyup="checkValidation()"
|
||||||
)
|
)
|
||||||
|
label #{translate('password')}
|
||||||
|
input.form-control(
|
||||||
|
type="password",
|
||||||
|
autocomplete="off",
|
||||||
|
placeholder="",
|
||||||
|
ng-model="state.password",
|
||||||
|
ng-keyup="checkValidation()"
|
||||||
|
)
|
||||||
|
div(ng-if="state.error")
|
||||||
|
div.alert.alert-danger
|
||||||
|
| #{translate('generic_something_went_wrong')}
|
||||||
|
div(ng-if="state.invalidCredentials")
|
||||||
|
div.alert.alert-danger
|
||||||
|
| #{translate('email_or_password_wrong_try_again')}
|
||||||
.modal-footer
|
.modal-footer
|
||||||
button.btn.btn-default(
|
button.btn.btn-default(
|
||||||
ng-click="cancel()"
|
ng-click="cancel()"
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
"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",
|
||||||
"metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.3.0",
|
"metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.6.0",
|
||||||
"mimelib": "0.2.14",
|
"mimelib": "0.2.14",
|
||||||
"mocha": "1.17.1",
|
"mocha": "1.17.1",
|
||||||
"mongojs": "0.18.2",
|
"mongojs": "0.18.2",
|
||||||
|
|
|
@ -69,6 +69,12 @@ define [
|
||||||
reviewPanelOpen: false
|
reviewPanelOpen: false
|
||||||
}
|
}
|
||||||
$scope.user = window.user
|
$scope.user = window.user
|
||||||
|
|
||||||
|
|
||||||
|
$scope.shouldABTestPlans = false
|
||||||
|
if $scope.user.signUpDate >= '2016-10-27'
|
||||||
|
$scope.shouldABTestPlans = true
|
||||||
|
|
||||||
$scope.settings = window.userSettings
|
$scope.settings = window.userSettings
|
||||||
$scope.anonymous = window.anonymous
|
$scope.anonymous = window.anonymous
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ define [], () ->
|
||||||
|
|
||||||
@connected = false
|
@connected = false
|
||||||
@userIsInactive = false
|
@userIsInactive = false
|
||||||
|
@gracefullyReconnecting = false
|
||||||
|
|
||||||
@$scope.connection =
|
@$scope.connection =
|
||||||
reconnecting: false
|
reconnecting: false
|
||||||
|
@ -54,6 +55,7 @@ define [], () ->
|
||||||
@ide.socket.on "connect", () =>
|
@ide.socket.on "connect", () =>
|
||||||
sl_console.log "[socket.io connect] Connected"
|
sl_console.log "[socket.io connect] Connected"
|
||||||
@connected = true
|
@connected = true
|
||||||
|
@gracefullyReconnecting = false
|
||||||
@ide.pushEvent("connected")
|
@ide.pushEvent("connected")
|
||||||
|
|
||||||
@$scope.$apply () =>
|
@$scope.$apply () =>
|
||||||
|
@ -81,7 +83,7 @@ define [], () ->
|
||||||
@$scope.$apply () =>
|
@$scope.$apply () =>
|
||||||
@$scope.connection.reconnecting = false
|
@$scope.connection.reconnecting = false
|
||||||
|
|
||||||
if !$scope.connection.forced_disconnect and !@userIsInactive
|
if !$scope.connection.forced_disconnect and !@userIsInactive and !@gracefullyReconnecting
|
||||||
@startAutoReconnectCountdown()
|
@startAutoReconnectCountdown()
|
||||||
|
|
||||||
@ide.socket.on 'forceDisconnect', (message) =>
|
@ide.socket.on 'forceDisconnect', (message) =>
|
||||||
|
@ -98,6 +100,10 @@ define [], () ->
|
||||||
location.reload()
|
location.reload()
|
||||||
, 10 * 1000
|
, 10 * 1000
|
||||||
|
|
||||||
|
@ide.socket.on "reconnectGracefully", () =>
|
||||||
|
sl_console.log "Reconnect gracefully"
|
||||||
|
@reconnectGracefully()
|
||||||
|
|
||||||
joinProject: () ->
|
joinProject: () ->
|
||||||
sl_console.log "[joinProject] joining..."
|
sl_console.log "[joinProject] joining..."
|
||||||
@ide.socket.emit 'joinProject', {
|
@ide.socket.emit 'joinProject', {
|
||||||
|
@ -180,3 +186,24 @@ define [], () ->
|
||||||
@$scope.$apply () =>
|
@$scope.$apply () =>
|
||||||
@$scope.connection.inactive_disconnect = true
|
@$scope.connection.inactive_disconnect = true
|
||||||
|
|
||||||
|
RECONNECT_GRACEFULLY_RETRY_INTERVAL: 5000 # ms
|
||||||
|
MAX_RECONNECT_GRACEFULLY_INTERVAL: 60 * 5 * 1000 # 5 minutes
|
||||||
|
reconnectGracefully: () ->
|
||||||
|
@reconnectGracefullyStarted ?= new Date()
|
||||||
|
userIsInactive = (new Date() - @lastUserAction) > @RECONNECT_GRACEFULLY_RETRY_INTERVAL
|
||||||
|
maxIntervalReached = (new Date() - @reconnectGracefullyStarted) > @MAX_RECONNECT_GRACEFULLY_INTERVAL
|
||||||
|
if userIsInactive or maxIntervalReached
|
||||||
|
sl_console.log "[reconnectGracefully] User didn't do anything for last 5 seconds, reconnecting"
|
||||||
|
@_reconnectGracefullyNow()
|
||||||
|
else
|
||||||
|
sl_console.log "[reconnectGracefully] User is working, will try again in 5 seconds"
|
||||||
|
setTimeout () =>
|
||||||
|
@reconnectGracefully()
|
||||||
|
, @RECONNECT_GRACEFULLY_RETRY_INTERVAL
|
||||||
|
|
||||||
|
_reconnectGracefullyNow: () ->
|
||||||
|
@gracefullyReconnecting = true
|
||||||
|
@reconnectGracefullyStarted = null
|
||||||
|
# Clear cookie so we don't go to the same backend server
|
||||||
|
$.cookie("SERVERID", "", { expires: -1, path: "/" })
|
||||||
|
@reconnectImmediately()
|
|
@ -8,11 +8,12 @@ define [
|
||||||
@$scope.editor = {
|
@$scope.editor = {
|
||||||
sharejs_doc: null
|
sharejs_doc: null
|
||||||
open_doc_id: null
|
open_doc_id: null
|
||||||
|
open_doc_name: null
|
||||||
opening: true
|
opening: true
|
||||||
}
|
}
|
||||||
|
|
||||||
@$scope.$on "entity:selected", (event, entity) =>
|
@$scope.$on "entity:selected", (event, entity) =>
|
||||||
if (@$scope.ui.view != "track-changes" and entity.type == "doc")
|
if (@$scope.ui.view != "history" and entity.type == "doc")
|
||||||
@openDoc(entity)
|
@openDoc(entity)
|
||||||
|
|
||||||
@$scope.$on "entity:deleted", (event, entity) =>
|
@$scope.$on "entity:deleted", (event, entity) =>
|
||||||
|
@ -59,6 +60,7 @@ define [
|
||||||
return
|
return
|
||||||
|
|
||||||
@$scope.editor.open_doc_id = doc.id
|
@$scope.editor.open_doc_id = doc.id
|
||||||
|
@$scope.editor.open_doc_name = doc.name
|
||||||
|
|
||||||
@ide.localStorage "doc.open_id.#{@$scope.project_id}", doc.id
|
@ide.localStorage "doc.open_id.#{@$scope.project_id}", doc.id
|
||||||
@ide.fileTreeManager.selectEntity(doc)
|
@ide.fileTreeManager.selectEntity(doc)
|
||||||
|
|
|
@ -2,19 +2,25 @@ define [
|
||||||
"base"
|
"base"
|
||||||
"ace/ace"
|
"ace/ace"
|
||||||
"ace/ext-searchbox"
|
"ace/ext-searchbox"
|
||||||
|
"ace/ext-modelist"
|
||||||
"ide/editor/directives/aceEditor/undo/UndoManager"
|
"ide/editor/directives/aceEditor/undo/UndoManager"
|
||||||
"ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager"
|
"ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager"
|
||||||
"ide/editor/directives/aceEditor/spell-check/SpellCheckManager"
|
"ide/editor/directives/aceEditor/spell-check/SpellCheckManager"
|
||||||
"ide/editor/directives/aceEditor/highlights/HighlightsManager"
|
"ide/editor/directives/aceEditor/highlights/HighlightsManager"
|
||||||
"ide/editor/directives/aceEditor/cursor-position/CursorPositionManager"
|
"ide/editor/directives/aceEditor/cursor-position/CursorPositionManager"
|
||||||
"ide/editor/directives/aceEditor/track-changes/TrackChangesManager"
|
"ide/editor/directives/aceEditor/track-changes/TrackChangesManager"
|
||||||
], (App, Ace, SearchBox, UndoManager, AutoCompleteManager, SpellCheckManager, HighlightsManager, CursorPositionManager, TrackChangesManager) ->
|
], (App, Ace, SearchBox, ModeList, UndoManager, AutoCompleteManager, SpellCheckManager, HighlightsManager, CursorPositionManager, TrackChangesManager) ->
|
||||||
EditSession = ace.require('ace/edit_session').EditSession
|
EditSession = ace.require('ace/edit_session').EditSession
|
||||||
|
ModeList = ace.require('ace/ext/modelist')
|
||||||
|
|
||||||
# set the path for ace workers if using a CDN (from editor.jade)
|
# set the path for ace workers if using a CDN (from editor.jade)
|
||||||
if window.aceWorkerPath != ""
|
if window.aceWorkerPath != ""
|
||||||
|
syntaxValidationEnabled = true
|
||||||
ace.config.set('workerPath', "#{window.aceWorkerPath}")
|
ace.config.set('workerPath', "#{window.aceWorkerPath}")
|
||||||
else
|
else
|
||||||
|
syntaxValidationEnabled = false
|
||||||
|
|
||||||
|
# By default, don't use workers - enable them per-session as required
|
||||||
ace.config.setDefaultValue("session", "useWorker", false)
|
ace.config.setDefaultValue("session", "useWorker", false)
|
||||||
|
|
||||||
# Ace loads its script itself, so we need to hook in to be able to clear
|
# Ace loads its script itself, so we need to hook in to be able to clear
|
||||||
|
@ -43,6 +49,7 @@ define [
|
||||||
readOnly: "="
|
readOnly: "="
|
||||||
annotations: "="
|
annotations: "="
|
||||||
navigateHighlights: "="
|
navigateHighlights: "="
|
||||||
|
fileName: "="
|
||||||
onCtrlEnter: "="
|
onCtrlEnter: "="
|
||||||
syntaxValidation: "="
|
syntaxValidation: "="
|
||||||
reviewPanel: "="
|
reviewPanel: "="
|
||||||
|
@ -62,6 +69,11 @@ define [
|
||||||
|
|
||||||
editor = ace.edit(element.find(".ace-editor-body")[0])
|
editor = ace.edit(element.find(".ace-editor-body")[0])
|
||||||
editor.$blockScrolling = Infinity
|
editor.$blockScrolling = Infinity
|
||||||
|
|
||||||
|
# disable auto insertion of brackets and quotes
|
||||||
|
editor.setOption('behavioursEnabled', false)
|
||||||
|
editor.setOption('wrapBehavioursEnabled', false)
|
||||||
|
|
||||||
window.editors ||= []
|
window.editors ||= []
|
||||||
window.editors.push editor
|
window.editors.push editor
|
||||||
|
|
||||||
|
@ -198,16 +210,15 @@ define [
|
||||||
editor.setReadOnly !!value
|
editor.setReadOnly !!value
|
||||||
|
|
||||||
scope.$watch "syntaxValidation", (value) ->
|
scope.$watch "syntaxValidation", (value) ->
|
||||||
|
# ignore undefined settings here
|
||||||
|
# only instances of ace with an explicit value should set useWorker
|
||||||
|
# the history instance will have syntaxValidation undefined
|
||||||
|
if value? and syntaxValidationEnabled
|
||||||
session = editor.getSession()
|
session = editor.getSession()
|
||||||
session.setOption("useWorker", value);
|
session.setOption("useWorker", value);
|
||||||
|
|
||||||
editor.setOption("scrollPastEnd", true)
|
editor.setOption("scrollPastEnd", true)
|
||||||
|
|
||||||
resetSession = () ->
|
|
||||||
session = editor.getSession()
|
|
||||||
session.setUseWrapMode(true)
|
|
||||||
session.setMode("ace/mode/latex")
|
|
||||||
|
|
||||||
updateCount = 0
|
updateCount = 0
|
||||||
onChange = () ->
|
onChange = () ->
|
||||||
updateCount++
|
updateCount++
|
||||||
|
@ -229,9 +240,36 @@ define [
|
||||||
session = editor.getSession()
|
session = editor.getSession()
|
||||||
if session?
|
if session?
|
||||||
session.destroy()
|
session.destroy()
|
||||||
editor.setSession(new EditSession(lines, "ace/mode/latex"))
|
|
||||||
resetSession()
|
# see if we can lookup a suitable mode from ace
|
||||||
session = editor.getSession()
|
# but fall back to text by default
|
||||||
|
try
|
||||||
|
if scope.fileName.match(/\.(Rtex|bbl)$/i)
|
||||||
|
# recognise Rtex and bbl as latex
|
||||||
|
mode = "ace/mode/latex"
|
||||||
|
else if scope.fileName.match(/\.(sty|cls|clo)$/)
|
||||||
|
# recognise some common files as tex
|
||||||
|
mode = "ace/mode/tex"
|
||||||
|
else
|
||||||
|
mode = ModeList.getModeForPath(scope.fileName).mode
|
||||||
|
# we prefer plain_text mode over text mode because ace's
|
||||||
|
# text mode is actually for code and has unwanted
|
||||||
|
# indenting (see wrapMethod in ace edit_session.js)
|
||||||
|
if mode is "ace/mode/text"
|
||||||
|
mode = "ace/mode/plain_text"
|
||||||
|
catch
|
||||||
|
mode = "ace/mode/plain_text"
|
||||||
|
|
||||||
|
# create our new session
|
||||||
|
session = new EditSession(lines, mode)
|
||||||
|
|
||||||
|
session.setUseWrapMode(true)
|
||||||
|
# use syntax validation only when explicitly set
|
||||||
|
if scope.syntaxValidation? and syntaxValidationEnabled
|
||||||
|
session.setOption("useWorker", scope.syntaxValidation);
|
||||||
|
|
||||||
|
# now attach session to editor
|
||||||
|
editor.setSession(session)
|
||||||
|
|
||||||
doc = session.getDocument()
|
doc = session.getDocument()
|
||||||
doc.on "change", onChange
|
doc.on "change", onChange
|
||||||
|
|
|
@ -233,8 +233,16 @@ define [
|
||||||
start = aceDelta.start
|
start = aceDelta.start
|
||||||
if !start?
|
if !start?
|
||||||
error = new Error("aceDelta had no start event.")
|
error = new Error("aceDelta had no start event.")
|
||||||
|
JSONstringifyWithCycles = (o) ->
|
||||||
|
seen = []
|
||||||
|
return JSON.stringify o, (k,v) ->
|
||||||
|
if (typeof v == 'object')
|
||||||
|
if ( !seen.indexOf(v) )
|
||||||
|
return '__cycle__'
|
||||||
|
seen.push(v);
|
||||||
|
return v
|
||||||
Raven?.captureException(error, {
|
Raven?.captureException(error, {
|
||||||
aceDelta: JSON.stringify(aceDelta)
|
aceDelta: JSONstringifyWithCycles(aceDelta)
|
||||||
})
|
})
|
||||||
throw error
|
throw error
|
||||||
linesBefore = docLines.slice(0, start.row)
|
linesBefore = docLines.slice(0, start.row)
|
||||||
|
|
|
@ -469,7 +469,7 @@ define [
|
||||||
|
|
||||||
event_tracking.sendMB "subscription-start-trial", { source }
|
event_tracking.sendMB "subscription-start-trial", { source }
|
||||||
|
|
||||||
window.open("/user/subscription/new?planCode=student_free_trial_7_days")
|
window.open("/user/subscription/new?planCode=#{$scope.startTrialPlanCode}")
|
||||||
$scope.startedFreeTrial = true
|
$scope.startedFreeTrial = true
|
||||||
|
|
||||||
App.factory "synctex", ["ide", "$http", "$q", (ide, $http, $q) ->
|
App.factory "synctex", ["ide", "$http", "$q", (ide, $http, $q) ->
|
||||||
|
|
|
@ -30,6 +30,10 @@ define [
|
||||||
TEXTLAYER_TIMEOUT: 100
|
TEXTLAYER_TIMEOUT: 100
|
||||||
|
|
||||||
constructor: (@url, @options) ->
|
constructor: (@url, @options) ->
|
||||||
|
# set up external character mappings - needed for Japanese etc
|
||||||
|
window.PDFJS.cMapUrl = './bcmaps/'
|
||||||
|
window.PDFJS.cMapPacked = true
|
||||||
|
|
||||||
if window.location?.search?.indexOf("disable-font-face=true") >= 0
|
if window.location?.search?.indexOf("disable-font-face=true") >= 0
|
||||||
window.PDFJS.disableFontFace = true
|
window.PDFJS.disableFontFace = true
|
||||||
else
|
else
|
||||||
|
|
|
@ -17,7 +17,12 @@ define [
|
||||||
# When we join the project:
|
# When we join the project:
|
||||||
# index all references files
|
# index all references files
|
||||||
# and don't broadcast to all clients
|
# and don't broadcast to all clients
|
||||||
|
@inited = false
|
||||||
@$scope.$on 'project:joined', (e) =>
|
@$scope.$on 'project:joined', (e) =>
|
||||||
|
# We only need to grab the references when the editor first loads,
|
||||||
|
# not on every reconnect
|
||||||
|
if !@inited
|
||||||
|
@inited = true
|
||||||
@indexAllReferences(false)
|
@indexAllReferences(false)
|
||||||
|
|
||||||
setTimeout(
|
setTimeout(
|
||||||
|
|
|
@ -30,7 +30,7 @@ define [
|
||||||
|
|
||||||
$scope.$watch "settings.syntaxValidation", (syntaxValidation, oldSyntaxValidation) =>
|
$scope.$watch "settings.syntaxValidation", (syntaxValidation, oldSyntaxValidation) =>
|
||||||
if syntaxValidation != oldSyntaxValidation
|
if syntaxValidation != oldSyntaxValidation
|
||||||
settings.saveProjectSettings({syntaxValidation: syntaxValidation})
|
settings.saveSettings({syntaxValidation: syntaxValidation})
|
||||||
|
|
||||||
$scope.$watch "project.spellCheckLanguage", (language, oldLanguage) =>
|
$scope.$watch "project.spellCheckLanguage", (language, oldLanguage) =>
|
||||||
return if @ignoreUpdates
|
return if @ignoreUpdates
|
||||||
|
|
|
@ -32,7 +32,10 @@ define [
|
||||||
$scope.state =
|
$scope.state =
|
||||||
isValid : false
|
isValid : false
|
||||||
deleteText: ""
|
deleteText: ""
|
||||||
|
password: ""
|
||||||
inflight: false
|
inflight: false
|
||||||
|
error: false
|
||||||
|
invalidCredentials: false
|
||||||
|
|
||||||
$modalInstance.opened.then () ->
|
$modalInstance.opened.then () ->
|
||||||
$timeout () ->
|
$timeout () ->
|
||||||
|
@ -40,20 +43,33 @@ define [
|
||||||
, 700
|
, 700
|
||||||
|
|
||||||
$scope.checkValidation = ->
|
$scope.checkValidation = ->
|
||||||
$scope.state.isValid = $scope.state.deleteText == $scope.email
|
$scope.state.isValid = $scope.state.deleteText == $scope.email and $scope.state.password.length > 0
|
||||||
|
|
||||||
$scope.delete = () ->
|
$scope.delete = () ->
|
||||||
$scope.state.inflight = true
|
$scope.state.inflight = true
|
||||||
|
$scope.state.error = false
|
||||||
|
$scope.state.invalidCredentials = false
|
||||||
$http({
|
$http({
|
||||||
method: "DELETE"
|
method: "POST"
|
||||||
url: "/user"
|
url: "/user/delete"
|
||||||
headers:
|
headers:
|
||||||
"X-CSRF-Token": window.csrfToken
|
"X-CSRF-Token": window.csrfToken
|
||||||
|
"Content-Type": 'application/json'
|
||||||
|
data:
|
||||||
|
password: $scope.state.password
|
||||||
})
|
})
|
||||||
.success () ->
|
.success () ->
|
||||||
$modalInstance.close()
|
$modalInstance.close()
|
||||||
|
$scope.state.inflight = false
|
||||||
|
$scope.state.error = false
|
||||||
|
$scope.state.invalidCredentials = false
|
||||||
window.location = "/"
|
window.location = "/"
|
||||||
|
.error (data, status) ->
|
||||||
|
$scope.state.inflight = false
|
||||||
|
if status == 403
|
||||||
|
$scope.state.invalidCredentials = true
|
||||||
|
else
|
||||||
|
$scope.state.error = true
|
||||||
|
|
||||||
$scope.cancel = () ->
|
$scope.cancel = () ->
|
||||||
$modalInstance.dismiss('cancel')
|
$modalInstance.dismiss('cancel')
|
||||||
|
|
|
@ -6,14 +6,34 @@ define [
|
||||||
$scope.buttonClass = "btn-primary"
|
$scope.buttonClass = "btn-primary"
|
||||||
|
|
||||||
$scope.startFreeTrial = (source, couponCode) ->
|
$scope.startFreeTrial = (source, couponCode) ->
|
||||||
event_tracking.sendMB "subscription-start-trial", { source }
|
plan = 'collaborator_free_trial_7_days'
|
||||||
|
|
||||||
w = window.open()
|
w = window.open()
|
||||||
sixpack.convert "track-changes-discount", ->
|
go = () ->
|
||||||
sixpack.participate 'in-editor-free-trial-plan', ['student', 'collaborator'], (planName, rawResponse)->
|
|
||||||
ga?('send', 'event', 'subscription-funnel', 'upgraded-free-trial', source)
|
ga?('send', 'event', 'subscription-funnel', 'upgraded-free-trial', source)
|
||||||
url = "/user/subscription/new?planCode=#{planName}_free_trial_7_days&ssp=#{planName == 'collaborator'}"
|
url = "/user/subscription/new?planCode=#{plan}&ssp=true"
|
||||||
if couponCode?
|
if couponCode?
|
||||||
url = "#{url}&cc=#{couponCode}"
|
url = "#{url}&cc=#{couponCode}"
|
||||||
$scope.startedFreeTrial = true
|
$scope.startedFreeTrial = true
|
||||||
|
|
||||||
|
switch source
|
||||||
|
when "dropbox"
|
||||||
|
sixpack.participate 'teaser-dropbox-text', ['default', 'dropbox-focused'], (variant) ->
|
||||||
|
event_tracking.sendMB "subscription-start-trial", { source, plan, variant }
|
||||||
|
|
||||||
|
when "history"
|
||||||
|
sixpack.participate 'teaser-history', ['default', 'focused'], (variant) ->
|
||||||
|
event_tracking.sendMB "subscription-start-trial", { source, plan, variant }
|
||||||
|
|
||||||
|
else
|
||||||
|
event_tracking.sendMB "subscription-start-trial", { source, plan }
|
||||||
|
|
||||||
w.location = url
|
w.location = url
|
||||||
|
|
||||||
|
if $scope.shouldABTestPlans
|
||||||
|
sixpack.participate 'plans-1610', ['default', 'heron', 'ibis'], (chosenVariation, rawResponse)->
|
||||||
|
if chosenVariation in ['heron', 'ibis']
|
||||||
|
plan = "collaborator_#{chosenVariation}"
|
||||||
|
go()
|
||||||
|
else
|
||||||
|
go()
|
||||||
|
|
|
@ -10,7 +10,10 @@ define [
|
||||||
$scope.plans = MultiCurrencyPricing.plans
|
$scope.plans = MultiCurrencyPricing.plans
|
||||||
|
|
||||||
$scope.switchToStudent = ()->
|
$scope.switchToStudent = ()->
|
||||||
window.location = "/user/subscription/new?planCode=student_free_trial_7_days¤cy=#{$scope.currencyCode}&cc=#{$scope.data.coupon}"
|
currentPlanCode = window.plan_code
|
||||||
|
planCode = currentPlanCode.replace('collaborator', 'student')
|
||||||
|
event_tracking.sendMB 'subscription-form-switch-to-student', { plan: window.plan_code }
|
||||||
|
window.location = "/user/subscription/new?planCode=#{planCode}¤cy=#{$scope.currencyCode}&cc=#{$scope.data.coupon}"
|
||||||
|
|
||||||
event_tracking.sendMB "subscription-form", { plan : window.plan_code }
|
event_tracking.sendMB "subscription-form", { plan : window.plan_code }
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,165 @@ define [
|
||||||
|
|
||||||
return {
|
return {
|
||||||
currencyCode:currencyCode
|
currencyCode:currencyCode
|
||||||
|
|
||||||
|
heron:
|
||||||
|
USD:
|
||||||
|
student:
|
||||||
|
monthly: "$6"
|
||||||
|
annual: "$60"
|
||||||
|
collaborator:
|
||||||
|
monthly: "$12"
|
||||||
|
annual: "$144"
|
||||||
|
EUR:
|
||||||
|
student:
|
||||||
|
monthly: "€5"
|
||||||
|
annual: "€50"
|
||||||
|
collaborator:
|
||||||
|
monthly: "€11"
|
||||||
|
annual: "€132"
|
||||||
|
GBP:
|
||||||
|
student:
|
||||||
|
monthly: "£5"
|
||||||
|
annual: "£50"
|
||||||
|
collaborator:
|
||||||
|
monthly: "£10"
|
||||||
|
annual: "£120"
|
||||||
|
SEK:
|
||||||
|
student:
|
||||||
|
monthly: "45 kr"
|
||||||
|
annual: "450 kr"
|
||||||
|
collaborator:
|
||||||
|
monthly: "90 kr"
|
||||||
|
annual: "1080 kr"
|
||||||
|
CAD:
|
||||||
|
student:
|
||||||
|
monthly: "$7"
|
||||||
|
annual: "$70"
|
||||||
|
collaborator:
|
||||||
|
monthly: "$14"
|
||||||
|
annual: "$168"
|
||||||
|
NOK:
|
||||||
|
student:
|
||||||
|
monthly: "45 kr"
|
||||||
|
annual: "450 kr"
|
||||||
|
collaborator:
|
||||||
|
monthly: "90 kr"
|
||||||
|
annual: "1080 kr"
|
||||||
|
DKK:
|
||||||
|
student:
|
||||||
|
monthly: "40 kr"
|
||||||
|
annual: "400 kr"
|
||||||
|
collaborator:
|
||||||
|
monthly: "70 kr"
|
||||||
|
annual: "840 kr"
|
||||||
|
AUD:
|
||||||
|
student:
|
||||||
|
monthly: "$8"
|
||||||
|
annual: "$80"
|
||||||
|
collaborator:
|
||||||
|
monthly: "$15"
|
||||||
|
annual: "$180"
|
||||||
|
NZD:
|
||||||
|
student:
|
||||||
|
monthly: "$8"
|
||||||
|
annual: "$80"
|
||||||
|
collaborator:
|
||||||
|
monthly: "$15"
|
||||||
|
annual: "$180"
|
||||||
|
CHF:
|
||||||
|
student:
|
||||||
|
monthly: "Fr 6"
|
||||||
|
annual: "Fr 60"
|
||||||
|
collaborator:
|
||||||
|
monthly: "Fr 12"
|
||||||
|
annual: "Fr 144"
|
||||||
|
SGD:
|
||||||
|
student:
|
||||||
|
monthly: "$8"
|
||||||
|
annual: "$80"
|
||||||
|
collaborator:
|
||||||
|
monthly: "$16"
|
||||||
|
annual: "$192"
|
||||||
|
|
||||||
|
ibis:
|
||||||
|
USD:
|
||||||
|
student:
|
||||||
|
monthly: "$10"
|
||||||
|
annual: "$100"
|
||||||
|
collaborator:
|
||||||
|
monthly: "$18"
|
||||||
|
annual: "$216"
|
||||||
|
EUR:
|
||||||
|
student:
|
||||||
|
monthly: "€9"
|
||||||
|
annual: "€90"
|
||||||
|
collaborator:
|
||||||
|
monthly: "€17"
|
||||||
|
annual: "€204"
|
||||||
|
GBP:
|
||||||
|
student:
|
||||||
|
monthly: "£7"
|
||||||
|
annual: "£70"
|
||||||
|
collaborator:
|
||||||
|
monthly: "£14"
|
||||||
|
annual: "£168"
|
||||||
|
SEK:
|
||||||
|
student:
|
||||||
|
monthly: "75 kr"
|
||||||
|
annual: "750 kr"
|
||||||
|
collaborator:
|
||||||
|
monthly: "140 kr"
|
||||||
|
annual: "1680 kr"
|
||||||
|
CAD:
|
||||||
|
student:
|
||||||
|
monthly: "$12"
|
||||||
|
annual: "$120"
|
||||||
|
collaborator:
|
||||||
|
monthly: "$22"
|
||||||
|
annual: "$264"
|
||||||
|
NOK:
|
||||||
|
student:
|
||||||
|
monthly: "75 kr"
|
||||||
|
annual: "750 kr"
|
||||||
|
collaborator:
|
||||||
|
monthly: "140 kr"
|
||||||
|
annual: "1680 kr"
|
||||||
|
DKK:
|
||||||
|
student:
|
||||||
|
monthly: "68 kr"
|
||||||
|
annual: "680 kr"
|
||||||
|
collaborator:
|
||||||
|
monthly: "110 kr"
|
||||||
|
annual: "1320 kr"
|
||||||
|
AUD:
|
||||||
|
student:
|
||||||
|
monthly: "$13"
|
||||||
|
annual: "$130"
|
||||||
|
collaborator:
|
||||||
|
monthly: "$22"
|
||||||
|
annual: "$264"
|
||||||
|
NZD:
|
||||||
|
student:
|
||||||
|
monthly: "$14"
|
||||||
|
annual: "$140"
|
||||||
|
collaborator:
|
||||||
|
monthly: "$22"
|
||||||
|
annual: "$264"
|
||||||
|
CHF:
|
||||||
|
student:
|
||||||
|
monthly: "Fr 10"
|
||||||
|
annual: "Fr 100"
|
||||||
|
collaborator:
|
||||||
|
monthly: "Fr 18"
|
||||||
|
annual: "Fr 216"
|
||||||
|
SGD:
|
||||||
|
student:
|
||||||
|
monthly: "$14"
|
||||||
|
annual: "$140"
|
||||||
|
collaborator:
|
||||||
|
monthly: "$25"
|
||||||
|
annual: "$300"
|
||||||
|
|
||||||
plans:
|
plans:
|
||||||
USD:
|
USD:
|
||||||
symbol: "$"
|
symbol: "$"
|
||||||
|
@ -141,30 +300,48 @@ define [
|
||||||
professional:
|
professional:
|
||||||
monthly: "$40"
|
monthly: "$40"
|
||||||
annual: "$480"
|
annual: "$480"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
App.controller "PlansController", ($scope, $modal, event_tracking, abTestManager, MultiCurrencyPricing, $http, sixpack) ->
|
||||||
|
|
||||||
|
$scope.showPlans = false
|
||||||
|
|
||||||
App.controller "PlansController", ($scope, $modal, event_tracking, abTestManager, MultiCurrencyPricing, $http) ->
|
$scope.plansVariant = 'default'
|
||||||
|
$scope.shouldABTestPlans = window.shouldABTestPlans
|
||||||
|
|
||||||
|
if $scope.shouldABTestPlans
|
||||||
|
sixpack.participate 'plans-1610', ['default', 'heron', 'ibis'], (chosenVariation, rawResponse)->
|
||||||
|
$scope.plansVariant = chosenVariation
|
||||||
|
event_tracking.sendMB 'plans-page', {plans_variant: chosenVariation}
|
||||||
|
if chosenVariation in ['heron', 'ibis']
|
||||||
|
# overwrite student plans with alternative
|
||||||
|
for currency, _v of $scope.plans
|
||||||
|
$scope.plans[currency]['student'] = MultiCurrencyPricing[chosenVariation][currency]['student']
|
||||||
|
$scope.plans[currency]['collaborator'] = MultiCurrencyPricing[chosenVariation][currency]['collaborator']
|
||||||
|
$scope.showPlans = true
|
||||||
|
else
|
||||||
|
$scope.showPlans = true
|
||||||
|
|
||||||
$scope.plans = MultiCurrencyPricing.plans
|
$scope.plans = MultiCurrencyPricing.plans
|
||||||
|
|
||||||
$scope.currencyCode = MultiCurrencyPricing.currencyCode
|
$scope.currencyCode = MultiCurrencyPricing.currencyCode
|
||||||
|
|
||||||
$scope.trial_len = 7
|
$scope.trial_len = 7
|
||||||
|
|
||||||
$scope.planQueryString = '_free_trial_7_days'
|
$scope.planQueryString = '_free_trial_7_days'
|
||||||
|
|
||||||
$scope.ui =
|
$scope.ui =
|
||||||
view: "monthly"
|
view: "monthly"
|
||||||
|
|
||||||
|
|
||||||
$scope.changeCurreny = (newCurrency)->
|
$scope.changeCurreny = (newCurrency)->
|
||||||
$scope.currencyCode = newCurrency
|
$scope.currencyCode = newCurrency
|
||||||
|
|
||||||
$scope.signUpNowClicked = (plan, annual)->
|
$scope.signUpNowClicked = (plan, annual)->
|
||||||
|
event_tracking.sendMB 'plans-page-start-trial', {plan}
|
||||||
if $scope.ui.view == "annual"
|
if $scope.ui.view == "annual"
|
||||||
plan = "#{plan}_annual"
|
plan = "#{plan}_annual"
|
||||||
|
|
||||||
event_tracking.send 'subscription-funnel', 'sign_up_now_button', plan
|
event_tracking.send 'subscription-funnel', 'sign_up_now_button', plan
|
||||||
|
|
||||||
$scope.switchToMonthly = ->
|
$scope.switchToMonthly = ->
|
||||||
|
|
|
@ -1,15 +1,21 @@
|
||||||
define [
|
define [
|
||||||
"base"
|
"base"
|
||||||
], (App)->
|
], (App)->
|
||||||
|
|
||||||
|
App.controller 'SuccessfulSubscriptionController', ($scope, sixpack) ->
|
||||||
|
sixpack.convert 'plans-1610', () ->
|
||||||
|
|
||||||
|
|
||||||
SUBSCRIPTION_URL = "/user/subscription/update"
|
SUBSCRIPTION_URL = "/user/subscription/update"
|
||||||
|
|
||||||
setupReturly = _.once ->
|
setupReturly = _.once ->
|
||||||
recurly?.configure window.recurlyApiKey
|
recurly?.configure window.recurlyApiKey
|
||||||
PRICES = {}
|
PRICES = {}
|
||||||
|
|
||||||
|
|
||||||
App.controller "CurrenyDropdownController", ($scope, MultiCurrencyPricing, $q)->
|
App.controller "CurrenyDropdownController", ($scope, MultiCurrencyPricing, $q)->
|
||||||
|
|
||||||
$scope.plans = MultiCurrencyPricing.plans
|
# $scope.plans = MultiCurrencyPricing.plans
|
||||||
$scope.currencyCode = MultiCurrencyPricing.currencyCode
|
$scope.currencyCode = MultiCurrencyPricing.currencyCode
|
||||||
|
|
||||||
$scope.changeCurrency = (newCurrency)->
|
$scope.changeCurrency = (newCurrency)->
|
||||||
|
@ -31,7 +37,7 @@ define [
|
||||||
$scope.currencyCode = MultiCurrencyPricing.currencyCode
|
$scope.currencyCode = MultiCurrencyPricing.currencyCode
|
||||||
|
|
||||||
$scope.pricing = MultiCurrencyPricing
|
$scope.pricing = MultiCurrencyPricing
|
||||||
$scope.plans = MultiCurrencyPricing.plans
|
# $scope.plans = MultiCurrencyPricing.plans
|
||||||
$scope.currencySymbol = MultiCurrencyPricing.plans[MultiCurrencyPricing.currencyCode].symbol
|
$scope.currencySymbol = MultiCurrencyPricing.plans[MultiCurrencyPricing.currencyCode].symbol
|
||||||
|
|
||||||
$scope.currencyCode = MultiCurrencyPricing.currencyCode
|
$scope.currencyCode = MultiCurrencyPricing.currencyCode
|
||||||
|
@ -87,6 +93,8 @@ define [
|
||||||
|
|
||||||
|
|
||||||
App.controller "UserSubscriptionController", ($scope, MultiCurrencyPricing, $http, sixpack, $modal) ->
|
App.controller "UserSubscriptionController", ($scope, MultiCurrencyPricing, $http, sixpack, $modal) ->
|
||||||
|
$scope.plans = MultiCurrencyPricing.plans
|
||||||
|
|
||||||
freeTrialEndDate = new Date(subscription?.trial_ends_at)
|
freeTrialEndDate = new Date(subscription?.trial_ends_at)
|
||||||
|
|
||||||
sevenDaysTime = new Date()
|
sevenDaysTime = new Date()
|
||||||
|
@ -96,6 +104,16 @@ define [
|
||||||
freeTrialExpiresUnderSevenDays = freeTrialEndDate < sevenDaysTime
|
freeTrialExpiresUnderSevenDays = freeTrialEndDate < sevenDaysTime
|
||||||
|
|
||||||
$scope.view = 'overview'
|
$scope.view = 'overview'
|
||||||
|
$scope.getSuffix = (planCode) ->
|
||||||
|
planCode?.match(/(.*?)_(.*)/)?[2] || null
|
||||||
|
$scope.subscriptionSuffix = $scope.getSuffix(window?.subscription?.planCode)
|
||||||
|
if $scope.subscriptionSuffix == 'free_trial_7_days'
|
||||||
|
$scope.subscriptionSuffix = ''
|
||||||
|
$scope.isNextGenPlan = $scope.subscriptionSuffix in ['heron', 'ibis']
|
||||||
|
|
||||||
|
$scope.shouldShowPlan = (planCode) ->
|
||||||
|
$scope.getSuffix(planCode) not in ['heron', 'ibis']
|
||||||
|
|
||||||
isMonthlyCollab = subscription?.planCode?.indexOf("collaborator") != -1 and subscription?.planCode?.indexOf("ann") == -1
|
isMonthlyCollab = subscription?.planCode?.indexOf("collaborator") != -1 and subscription?.planCode?.indexOf("ann") == -1
|
||||||
stillInFreeTrial = freeTrialInFuture and freeTrialExpiresUnderSevenDays
|
stillInFreeTrial = freeTrialInFuture and freeTrialExpiresUnderSevenDays
|
||||||
|
|
||||||
|
@ -166,6 +184,3 @@ define [
|
||||||
location.reload()
|
location.reload()
|
||||||
.error ->
|
.error ->
|
||||||
console.log "something went wrong changing plan"
|
console.log "something went wrong changing plan"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
BIN
services/web/public/img/teasers/history/teaser-history.png
Normal file
BIN
services/web/public/img/teasers/history/teaser-history.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 114 KiB |
|
@ -205,128 +205,157 @@ var LatexFoldMode = require("./folding/latex").FoldMode;
|
||||||
var Range = require("../range").Range;
|
var Range = require("../range").Range;
|
||||||
var WorkerClient = require("ace/worker/worker_client").WorkerClient;
|
var WorkerClient = require("ace/worker/worker_client").WorkerClient;
|
||||||
|
|
||||||
|
var createLatexWorker = function (session) {
|
||||||
var Mode = function() {
|
|
||||||
this.HighlightRules = LatexHighlightRules;
|
|
||||||
this.foldingRules = new LatexFoldMode();
|
|
||||||
this.createWorker = function(session) {
|
|
||||||
var doc = session.getDocument();
|
var doc = session.getDocument();
|
||||||
var selection = session.getSelection();
|
var selection = session.getSelection();
|
||||||
|
var cursorAnchor = selection.lead;
|
||||||
|
|
||||||
var savedRange = {};
|
var savedRange = {};
|
||||||
var suppressions = [];
|
var suppressions = [];
|
||||||
var hints = [];
|
var hints = [];
|
||||||
var changeHandler = null;
|
var changeHandler = null;
|
||||||
|
var docChangePending = false;
|
||||||
|
var firstPass = true;
|
||||||
|
|
||||||
var worker = new WorkerClient(["ace"], "ace/mode/latex_worker", "LatexWorker");
|
var worker = new WorkerClient(["ace"], "ace/mode/latex_worker", "LatexWorker");
|
||||||
worker.attachToDocument(doc);
|
worker.attachToDocument(doc);
|
||||||
|
var docChangeHandler = doc.on("change", function () {
|
||||||
doc.on("change", function () {
|
docChangePending = true;
|
||||||
if(changeHandler) {
|
if(changeHandler) {
|
||||||
clearTimeout(changeHandler);
|
clearTimeout(changeHandler);
|
||||||
changeHandler = null;
|
changeHandler = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
selection.on("changeCursor", function () {
|
var cursorHandler = selection.on("changeCursor", function () {
|
||||||
if(suppressions.length > 0) {
|
if (docChangePending) { return; } ;
|
||||||
changeHandler = setTimeout(function () {
|
changeHandler = setTimeout(function () {
|
||||||
updateMarkers();
|
updateMarkers({cursorMoveOnly:true});
|
||||||
suppressions = [];
|
suppressions = [];
|
||||||
changeHandler = null;
|
changeHandler = null;
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var updateMarkers = function () {
|
var updateMarkers = function (options) {
|
||||||
|
if (!options) { options = {};};
|
||||||
|
var cursorMoveOnly = options.cursorMoveOnly;
|
||||||
var annotations = [];
|
var annotations = [];
|
||||||
var newRange = {};
|
var newRange = {};
|
||||||
var cursor = selection.getCursor();
|
var cursor = selection.getCursor();
|
||||||
suppressions = [];
|
suppressions = [];
|
||||||
|
|
||||||
for (var i = 0; i<hints.length; i++) {
|
for (var i = 0, len = hints.length; i<len; i++) {
|
||||||
var data = hints[i];
|
var hint = hints[i];
|
||||||
var start_row = data.start_row;
|
|
||||||
var start_col = data.start_col;
|
var suppressedChanges = 0;
|
||||||
var end_row = data.end_row;
|
var hintRange = new Range(hint.start_row, hint.start_col, hint.end_row, hint.end_col);
|
||||||
var end_col = data.end_col;
|
|
||||||
if (data.suppressIfEditing &&
|
var cursorInRange = hintRange.insideStart(cursor.row, cursor.column);
|
||||||
((cursor.row === start_row && cursor.column == start_col+1)
|
var cursorAtStart = hintRange.isStart(cursor.row, cursor.column);
|
||||||
|| (cursor.row === end_row && (cursor.column+1) == end_col))) {
|
var cursorAtEnd = hintRange.isEnd(cursor.row, cursor.column);
|
||||||
suppressions.push([start_row, start_col, end_row, end_col]);
|
if (hint.suppressIfEditing && (cursorAtStart || cursorAtEnd)) {
|
||||||
|
suppressions.push(hintRange);
|
||||||
|
if (!hint.suppressed) { suppressedChanges++; };
|
||||||
|
hint.suppressed = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var suppress = false;
|
var isCascadeError = false;
|
||||||
for (var j = 0; j < suppressions.length; j++) {
|
for (var j = 0, suplen = suppressions.length; j < suplen; j++) {
|
||||||
var e=suppressions[j];
|
var badRange = suppressions[j];
|
||||||
var fromRow=e[0], fromCol=e[1], toRow=e[2], toCol=e[3];
|
if (badRange.intersects(hintRange)) {
|
||||||
if (start_row == fromRow && start_col >= fromCol && start_row === toRow && start_col <= toCol) {
|
isCascadeError = true;
|
||||||
suppress = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(suppress) { continue; };
|
if(isCascadeError) {
|
||||||
|
if (!hint.suppressed) { suppressedChanges++; };
|
||||||
|
hint.suppressed = true;
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
var key = "(" + start_row + "," + start_col + ")" + ":" + "(" + end_row + "," + end_col + ")";
|
if (hint.suppressed) { suppressedChanges++; };
|
||||||
newRange[key] = data;
|
hint.suppressed = false;
|
||||||
annotations.push(data);
|
|
||||||
|
annotations.push(hint);
|
||||||
|
if (hint.type === "info") {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
var key = hintRange.toString() + (cursorInRange ? "+cursor" : "");
|
||||||
|
newRange[key] = {hint: hint, cursorInRange: cursorInRange, range: hintRange};
|
||||||
}
|
}
|
||||||
|
for (key in newRange) {
|
||||||
var newKeys = Object.keys(newRange);
|
if (!savedRange[key]) { // doesn't exist in already displayed errors
|
||||||
var oldKeys = Object.keys(savedRange);
|
var new_range = newRange[key].range;
|
||||||
var changes = 0;
|
cursorInRange = newRange[key].cursorInRange;
|
||||||
for (i = 0; i < newKeys.length; i++) {
|
hint = newRange[key].hint;
|
||||||
key = newKeys[i];
|
var errorAtStart = (hint.row === hint.start_row && hint.column === hint.start_col);
|
||||||
if (!savedRange[key]) {
|
var a = (cursorInRange && !errorAtStart) ? cursorAnchor : doc.createAnchor(new_range.start);
|
||||||
var new_range = newRange[key];
|
var b = (cursorInRange && errorAtStart) ? cursorAnchor : doc.createAnchor(new_range.end);
|
||||||
var a = doc.createAnchor(new_range.start_row, new_range.start_col);
|
|
||||||
var b = doc.createAnchor(new_range.end_row, new_range.end_col);
|
|
||||||
var range = new Range();
|
var range = new Range();
|
||||||
range.start = a;
|
range.start = a;
|
||||||
range.end = b;
|
range.end = b;
|
||||||
range.id = session.addMarker(range, "ace_error-marker", "text");
|
var cssClass = "ace_error-marker";
|
||||||
|
if (hint.type === "warning") { cssClass = "ace_highlight-marker"; };
|
||||||
|
range.id = session.addMarker(range, cssClass, "text");
|
||||||
savedRange[key] = range;
|
savedRange[key] = range;
|
||||||
changes++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (key in savedRange) {
|
||||||
for (i = 0; i < oldKeys.length; i++) {
|
if (!newRange[key]) { // no longer present in list of errors to display
|
||||||
key = oldKeys[i];
|
|
||||||
if (!newRange[key]) {
|
|
||||||
range = savedRange[key];
|
range = savedRange[key];
|
||||||
range.start.detach();
|
if (range.start !== cursorAnchor) { range.start.detach(); }
|
||||||
range.end.detach();
|
if (range.end !== cursorAnchor) { range.end.detach(); }
|
||||||
session.removeMarker(range.id);
|
session.removeMarker(range.id);
|
||||||
delete savedRange[key];
|
delete savedRange[key];
|
||||||
changes++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!cursorMoveOnly || suppressedChanges) {
|
||||||
if (changes>0) {
|
if (firstPass) {
|
||||||
|
if (annotations.length > 0) {
|
||||||
|
var originalAnnotations = session.getAnnotations();
|
||||||
|
session.setAnnotations(originalAnnotations.concat(annotations));
|
||||||
|
};
|
||||||
|
firstPass = false;
|
||||||
|
} else {
|
||||||
session.setAnnotations(annotations);
|
session.setAnnotations(annotations);
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
};
|
||||||
worker.on("lint", function(results) {
|
worker.on("lint", function(results) {
|
||||||
|
if(docChangePending) { docChangePending = false; };
|
||||||
hints = results.data;
|
hints = results.data;
|
||||||
if (hints.length > 100) {
|
if (hints.length > 100) {
|
||||||
hints = hints.slice(0, 100); // limit to 100 errors
|
hints = hints.slice(0, 100); // limit to 100 errors
|
||||||
};
|
};
|
||||||
updateMarkers();
|
updateMarkers();
|
||||||
});
|
});
|
||||||
|
|
||||||
worker.on("terminate", function() {
|
worker.on("terminate", function() {
|
||||||
var oldKeys = Object.keys(savedRange);
|
if(changeHandler) {
|
||||||
for (var i = 0; i < oldKeys.length; i++) {
|
clearTimeout(changeHandler);
|
||||||
var key = oldKeys[i];
|
changeHandler = null;
|
||||||
var range = savedRange[key];
|
|
||||||
session.removeMarker(range.id);
|
|
||||||
delete savedRange[key];
|
|
||||||
}
|
}
|
||||||
|
doc.off("change", docChangeHandler);
|
||||||
|
selection.off("changeCursor", cursorHandler);
|
||||||
|
for (var key in savedRange) {
|
||||||
|
var range = savedRange[key];
|
||||||
|
if (range.start !== cursorAnchor) { range.start.detach(); }
|
||||||
|
if (range.end !== cursorAnchor) { range.end.detach(); }
|
||||||
|
session.removeMarker(range.id);
|
||||||
|
}
|
||||||
|
savedRange = {};
|
||||||
|
hints = [];
|
||||||
|
suppressions = [];
|
||||||
|
session.clearAnnotations();
|
||||||
});
|
});
|
||||||
|
|
||||||
return worker;
|
return worker;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var Mode = function() {
|
||||||
|
this.HighlightRules = LatexHighlightRules;
|
||||||
|
this.foldingRules = new LatexFoldMode();
|
||||||
|
this.createWorker = createLatexWorker;
|
||||||
};
|
};
|
||||||
oop.inherits(Mode, TextMode);
|
oop.inherits(Mode, TextMode);
|
||||||
|
|
||||||
|
|
|
@ -1419,107 +1419,28 @@ var LatexWorker = exports.LatexWorker = function(sender) {
|
||||||
|
|
||||||
oop.inherits(LatexWorker, Mirror);
|
oop.inherits(LatexWorker, Mirror);
|
||||||
|
|
||||||
var Parse = function (text) {
|
var Tokenise = function (text) {
|
||||||
var errors = [];
|
|
||||||
var Comments = [];
|
|
||||||
var Tokens = [];
|
var Tokens = [];
|
||||||
var Environments = [];
|
var Comments = [];
|
||||||
var pos = -1;
|
var pos = -1;
|
||||||
var SPECIAL = /[\\\{\}\$\&\#\^\_\~\%]/g;
|
var SPECIAL = /[\\\{\}\$\&\#\^\_\~\%]/g; // match TeX special characters
|
||||||
var CS = /[^a-zA-Z]/g;
|
var NEXTCS = /[^a-zA-Z]/g; // match characters which aren't part of a TeX control sequence
|
||||||
var idx = 0;
|
var idx = 0;
|
||||||
var lineNumber = 0;
|
|
||||||
var linePosition = [];
|
var lineNumber = 0; // current line number when parsing tokens (zero-based)
|
||||||
|
var linePosition = []; // mapping from line number to absolute offset of line in text[]
|
||||||
linePosition[0] = 0;
|
linePosition[0] = 0;
|
||||||
|
|
||||||
var TokenError = function (token, message) {
|
|
||||||
var line = token[0], type = token[1], start = token[2], end = token[3], seq = token[4];
|
|
||||||
var start_col = start - linePosition[line];
|
|
||||||
var end_col = end - linePosition[line] + 1;
|
|
||||||
errors.push({row: line,
|
|
||||||
column: start_col,
|
|
||||||
start_row:line,
|
|
||||||
start_col: start_col,
|
|
||||||
end_row:line,
|
|
||||||
end_col: end_col,
|
|
||||||
type:"error",
|
|
||||||
text:message,
|
|
||||||
suppressIfEditing:true});
|
|
||||||
};
|
|
||||||
|
|
||||||
var TokenErrorFromTo = function (fromToken, toToken, message) {
|
|
||||||
var fromLine = fromToken[0], fromStart = fromToken[2], fromEnd = fromToken[3];
|
|
||||||
var toLine = toToken[0], toStart = toToken[2], toEnd = toToken[3];
|
|
||||||
if (!toEnd) { toEnd = toStart + 1;};
|
|
||||||
var start_col = fromStart - linePosition[fromLine];
|
|
||||||
var end_col = toEnd - linePosition[toLine] + 1;
|
|
||||||
|
|
||||||
errors.push({row: line,
|
|
||||||
column: start_col,
|
|
||||||
start_row: fromLine,
|
|
||||||
start_col: start_col,
|
|
||||||
end_row: toLine,
|
|
||||||
end_col: end_col,
|
|
||||||
type:"error",
|
|
||||||
text:message,
|
|
||||||
suppressIfEditing:true});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
var EnvErrorFromTo = function (fromEnv, toEnv, message, options) {
|
|
||||||
if(!options) { options = {} ; };
|
|
||||||
var fromToken = fromEnv.token, toToken = toEnv.closeToken || toEnv.token;
|
|
||||||
var fromLine = fromToken[0], fromStart = fromToken[2], fromEnd = fromToken[3], fromSeq = fromToken[4];
|
|
||||||
if (!toToken) {toToken = fromToken;};
|
|
||||||
var toLine = toToken[0], toStart = toToken[2], toEnd = toToken[3], toSeq = toToken[4];
|
|
||||||
if (!toEnd) { toEnd = toStart + 1;};
|
|
||||||
var start_col = fromStart - linePosition[fromLine];
|
|
||||||
var end_col = toEnd - linePosition[toLine] + 1;
|
|
||||||
errors.push({row:toLine,
|
|
||||||
column:end_col,
|
|
||||||
start_row:fromLine,
|
|
||||||
start_col: start_col,
|
|
||||||
end_row:toLine,
|
|
||||||
end_col: end_col,
|
|
||||||
type:"error",
|
|
||||||
text:message,
|
|
||||||
suppressIfEditing:options.suppressIfEditing});
|
|
||||||
};
|
|
||||||
|
|
||||||
var EnvErrorTo = function (toEnv, message) {
|
|
||||||
var token = toEnv.closeToken || toEnv.token;
|
|
||||||
var line = token[0], type = token[1], start = token[2], end = token[3], seq = token[4];
|
|
||||||
if (!end) { end = start + 1; };
|
|
||||||
var end_col = end - linePosition[line] + 1;
|
|
||||||
var err = {row: line,
|
|
||||||
column: end_col,
|
|
||||||
start_row:0,
|
|
||||||
start_col: 0,
|
|
||||||
end_row: line,
|
|
||||||
end_col: end_col,
|
|
||||||
type:"error",
|
|
||||||
text:message};
|
|
||||||
errors.push(err);
|
|
||||||
};
|
|
||||||
|
|
||||||
var EnvErrorFrom = function (env, message) {
|
|
||||||
var token = env.token;
|
|
||||||
var line = token[0], type = token[1], start = token[2], end = token[3], seq = token[4];
|
|
||||||
var start_col = start - linePosition[line];
|
|
||||||
var end_col = Infinity;
|
|
||||||
errors.push({row: line,
|
|
||||||
column: start_col,
|
|
||||||
start_row:line,
|
|
||||||
start_col: start_col,
|
|
||||||
end_row: lineNumber,
|
|
||||||
end_col: end_col,
|
|
||||||
type:"error",
|
|
||||||
text:message});
|
|
||||||
};
|
|
||||||
|
|
||||||
var checkingDisabled = false;
|
var checkingDisabled = false;
|
||||||
|
var count = 0; // number of tokens parses
|
||||||
|
var MAX_TOKENS = 100000;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
count++;
|
||||||
|
if (count > MAX_TOKENS) {
|
||||||
|
throw new Error("exceed max token count of " + MAX_TOKENS);
|
||||||
|
break;
|
||||||
|
};
|
||||||
var result = SPECIAL.exec(text);
|
var result = SPECIAL.exec(text);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
if (idx < text.length) {
|
if (idx < text.length) {
|
||||||
|
@ -1528,10 +1449,10 @@ var Parse = function (text) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (result && result.index <= pos) {
|
if (result && result.index <= pos) {
|
||||||
|
throw new Error("infinite loop in parsing");
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
pos = result.index;
|
pos = result.index;
|
||||||
var newIdx = SPECIAL.lastIndex;
|
|
||||||
if (pos > idx) {
|
if (pos > idx) {
|
||||||
Tokens.push([lineNumber, "Text", idx, pos]);
|
Tokens.push([lineNumber, "Text", idx, pos]);
|
||||||
}
|
}
|
||||||
|
@ -1541,9 +1462,11 @@ var Parse = function (text) {
|
||||||
linePosition[lineNumber] = i+1;
|
linePosition[lineNumber] = i+1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var newIdx = SPECIAL.lastIndex;
|
||||||
idx = newIdx;
|
idx = newIdx;
|
||||||
var code = result[0];
|
var code = result[0];
|
||||||
if (code === "%") {
|
if (code === "%") { // comment character
|
||||||
var newLinePos = text.indexOf("\n", idx);
|
var newLinePos = text.indexOf("\n", idx);
|
||||||
if (newLinePos === -1) {
|
if (newLinePos === -1) {
|
||||||
newLinePos = text.length;
|
newLinePos = text.length;
|
||||||
|
@ -1562,9 +1485,9 @@ var Parse = function (text) {
|
||||||
linePosition[lineNumber] = idx;
|
linePosition[lineNumber] = idx;
|
||||||
} else if (checkingDisabled) {
|
} else if (checkingDisabled) {
|
||||||
continue;
|
continue;
|
||||||
} else if (code === '\\') {
|
} else if (code === '\\') { // escape character
|
||||||
CS.lastIndex = idx;
|
NEXTCS.lastIndex = idx;
|
||||||
var controlSequence = CS.exec(text);
|
var controlSequence = NEXTCS.exec(text);
|
||||||
var nextSpecialPos = controlSequence === null ? idx : controlSequence.index;
|
var nextSpecialPos = controlSequence === null ? idx : controlSequence.index;
|
||||||
if (nextSpecialPos === idx) {
|
if (nextSpecialPos === idx) {
|
||||||
Tokens.push([lineNumber, code, pos, idx + 1, text[idx]]);
|
Tokens.push([lineNumber, code, pos, idx + 1, text[idx]]);
|
||||||
|
@ -1580,50 +1503,67 @@ var Parse = function (text) {
|
||||||
}
|
}
|
||||||
idx = SPECIAL.lastIndex = nextSpecialPos;
|
idx = SPECIAL.lastIndex = nextSpecialPos;
|
||||||
}
|
}
|
||||||
} else if (code === "{") {
|
} else if (code === "{") { // open group
|
||||||
Tokens.push([lineNumber, code, pos]);
|
Tokens.push([lineNumber, code, pos]);
|
||||||
} else if (code === "}") {
|
} else if (code === "}") { // close group
|
||||||
Tokens.push([lineNumber, code, pos]);
|
Tokens.push([lineNumber, code, pos]);
|
||||||
} else if (code === "$") {
|
} else if (code === "$") { // math mode
|
||||||
if (text[idx] === "$") {
|
if (text[idx] === "$") {
|
||||||
idx = SPECIAL.lastIndex = idx + 1;
|
idx = SPECIAL.lastIndex = idx + 1;
|
||||||
Tokens.push([lineNumber, "$$", pos]);
|
Tokens.push([lineNumber, "$$", pos]);
|
||||||
} else {
|
} else {
|
||||||
Tokens.push([lineNumber, code, pos]);
|
Tokens.push([lineNumber, code, pos]);
|
||||||
}
|
}
|
||||||
} else if (code === "&") {
|
} else if (code === "&") { // tabalign
|
||||||
Tokens.push([lineNumber, code, pos]);
|
Tokens.push([lineNumber, code, pos]);
|
||||||
} else if (code === "#") {
|
} else if (code === "#") { // macro parameter
|
||||||
Tokens.push([lineNumber, code, pos]);
|
Tokens.push([lineNumber, code, pos]);
|
||||||
} else if (code === "^") {
|
} else if (code === "^") { // superscript
|
||||||
Tokens.push([lineNumber, code, pos]);
|
Tokens.push([lineNumber, code, pos]);
|
||||||
} else if (code === "_") {
|
} else if (code === "_") { // subscript
|
||||||
Tokens.push([lineNumber, code, pos]);
|
Tokens.push([lineNumber, code, pos]);
|
||||||
} else if (code === "~") {
|
} else if (code === "~") { // active character (space)
|
||||||
Tokens.push([lineNumber, code, pos]);
|
Tokens.push([lineNumber, code, pos]);
|
||||||
} else {
|
} else {
|
||||||
throw "unrecognised character " + code;
|
throw "unrecognised character " + code;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var read1arg = function (k) {
|
return {tokens: Tokens, comments: Comments, linePosition: linePosition, lineNumber: lineNumber, text: text};
|
||||||
|
};
|
||||||
|
|
||||||
|
var read1arg = function (TokeniseResult, k, options) {
|
||||||
|
var Tokens = TokeniseResult.tokens;
|
||||||
|
var text = TokeniseResult.text;
|
||||||
|
if (options && options.allowStar) {
|
||||||
|
var optional = Tokens[k+1];
|
||||||
|
if (optional && optional[1] === "Text") {
|
||||||
|
var optionalstr = text.substring(optional[2], optional[3]);
|
||||||
|
if (optionalstr === "*") { k++;}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
var open = Tokens[k+1];
|
var open = Tokens[k+1];
|
||||||
var env = Tokens[k+2];
|
var env = Tokens[k+2];
|
||||||
var close = Tokens[k+3];
|
var close = Tokens[k+3];
|
||||||
var envName;
|
var envName;
|
||||||
|
|
||||||
if(open && open[1] === "\\") {
|
if(open && open[1] === "\\") {
|
||||||
envName = open[4];
|
envName = open[4]; // array element 4 is command sequence
|
||||||
return k + 1;
|
return k + 1;
|
||||||
} else if(open && open[1] === "{" && env && env[1] === "\\" && close && close[1] === "}") {
|
} else if(open && open[1] === "{" && env && env[1] === "\\" && close && close[1] === "}") {
|
||||||
envName = env[4];
|
envName = env[4]; // NOTE: if we were actually using this, keep track of * above
|
||||||
return k + 3;
|
return k + 3; // array element 4 is command sequence
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var read1name = function (k) {
|
|
||||||
|
var read1name = function (TokeniseResult, k) {
|
||||||
|
var Tokens = TokeniseResult.tokens;
|
||||||
|
var text = TokeniseResult.text;
|
||||||
|
|
||||||
var open = Tokens[k+1];
|
var open = Tokens[k+1];
|
||||||
var env = Tokens[k+2];
|
var env = Tokens[k+2];
|
||||||
var close = Tokens[k+3];
|
var close = Tokens[k+3];
|
||||||
|
@ -1631,35 +1571,57 @@ var Parse = function (text) {
|
||||||
if(open && open[1] === "{" && env && env[1] === "Text" && close && close[1] === "}") {
|
if(open && open[1] === "{" && env && env[1] === "Text" && close && close[1] === "}") {
|
||||||
var envName = text.substring(env[2], env[3]);
|
var envName = text.substring(env[2], env[3]);
|
||||||
return k + 3;
|
return k + 3;
|
||||||
|
} else if (open && open[1] === "{" && env && env[1] === "Text") {
|
||||||
|
envName = "";
|
||||||
|
for (var j = k + 2, tok; (tok = Tokens[j]); j++) {
|
||||||
|
if (tok[1] === "Text") {
|
||||||
|
var str = text.substring(tok[2], tok[3]);
|
||||||
|
if (!str.match(/^\S*$/)) { break; }
|
||||||
|
envName = envName + str;
|
||||||
|
} else if (tok[1] === "_") {
|
||||||
|
envName = envName + "_";
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tok && tok[1] === "}") {
|
||||||
|
return j; // advance past these tokens
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var readOptionalParams = function(TokeniseResult, k) {
|
||||||
|
var Tokens = TokeniseResult.tokens;
|
||||||
|
var text = TokeniseResult.text;
|
||||||
|
|
||||||
|
|
||||||
var readOptionalParams = function(k) {
|
|
||||||
var params = Tokens[k+1];
|
var params = Tokens[k+1];
|
||||||
|
|
||||||
if(params && params[1] === "Text") {
|
if(params && params[1] === "Text") {
|
||||||
var paramNum = text.substring(params[2], params[3]);
|
var paramNum = text.substring(params[2], params[3]);
|
||||||
if (paramNum.match(/^\[\d+\]$/)) {
|
if (paramNum.match(/^\[\d+\](\[[^\]]*\])*\s*$/)) {
|
||||||
return k + 1;
|
return k + 1; // got it
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
var readDefinition = function(k) {
|
var readDefinition = function(TokeniseResult, k) {
|
||||||
|
var Tokens = TokeniseResult.tokens;
|
||||||
|
var text = TokeniseResult.text;
|
||||||
|
|
||||||
k = k + 1;
|
k = k + 1;
|
||||||
var count = 0;
|
var count = 0;
|
||||||
var nextToken = Tokens[k];
|
var nextToken = Tokens[k];
|
||||||
while (nextToken && nextToken[1] === "Text") {
|
while (nextToken && nextToken[1] === "Text") {
|
||||||
var start = nextToken[2], end = nextToken[3];
|
var start = nextToken[2], end = nextToken[3];
|
||||||
for (i = start; i < end; i++) {
|
for (var i = start; i < end; i++) {
|
||||||
var char = text[i];
|
var char = text[i];
|
||||||
if (char === ' ' || char === '\t' || char === '\r' || char === '\n') { continue; }
|
if (char === ' ' || char === '\t' || char === '\r' || char === '\n') { continue; }
|
||||||
return null;
|
return null; // bail out, should begin with a {
|
||||||
}
|
}
|
||||||
k++;
|
k++;
|
||||||
nextToken = Tokens[k];
|
nextToken = Tokens[k];
|
||||||
|
@ -1675,34 +1637,120 @@ var Parse = function (text) {
|
||||||
}
|
}
|
||||||
return k;
|
return k;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
for (var _j = 0, _len = Tokens.length; _j < _len; _j++) {
|
var readVerb = function(TokeniseResult, k) {
|
||||||
var token = Tokens[_j];
|
|
||||||
|
var Tokens = TokeniseResult.tokens;
|
||||||
|
var text = TokeniseResult.text;
|
||||||
|
|
||||||
|
var verbToken = Tokens[k];
|
||||||
|
var verbStr = text.substring(verbToken[2], verbToken[3]);
|
||||||
|
var pos = verbToken[3];
|
||||||
|
if (text[pos] === "*") { pos++; } // \verb* form of command
|
||||||
|
var delimiter = text[pos];
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
var nextToken = Tokens[k+1];
|
||||||
|
for (var i = pos, end = text.length; i < end; i++) {
|
||||||
|
var char = text[i];
|
||||||
|
if (nextToken && i >= nextToken[2]) { k++; nextToken = Tokens[k+1];};
|
||||||
|
if (char === delimiter) { return k; };
|
||||||
|
if (char === '\r' || char === '\n') { return null; }
|
||||||
|
};
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
var readUrl = function(TokeniseResult, k) {
|
||||||
|
|
||||||
|
var Tokens = TokeniseResult.tokens;
|
||||||
|
var text = TokeniseResult.text;
|
||||||
|
|
||||||
|
var urlToken = Tokens[k];
|
||||||
|
var urlStr = text.substring(urlToken[2], urlToken[3]);
|
||||||
|
var pos = urlToken[3];
|
||||||
|
var openDelimiter = text[pos];
|
||||||
|
var closeDelimiter = (openDelimiter === "{") ? "}" : openDelimiter;
|
||||||
|
var nextToken = Tokens[k+1];
|
||||||
|
if (nextToken && pos === nextToken[2]) {
|
||||||
|
k++;
|
||||||
|
nextToken = Tokens[k+1];
|
||||||
|
};
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
var count = 1;
|
||||||
|
for (var i = pos, end = text.length; count > 0 && i < end; i++) {
|
||||||
|
var char = text[i];
|
||||||
|
if (nextToken && i >= nextToken[2]) { k++; nextToken = Tokens[k+1];};
|
||||||
|
if (char === closeDelimiter) {
|
||||||
|
count--;
|
||||||
|
} else if (char === openDelimiter) {
|
||||||
|
count++;
|
||||||
|
};
|
||||||
|
if (count === 0) { return k; };
|
||||||
|
if (char === '\r' || char === '\n') { return null; }
|
||||||
|
};
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var InterpretTokens = function (TokeniseResult, ErrorReporter) {
|
||||||
|
var Tokens = TokeniseResult.tokens;
|
||||||
|
var linePosition = TokeniseResult.linePosition;
|
||||||
|
var lineNumber = TokeniseResult.lineNumber;
|
||||||
|
var text = TokeniseResult.text;
|
||||||
|
|
||||||
|
var TokenErrorFromTo = ErrorReporter.TokenErrorFromTo;
|
||||||
|
var TokenError = ErrorReporter.TokenError;
|
||||||
|
var Environments = [];
|
||||||
|
|
||||||
|
for (var i = 0, len = Tokens.length; i < len; i++) {
|
||||||
|
var token = Tokens[i];
|
||||||
var line = token[0], type = token[1], start = token[2], end = token[3], seq = token[4];
|
var line = token[0], type = token[1], start = token[2], end = token[3], seq = token[4];
|
||||||
if (type === "\\") {
|
if (type === "\\") {
|
||||||
if (seq === "begin" || seq === "end") {
|
if (seq === "begin" || seq === "end") {
|
||||||
var open = Tokens[_j+1];
|
var open = Tokens[i+1];
|
||||||
var env = Tokens[_j+2];
|
var env = Tokens[i+2];
|
||||||
var close = Tokens[_j+3];
|
var close = Tokens[i+3];
|
||||||
if(open && open[1] === "{" && env && env[1] === "Text" && close && close[1] === "}") {
|
if(open && open[1] === "{" && env && env[1] === "Text" && close && close[1] === "}") {
|
||||||
var envName = text.substring(env[2], env[3]);
|
var envName = text.substring(env[2], env[3]);
|
||||||
Environments.push({command: seq, name: envName, token: token, closeToken: close});
|
Environments.push({command: seq, name: envName, token: token, closeToken: close});
|
||||||
_j = _j + 3; // advance past these tokens
|
i = i + 3; // advance past these tokens
|
||||||
} else {
|
} else {
|
||||||
|
if (open && open[1] === "{" && env && env[1] === "Text") {
|
||||||
|
envName = "";
|
||||||
|
for (var j = i + 2, tok; (tok = Tokens[j]); j++) {
|
||||||
|
if (tok[1] === "Text") {
|
||||||
|
var str = text.substring(tok[2], tok[3]);
|
||||||
|
if (!str.match(/^\S*$/)) { break; }
|
||||||
|
envName = envName + str;
|
||||||
|
} else if (tok[1] === "_") {
|
||||||
|
envName = envName + "_";
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tok && tok[1] === "}") {
|
||||||
|
Environments.push({command: seq, name: envName, token: token, closeToken: close});
|
||||||
|
i = j; // advance past these tokens
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
var endToken = null;
|
var endToken = null;
|
||||||
if (open && open[1] === "{") {
|
if (open && open[1] === "{") {
|
||||||
endToken = open;
|
endToken = open; // we've got a {
|
||||||
|
|
||||||
if (env && env[1] === "Text") {
|
if (env && env[1] === "Text") {
|
||||||
endToken = env.slice();
|
endToken = env.slice(); // we've got some text following the {
|
||||||
start = endToken[2]; end = endToken[3];
|
start = endToken[2]; end = endToken[3];
|
||||||
for (i = start; i < end; i++) {
|
for (j = start; j < end; j++) {
|
||||||
char = text[i];
|
var char = text[j];
|
||||||
if (char === ' ' || char === '\t' || char === '\r' || char === '\n') { break; }
|
if (char === ' ' || char === '\t' || char === '\r' || char === '\n') { break; }
|
||||||
}
|
}
|
||||||
endToken[3] = i;
|
endToken[3] = j; // the end of partial token is as far as we got looking ahead
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1713,68 +1761,116 @@ var Parse = function (text) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else if (seq === "newcommand" || seq === "renewcommand" || seq === "def" || seq === "DeclareRobustCommand") {
|
} else if (seq === "newcommand" || seq === "renewcommand" || seq === "def" || seq === "DeclareRobustCommand") {
|
||||||
|
var newPos = read1arg(TokeniseResult, i, {allowStar: (seq != "def")});
|
||||||
|
if (newPos === null) { continue; } else {i = newPos;};
|
||||||
|
newPos = readOptionalParams(TokeniseResult, i);
|
||||||
|
if (newPos === null) { /* do nothing */ } else {i = newPos;};
|
||||||
|
newPos = readDefinition(TokeniseResult, i);
|
||||||
|
if (newPos === null) { /* do nothing */ } else {i = newPos;};
|
||||||
|
|
||||||
var newPos = read1arg(_j);
|
} else if (seq === "newcolumntype") {
|
||||||
if (newPos === null) { continue; } else {_j = newPos;};
|
newPos = read1name(TokeniseResult, i);
|
||||||
|
if (newPos === null) { continue; } else {i = newPos;};
|
||||||
|
newPos = readOptionalParams(TokeniseResult, i);
|
||||||
|
if (newPos === null) { /* do nothing */ } else {i = newPos;};
|
||||||
|
newPos = readDefinition(TokeniseResult, i);
|
||||||
|
if (newPos === null) { /* do nothing */ } else {i = newPos;};
|
||||||
|
|
||||||
newPos = readOptionalParams(_j);
|
} else if (seq === "newenvironment" || seq === "renewenvironment") {
|
||||||
if (newPos === null) { /* do nothing */ } else {_j = newPos;};
|
newPos = read1name(TokeniseResult, i);
|
||||||
|
if (newPos === null) { continue; } else {i = newPos;};
|
||||||
newPos = readDefinition(_j);
|
newPos = readOptionalParams(TokeniseResult, i);
|
||||||
if (newPos === null) { /* do nothing */ } else {_j = newPos;};
|
if (newPos === null) { /* do nothing */ } else {i = newPos;};
|
||||||
|
newPos = readDefinition(TokeniseResult, i);
|
||||||
} else if (seq === "newenvironment") {
|
if (newPos === null) { /* do nothing */ } else {i = newPos;};
|
||||||
|
newPos = readDefinition(TokeniseResult, i);
|
||||||
newPos = read1name(_j);
|
if (newPos === null) { /* do nothing */ } else {i = newPos;};
|
||||||
if (newPos === null) { continue; } else {_j = newPos;};
|
} else if (seq === "verb") {
|
||||||
|
newPos = readVerb(TokeniseResult, i);
|
||||||
newPos = readOptionalParams(_j);
|
if (newPos === null) { TokenError(token, "invalid verbatim command"); } else {i = newPos;};
|
||||||
if (newPos === null) { /* do nothing */ } else {_j = newPos;};
|
} else if (seq === "url") {
|
||||||
|
newPos = readUrl(TokeniseResult, i);
|
||||||
newPos = readDefinition(_j);
|
if (newPos === null) { TokenError(token, "invalid url command"); } else {i = newPos;};
|
||||||
if (newPos === null) { /* do nothing */ } else {_j = newPos;};
|
|
||||||
|
|
||||||
newPos = readDefinition(_j);
|
|
||||||
if (newPos === null) { /* do nothing */ } else {_j = newPos;};
|
|
||||||
}
|
}
|
||||||
} else if (type === "{") {
|
} else if (type === "{") {
|
||||||
Environments.push({command:"{", token:token});
|
Environments.push({command:"{", token:token});
|
||||||
} else if (type === "}") {
|
} else if (type === "}") {
|
||||||
Environments.push({command:"}", token:token});
|
Environments.push({command:"}", token:token});
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
return Environments;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var CheckEnvironments = function (Environments, ErrorReporter) {
|
||||||
|
var ErrorTo = ErrorReporter.EnvErrorTo;
|
||||||
|
var ErrorFromTo = ErrorReporter.EnvErrorFromTo;
|
||||||
|
var ErrorFrom = ErrorReporter.EnvErrorFrom;
|
||||||
|
|
||||||
if ((start != null) && (end != null) && (seq != null)) {
|
|
||||||
} else if ((start != null) && (end != null)) {
|
|
||||||
} else if (start != null) {
|
|
||||||
} else {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var state = [];
|
var state = [];
|
||||||
for (i = 0; i < Environments.length; i++) {
|
var documentClosed = null;
|
||||||
|
var inVerbatim = false;
|
||||||
|
var verbatimRanges = [];
|
||||||
|
for (var i = 0, len = Environments.length; i < len; i++) {
|
||||||
|
var name = Environments[i].name ;
|
||||||
|
if (name && name.match(/^(verbatim|boxedverbatim|lstlisting)$/)) {
|
||||||
|
Environments[i].verbatim = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0, len = Environments.length; i < len; i++) {
|
||||||
var thisEnv = Environments[i];
|
var thisEnv = Environments[i];
|
||||||
if(thisEnv.command === "begin" || thisEnv.command === "{") {
|
if(thisEnv.command === "begin" || thisEnv.command === "{") {
|
||||||
|
if (inVerbatim) { continue; } // ignore anything in verbatim environments
|
||||||
|
if (thisEnv.verbatim) {inVerbatim = true;};
|
||||||
state.push(thisEnv);
|
state.push(thisEnv);
|
||||||
} else if (thisEnv.command === "end" || thisEnv.command === "}") {
|
} else if (thisEnv.command === "end" || thisEnv.command === "}") {
|
||||||
var lastEnv = state.pop();
|
var lastEnv = state.pop();
|
||||||
if (!lastEnv) {
|
|
||||||
if (thisEnv.command === "}") {
|
if (inVerbatim) {
|
||||||
EnvErrorTo(thisEnv, "unexpected end group }");
|
if (lastEnv && lastEnv.name === thisEnv.name) {
|
||||||
} else if (thisEnv.command === "end") {
|
inVerbatim = false;
|
||||||
EnvErrorTo(thisEnv, "unexpected \\end{" + thisEnv.name + "}");
|
verbatimRanges.push({start: lastEnv.token[2], end: thisEnv.token[2]});
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
if(lastEnv) { state.push(lastEnv); } ;
|
||||||
|
continue; // ignore all other commands
|
||||||
}
|
}
|
||||||
} else if (lastEnv.command === "{" && thisEnv.command === "}") {
|
};
|
||||||
continue; // closed group correctly
|
|
||||||
} else if (lastEnv.name === thisEnv.name) {
|
if (lastEnv && lastEnv.command === "{" && thisEnv.command === "}") {
|
||||||
continue; // closed environment correctly
|
continue;
|
||||||
} else if (thisEnv.command === "}") {
|
} else if (lastEnv && lastEnv.name === thisEnv.name) {
|
||||||
EnvErrorFromTo(lastEnv, thisEnv, "unexpected end group } after \\begin{" + lastEnv.name +"}");
|
if (thisEnv.name === "document" && !documentClosed) {
|
||||||
|
documentClosed = thisEnv;
|
||||||
|
};
|
||||||
|
continue;
|
||||||
|
} else if (!lastEnv) {
|
||||||
|
if (thisEnv.command === "}") {
|
||||||
|
if (documentClosed) {
|
||||||
|
ErrorFromTo(documentClosed, thisEnv, "\\end{" + documentClosed.name + "} is followed by unexpected end group }",{errorAtStart: true, type: "info"});
|
||||||
|
} else {
|
||||||
|
ErrorTo(thisEnv, "unexpected end group }");
|
||||||
|
};
|
||||||
|
} else if (thisEnv.command === "end") {
|
||||||
|
if (documentClosed) {
|
||||||
|
ErrorFromTo(documentClosed, thisEnv, "\\end{" + documentClosed.name + "} is followed by unexpected content",{errorAtStart: true, type: "info"});
|
||||||
|
} else {
|
||||||
|
ErrorTo(thisEnv, "unexpected \\end{" + thisEnv.name + "}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (lastEnv.command === "begin" && thisEnv.command === "}") {
|
||||||
|
ErrorFromTo(lastEnv, thisEnv, "unexpected end group } after \\begin{" + lastEnv.name +"}");
|
||||||
state.push(lastEnv);
|
state.push(lastEnv);
|
||||||
} else if (lastEnv.command === "{" && thisEnv.command === "end") {
|
} else if (lastEnv.command === "{" && thisEnv.command === "end") {
|
||||||
EnvErrorFromTo(lastEnv, thisEnv, "unexpected \\end{" + thisEnv.name + "} inside group {", {suppressIfEditing:true});
|
ErrorFromTo(lastEnv, thisEnv,
|
||||||
|
"unclosed group { found at \\end{" + thisEnv.name + "}",
|
||||||
|
{suppressIfEditing:true, errorAtStart: true, type:"warning"});
|
||||||
i--;
|
i--;
|
||||||
} else if (lastEnv.command === "begin" && thisEnv.command === "end") {
|
} else if (lastEnv.command === "begin" && thisEnv.command === "end") {
|
||||||
EnvErrorFromTo(lastEnv, thisEnv, "unexpected \\end{" + thisEnv.name + "} after \\begin{" + lastEnv.name + "}");
|
ErrorFromTo(lastEnv, thisEnv,
|
||||||
for (var j = i + 1; j < Environments.length; j++) {
|
"unclosed \\begin{" + lastEnv.name + "} found at \\end{" + thisEnv.name + "} " ,
|
||||||
|
{errorAtStart: true});
|
||||||
|
for (var j = i + 1; j < len; j++) {
|
||||||
var futureEnv = Environments[j];
|
var futureEnv = Environments[j];
|
||||||
if (futureEnv.command === "end" && futureEnv.name === lastEnv.name) {
|
if (futureEnv.command === "end" && futureEnv.name === lastEnv.name) {
|
||||||
state.push(lastEnv);
|
state.push(lastEnv);
|
||||||
|
@ -1796,12 +1892,143 @@ var Parse = function (text) {
|
||||||
while (state.length > 0) {
|
while (state.length > 0) {
|
||||||
thisEnv = state.pop();
|
thisEnv = state.pop();
|
||||||
if (thisEnv.command === "{") {
|
if (thisEnv.command === "{") {
|
||||||
EnvErrorFrom(thisEnv, "unclosed group {");
|
ErrorFrom(thisEnv, "unclosed group {", {type:"warning"});
|
||||||
} else if (thisEnv.command === "begin") {
|
} else if (thisEnv.command === "begin") {
|
||||||
EnvErrorFrom(thisEnv, "unclosed environment \\begin{" + thisEnv.name + "}");
|
ErrorFrom(thisEnv, "unclosed environment \\begin{" + thisEnv.name + "}");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return errors;
|
var vlen = verbatimRanges.length;
|
||||||
|
len = ErrorReporter.tokenErrors.length;
|
||||||
|
if (vlen >0 && len > 0) {
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
var tokenError = ErrorReporter.tokenErrors[i];
|
||||||
|
var startPos = tokenError.startPos;
|
||||||
|
var endPos = tokenError.endPos;
|
||||||
|
for (j = 0; j < vlen; j++) {
|
||||||
|
if (startPos > verbatimRanges[j].start && startPos < verbatimRanges[j].end) {
|
||||||
|
tokenError.ignore = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
var ErrorReporter = function (TokeniseResult) {
|
||||||
|
var text = TokeniseResult.text;
|
||||||
|
var linePosition = TokeniseResult.linePosition;
|
||||||
|
var lineNumber = TokeniseResult.lineNumber;
|
||||||
|
|
||||||
|
var errors = [], tokenErrors = [];
|
||||||
|
this.errors = errors;
|
||||||
|
this.tokenErrors = tokenErrors;
|
||||||
|
|
||||||
|
this.getErrors = function () {
|
||||||
|
var returnedErrors = [];
|
||||||
|
for (var i = 0, len = tokenErrors.length; i < len; i++) {
|
||||||
|
if (!tokenErrors[i].ignore) { returnedErrors.push(tokenErrors[i]); }
|
||||||
|
}
|
||||||
|
return returnedErrors.concat(errors);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.TokenError = function (token, message) {
|
||||||
|
var line = token[0], type = token[1], start = token[2], end = token[3];
|
||||||
|
var start_col = start - linePosition[line];
|
||||||
|
var end_col = end - linePosition[line];
|
||||||
|
tokenErrors.push({row: line,
|
||||||
|
column: start_col,
|
||||||
|
start_row:line,
|
||||||
|
start_col: start_col,
|
||||||
|
end_row:line,
|
||||||
|
end_col: end_col,
|
||||||
|
type:"error",
|
||||||
|
text:message,
|
||||||
|
startPos: start,
|
||||||
|
endPos: end,
|
||||||
|
suppressIfEditing:true});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.TokenErrorFromTo = function (fromToken, toToken, message) {
|
||||||
|
var fromLine = fromToken[0], fromStart = fromToken[2], fromEnd = fromToken[3];
|
||||||
|
var toLine = toToken[0], toStart = toToken[2], toEnd = toToken[3];
|
||||||
|
if (!toEnd) { toEnd = toStart + 1;};
|
||||||
|
var start_col = fromStart - linePosition[fromLine];
|
||||||
|
var end_col = toEnd - linePosition[toLine];
|
||||||
|
|
||||||
|
tokenErrors.push({row: fromLine,
|
||||||
|
column: start_col,
|
||||||
|
start_row: fromLine,
|
||||||
|
start_col: start_col,
|
||||||
|
end_row: toLine,
|
||||||
|
end_col: end_col,
|
||||||
|
type:"error",
|
||||||
|
text:message,
|
||||||
|
startPos: fromStart,
|
||||||
|
endPos: toEnd,
|
||||||
|
suppressIfEditing:true});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
this.EnvErrorFromTo = function (fromEnv, toEnv, message, options) {
|
||||||
|
if(!options) { options = {} ; };
|
||||||
|
var fromToken = fromEnv.token, toToken = toEnv.closeToken || toEnv.token;
|
||||||
|
var fromLine = fromToken[0], fromStart = fromToken[2], fromEnd = fromToken[3];
|
||||||
|
if (!toToken) {toToken = fromToken;};
|
||||||
|
var toLine = toToken[0], toStart = toToken[2], toEnd = toToken[3];
|
||||||
|
if (!toEnd) { toEnd = toStart + 1;};
|
||||||
|
var start_col = fromStart - linePosition[fromLine];
|
||||||
|
var end_col = toEnd - linePosition[toLine];
|
||||||
|
errors.push({row: options.errorAtStart ? fromLine : toLine,
|
||||||
|
column: options.errorAtStart ? start_col: end_col,
|
||||||
|
start_row:fromLine,
|
||||||
|
start_col: start_col,
|
||||||
|
end_row:toLine,
|
||||||
|
end_col: end_col,
|
||||||
|
type: options.type ? options.type : "error",
|
||||||
|
text:message,
|
||||||
|
suppressIfEditing:options.suppressIfEditing});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.EnvErrorTo = function (toEnv, message, options) {
|
||||||
|
if(!options) { options = {} ; };
|
||||||
|
var token = toEnv.closeToken || toEnv.token;
|
||||||
|
var line = token[0], type = token[1], start = token[2], end = token[3];
|
||||||
|
if (!end) { end = start + 1; };
|
||||||
|
var end_col = end - linePosition[line];
|
||||||
|
var err = {row: line,
|
||||||
|
column: end_col,
|
||||||
|
start_row:0,
|
||||||
|
start_col: 0,
|
||||||
|
end_row: line,
|
||||||
|
end_col: end_col,
|
||||||
|
type: options.type ? options.type : "error",
|
||||||
|
text:message};
|
||||||
|
errors.push(err);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.EnvErrorFrom = function (env, message, options) {
|
||||||
|
if(!options) { options = {} ; };
|
||||||
|
var token = env.token;
|
||||||
|
var line = token[0], type = token[1], start = token[2], end = token[3];
|
||||||
|
var start_col = start - linePosition[line];
|
||||||
|
var end_col = Infinity;
|
||||||
|
errors.push({row: line,
|
||||||
|
column: start_col,
|
||||||
|
start_row:line,
|
||||||
|
start_col: start_col,
|
||||||
|
end_row: lineNumber,
|
||||||
|
end_col: end_col,
|
||||||
|
type: options.type ? options.type : "error",
|
||||||
|
text:message});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var Parse = function (text) {
|
||||||
|
var TokeniseResult = Tokenise(text);
|
||||||
|
var Reporter = new ErrorReporter(TokeniseResult);
|
||||||
|
var Environments = InterpretTokens(TokeniseResult, Reporter);
|
||||||
|
CheckEnvironments(Environments, Reporter);
|
||||||
|
return Reporter.getErrors();
|
||||||
};
|
};
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
|
|
1
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/.gitattributes
vendored
Normal file
1
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/.gitattributes
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
* binary
|
Binary file not shown.
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/78-H.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/78-H.bcmap
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/78-V.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/78-V.bcmap
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/Add-H.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/Add-H.bcmap
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/Add-V.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/Add-V.bcmap
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/B5-H.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/B5-H.bcmap
Normal file
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/B5-V.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/B5-V.bcmap
Normal file
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/B5pc-H.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/B5pc-H.bcmap
Normal file
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/B5pc-V.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/B5pc-V.bcmap
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/CNS1-H.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/CNS1-H.bcmap
Normal file
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/CNS1-V.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/CNS1-V.bcmap
Normal file
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/CNS2-H.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/CNS2-H.bcmap
Normal file
Binary file not shown.
|
@ -0,0 +1,3 @@
|
||||||
|
àRCopyright 1990-2009 Adobe Systems Incorporated.
|
||||||
|
All rights reserved.
|
||||||
|
See ./LICENSEáCNS2-H
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,3 @@
|
||||||
|
àRCopyright 1990-2009 Adobe Systems Incorporated.
|
||||||
|
All rights reserved.
|
||||||
|
See ./LICENSEá ETen-B5-H` ^
|
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/EUC-H.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/EUC-H.bcmap
Normal file
Binary file not shown.
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/EUC-V.bcmap
Normal file
BIN
services/web/public/js/libs/pdfjs-1.6.210p1/bcmaps/EUC-V.bcmap
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue