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)}"
|
||||
].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)->
|
||||
Project
|
||||
.findOne(_id: project_id )
|
||||
|
|
|
@ -7,10 +7,18 @@ settings = require("settings-sharelatex")
|
|||
|
||||
templates = {}
|
||||
|
||||
|
||||
templates.registered =
|
||||
subject: _.template "Activate your #{settings.appName} Account"
|
||||
layout: PersonalEmailLayout
|
||||
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 """
|
||||
<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>
|
||||
"""
|
||||
|
||||
|
||||
templates.canceledSubscription =
|
||||
subject: _.template "ShareLaTeX thoughts"
|
||||
layout: PersonalEmailLayout
|
||||
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 '''
|
||||
<p>Hi <%= first_name %>,</p>
|
||||
|
||||
|
@ -36,10 +58,26 @@ ShareLaTeX Co-founder
|
|||
</p>
|
||||
'''
|
||||
|
||||
|
||||
templates.passwordResetRequested =
|
||||
subject: _.template "Password Reset - #{settings.appName}"
|
||||
layout: NotificationEmailLayout
|
||||
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 """
|
||||
<h2>Password Reset</h2>
|
||||
<p>
|
||||
|
@ -66,26 +104,6 @@ If you didn't request a password reset, let us know.
|
|||
<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 =
|
||||
subject: _.template "<%= project.name %> - shared by <%= owner.email %>"
|
||||
|
@ -113,10 +131,20 @@ Thank you
|
|||
<p> <a href="<%= siteUrl %>">#{settings.appName}</a></p>
|
||||
"""
|
||||
|
||||
|
||||
templates.completeJoinGroupAccount =
|
||||
subject: _.template "Verify Email to join <%= group_name %> group"
|
||||
layout: NotificationEmailLayout
|
||||
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 """
|
||||
<p>Hi, please verify your email to join the <%= group_name %> and get your free premium account</p>
|
||||
<center>
|
||||
|
@ -134,6 +162,7 @@ templates.completeJoinGroupAccount =
|
|||
<p> <a href="<%= siteUrl %>">#{settings.appName}</a></p>
|
||||
"""
|
||||
|
||||
|
||||
module.exports =
|
||||
templates: templates
|
||||
|
||||
|
|
|
@ -63,6 +63,7 @@ Reporter = (res) ->
|
|||
|
||||
res.contentType("application/json")
|
||||
if failures.length > 0
|
||||
logger.err failures:failures, "health check failed"
|
||||
res.send 500, JSON.stringify(results, null, 2)
|
||||
else
|
||||
res.send 200, JSON.stringify(results, null, 2)
|
||||
|
|
|
@ -8,6 +8,7 @@ Settings = require 'settings-sharelatex'
|
|||
logger = require('logger-sharelatex')
|
||||
GeoIpLookup = require("../../infrastructure/GeoIpLookup")
|
||||
SubscriptionDomainHandler = require("./SubscriptionDomainHandler")
|
||||
UserGetter = require "../User/UserGetter"
|
||||
|
||||
module.exports = SubscriptionController =
|
||||
|
||||
|
@ -21,14 +22,25 @@ module.exports = SubscriptionController =
|
|||
if req.query.v?
|
||||
viewName = "#{viewName}_#{req.query.v}"
|
||||
logger.log viewName:viewName, "showing plans page"
|
||||
currentUser = null
|
||||
GeoIpLookup.getCurrencyCode req.query?.ip || req.ip, (err, recomendedCurrency)->
|
||||
return next(err) if err?
|
||||
render = () ->
|
||||
res.render viewName,
|
||||
title: "plans_and_pricing"
|
||||
plans: plans
|
||||
baseUrl: baseUrl
|
||||
gaExperiments: Settings.gaExperiments.plansPage
|
||||
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
|
||||
paymentPage: (req, res, next) ->
|
||||
|
|
|
@ -68,4 +68,3 @@ module.exports =
|
|||
!plan.groupPlan and plan.annual and plan.planCode.indexOf("student") == -1
|
||||
|
||||
return result
|
||||
|
||||
|
|
|
@ -15,10 +15,24 @@ settings = require "settings-sharelatex"
|
|||
|
||||
module.exports = UserController =
|
||||
|
||||
deleteUser: (req, res)->
|
||||
tryDeleteUser: (req, res, next) ->
|
||||
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) ->
|
||||
if !err?
|
||||
if err?
|
||||
logger.err {user_id}, "error while deleting user account"
|
||||
return next(err)
|
||||
req.session?.destroy()
|
||||
res.sendStatus(200)
|
||||
|
||||
|
@ -143,7 +157,7 @@ module.exports = UserController =
|
|||
type:'success'
|
||||
text:'Your password has been changed'
|
||||
else
|
||||
logger.log user: user, "current password wrong"
|
||||
logger.log user_id: user_id, "current password wrong"
|
||||
res.send
|
||||
message:
|
||||
type:'error'
|
||||
|
|
|
@ -39,7 +39,7 @@ pathList = [
|
|||
["#{jsPath}ide.js"]
|
||||
["#{jsPath}main.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.worker.js"]
|
||||
["#{jsPath}libs/#{pdfjs}/compatibility.js"]
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
module.exports =
|
||||
user: (user) ->
|
||||
if !user?
|
||||
return null
|
||||
if !user._id?
|
||||
user = {_id : user}
|
||||
return {
|
||||
|
@ -10,6 +12,8 @@ module.exports =
|
|||
}
|
||||
|
||||
project: (project) ->
|
||||
if !project?
|
||||
return null
|
||||
if !project._id?
|
||||
project = {_id: project}
|
||||
return {
|
||||
|
|
|
@ -92,7 +92,7 @@ module.exports = class Router
|
|||
webRouter.post '/user/sessions/clear', AuthenticationController.requireLogin(), UserController.clearSessions
|
||||
|
||||
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
|
||||
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").
|
||||
window.csrfToken = "#{csrfToken}";
|
||||
|
||||
block scripts
|
||||
|
||||
script(src=buildJsPath("libs/jquery-1.11.1.min.js", {fingerprint:false}))
|
||||
script(type="text/javascript").
|
||||
var noCdnKey = "nocdn=true"
|
||||
|
@ -61,6 +59,9 @@ html(itemscope, itemtype='http://schema.org/Product')
|
|||
if (cdnBlocked && !noCdnAlreadyInUrl && navigator.userAgent.indexOf("Googlebot") == -1) {
|
||||
window.location.search += '&'+noCdnKey;
|
||||
}
|
||||
|
||||
block scripts
|
||||
|
||||
script(src=buildJsPath("libs/angular-1.3.15.min.js", {fingerprint:false}))
|
||||
|
||||
script.
|
||||
|
|
|
@ -118,6 +118,9 @@ block requirejs
|
|||
"ace/ext-searchbox": {
|
||||
"deps": ["ace/ace"]
|
||||
},
|
||||
"ace/ext-modelist": {
|
||||
"deps": ["ace/ace"]
|
||||
},
|
||||
"ace/ext-language_tools": {
|
||||
"deps": ["ace/ace"]
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ div.full-size(
|
|||
resize-on="layout:main:resize,layout:pdf:resize,layout:review:resize,reviewPanel:toggle",
|
||||
annotations="pdf.logEntryAnnotations[editor.open_doc_id]",
|
||||
read-only="!permissions.write",
|
||||
file-name="editor.open_doc_name",
|
||||
on-ctrl-enter="recompileViaKey",
|
||||
syntax-validation="settings.syntaxValidation",
|
||||
review-panel="reviewPanel",
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
div#history(ng-show="ui.view == 'history'")
|
||||
span(ng-controller="HistoryPremiumPopup")
|
||||
.upgrade-prompt(ng-show="!project.features.versioning")
|
||||
.message(ng-show="project.owner._id == user.id")
|
||||
.upgrade-prompt(ng-if="project.features.versioning === false && ui.view === 'history'")
|
||||
|
||||
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.small(ng-show="startedFreeTrial") #{translate("refresh_page_after_starting_free_trial")}
|
||||
ul.list-unstyled
|
||||
|
@ -28,14 +33,53 @@ div#history(ng-show="ui.view == 'history'")
|
|||
li
|
||||
i.fa.fa-check
|
||||
|#{translate("compile_larger_projects")}
|
||||
|
||||
p.text-center(ng-controller="FreeTrialModalController")
|
||||
a.btn.btn-success(
|
||||
href
|
||||
ng-class="buttonClass"
|
||||
ng-click="startFreeTrial('history')"
|
||||
sixpack-convert="teaser-history"
|
||||
) #{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")
|
||||
p #{translate("ask_proj_owner_to_upgrade_for_history")}
|
||||
|
|
|
@ -12,8 +12,8 @@ block scripts
|
|||
|
||||
mixin printPlan(plan)
|
||||
-if (!plan.hideFromUsers)
|
||||
tr(ng-controller="ChangePlanFormController")
|
||||
td(ng-init="plan=#{JSON.stringify(plan)}")
|
||||
tr(ng-controller="ChangePlanFormController", ng-init="plan=#{JSON.stringify(plan)}", ng-show="shouldShowPlan(plan.planCode)")
|
||||
td
|
||||
strong #{plan.name}
|
||||
td {{refreshPrice(plan.planCode)}}
|
||||
-if (plan.annual)
|
||||
|
@ -46,7 +46,7 @@ block content
|
|||
|
|
||||
| #{translate("your_billing_details_were_saved")}
|
||||
.card(ng-if="view == 'overview'")
|
||||
.page-header
|
||||
.page-header(x-current-plan="#{subscription.planCode}")
|
||||
h1 #{translate("your_subscription")}
|
||||
|
||||
- if (subscription && user._id+'' == subscription.admin_id+'')
|
||||
|
@ -56,6 +56,7 @@ block content
|
|||
|
||||
when "active"
|
||||
p !{translate("currently_subscribed_to_plan", {planName:"<strong>" + subscription.name + "</strong>"})}
|
||||
span(ng-show="!isNextGenPlan")
|
||||
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.pull-right
|
||||
|
|
|
@ -3,6 +3,7 @@ block scripts
|
|||
script(type='text/javascript').
|
||||
window.recomendedCurrency = '#{recomendedCurrency}'
|
||||
window.abCurrencyFlag = '#{abCurrencyFlag}'
|
||||
window.shouldABTestPlans = #{shouldABTestPlans || false}
|
||||
|
||||
script(type='text/javascript').
|
||||
(function() {var s=document.createElement('script'); s.type='text/javascript';s.async=true;
|
||||
|
@ -56,6 +57,7 @@ block content
|
|||
ng-click="changeCurreny(currency)"
|
||||
) {{currency}} ({{value['symbol']}})
|
||||
|
||||
div(ng-show="showPlans")
|
||||
.row(ng-cloak)
|
||||
.col-md-10.col-md-offset-1
|
||||
.row
|
||||
|
@ -89,7 +91,9 @@ block content
|
|||
span.small /yr
|
||||
ul.list-unstyled
|
||||
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("sync_to_dropbox")}
|
||||
li #{translate("sync_to_github")}
|
||||
|
@ -97,7 +101,7 @@ block content
|
|||
br
|
||||
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("buy_now")}
|
||||
|
@ -154,14 +158,17 @@ block content
|
|||
span.small /mo
|
||||
ul.list-unstyled
|
||||
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("sync_to_dropbox")}
|
||||
li #{translate("sync_to_github")}
|
||||
li
|
||||
br
|
||||
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")}
|
||||
|
||||
.col-md-4
|
||||
|
@ -174,16 +181,21 @@ block content
|
|||
span.small /yr
|
||||
ul.list-unstyled
|
||||
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("sync_to_dropbox")}
|
||||
li #{translate("sync_to_github")}
|
||||
li
|
||||
br
|
||||
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")}
|
||||
|
||||
|
||||
|
||||
.row.row-spaced(ng-cloak)
|
||||
p.text-centered #{translate("choose_plan_works_for_you", {len:'{{trial_len}}'})}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ extends ../layout
|
|||
|
||||
block content
|
||||
.content.content-alt
|
||||
.container
|
||||
.container(ng-controller="SuccessfulSubscriptionController")
|
||||
.row
|
||||
.col-md-8.col-md-offset-2
|
||||
.card(ng-cloak)
|
||||
|
|
|
@ -150,16 +150,32 @@ block content
|
|||
script(type='text/ng-template', id='deleteAccountModalTemplate')
|
||||
.modal-header
|
||||
h3 #{translate("delete_account")}
|
||||
.modal-body
|
||||
p !{translate("delete_account_warning_message_2")}
|
||||
div.modal-body#delete-account-modal
|
||||
p !{translate("delete_account_warning_message_3")}
|
||||
form(novalidate, name="deleteAccountForm")
|
||||
label #{translate('email')}
|
||||
input.form-control(
|
||||
type="text",
|
||||
autocomplete="off",
|
||||
placeholder="",
|
||||
ng-model="state.deleteText",
|
||||
focus-on="open",
|
||||
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
|
||||
button.btn.btn-default(
|
||||
ng-click="cancel()"
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
"lynx": "0.1.1",
|
||||
"marked": "^0.3.5",
|
||||
"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",
|
||||
"mocha": "1.17.1",
|
||||
"mongojs": "0.18.2",
|
||||
|
|
|
@ -69,6 +69,12 @@ define [
|
|||
reviewPanelOpen: false
|
||||
}
|
||||
$scope.user = window.user
|
||||
|
||||
|
||||
$scope.shouldABTestPlans = false
|
||||
if $scope.user.signUpDate >= '2016-10-27'
|
||||
$scope.shouldABTestPlans = true
|
||||
|
||||
$scope.settings = window.userSettings
|
||||
$scope.anonymous = window.anonymous
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ define [], () ->
|
|||
|
||||
@connected = false
|
||||
@userIsInactive = false
|
||||
@gracefullyReconnecting = false
|
||||
|
||||
@$scope.connection =
|
||||
reconnecting: false
|
||||
|
@ -54,6 +55,7 @@ define [], () ->
|
|||
@ide.socket.on "connect", () =>
|
||||
sl_console.log "[socket.io connect] Connected"
|
||||
@connected = true
|
||||
@gracefullyReconnecting = false
|
||||
@ide.pushEvent("connected")
|
||||
|
||||
@$scope.$apply () =>
|
||||
|
@ -81,7 +83,7 @@ define [], () ->
|
|||
@$scope.$apply () =>
|
||||
@$scope.connection.reconnecting = false
|
||||
|
||||
if !$scope.connection.forced_disconnect and !@userIsInactive
|
||||
if !$scope.connection.forced_disconnect and !@userIsInactive and !@gracefullyReconnecting
|
||||
@startAutoReconnectCountdown()
|
||||
|
||||
@ide.socket.on 'forceDisconnect', (message) =>
|
||||
|
@ -98,6 +100,10 @@ define [], () ->
|
|||
location.reload()
|
||||
, 10 * 1000
|
||||
|
||||
@ide.socket.on "reconnectGracefully", () =>
|
||||
sl_console.log "Reconnect gracefully"
|
||||
@reconnectGracefully()
|
||||
|
||||
joinProject: () ->
|
||||
sl_console.log "[joinProject] joining..."
|
||||
@ide.socket.emit 'joinProject', {
|
||||
|
@ -180,3 +186,24 @@ define [], () ->
|
|||
@$scope.$apply () =>
|
||||
@$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 = {
|
||||
sharejs_doc: null
|
||||
open_doc_id: null
|
||||
open_doc_name: null
|
||||
opening: true
|
||||
}
|
||||
|
||||
@$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)
|
||||
|
||||
@$scope.$on "entity:deleted", (event, entity) =>
|
||||
|
@ -59,6 +60,7 @@ define [
|
|||
return
|
||||
|
||||
@$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.fileTreeManager.selectEntity(doc)
|
||||
|
|
|
@ -2,19 +2,25 @@ define [
|
|||
"base"
|
||||
"ace/ace"
|
||||
"ace/ext-searchbox"
|
||||
"ace/ext-modelist"
|
||||
"ide/editor/directives/aceEditor/undo/UndoManager"
|
||||
"ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager"
|
||||
"ide/editor/directives/aceEditor/spell-check/SpellCheckManager"
|
||||
"ide/editor/directives/aceEditor/highlights/HighlightsManager"
|
||||
"ide/editor/directives/aceEditor/cursor-position/CursorPositionManager"
|
||||
"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
|
||||
ModeList = ace.require('ace/ext/modelist')
|
||||
|
||||
# set the path for ace workers if using a CDN (from editor.jade)
|
||||
if window.aceWorkerPath != ""
|
||||
syntaxValidationEnabled = true
|
||||
ace.config.set('workerPath', "#{window.aceWorkerPath}")
|
||||
else
|
||||
syntaxValidationEnabled = false
|
||||
|
||||
# By default, don't use workers - enable them per-session as required
|
||||
ace.config.setDefaultValue("session", "useWorker", false)
|
||||
|
||||
# Ace loads its script itself, so we need to hook in to be able to clear
|
||||
|
@ -43,6 +49,7 @@ define [
|
|||
readOnly: "="
|
||||
annotations: "="
|
||||
navigateHighlights: "="
|
||||
fileName: "="
|
||||
onCtrlEnter: "="
|
||||
syntaxValidation: "="
|
||||
reviewPanel: "="
|
||||
|
@ -62,6 +69,11 @@ define [
|
|||
|
||||
editor = ace.edit(element.find(".ace-editor-body")[0])
|
||||
editor.$blockScrolling = Infinity
|
||||
|
||||
# disable auto insertion of brackets and quotes
|
||||
editor.setOption('behavioursEnabled', false)
|
||||
editor.setOption('wrapBehavioursEnabled', false)
|
||||
|
||||
window.editors ||= []
|
||||
window.editors.push editor
|
||||
|
||||
|
@ -198,16 +210,15 @@ define [
|
|||
editor.setReadOnly !!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.setOption("useWorker", value);
|
||||
|
||||
editor.setOption("scrollPastEnd", true)
|
||||
|
||||
resetSession = () ->
|
||||
session = editor.getSession()
|
||||
session.setUseWrapMode(true)
|
||||
session.setMode("ace/mode/latex")
|
||||
|
||||
updateCount = 0
|
||||
onChange = () ->
|
||||
updateCount++
|
||||
|
@ -229,9 +240,36 @@ define [
|
|||
session = editor.getSession()
|
||||
if session?
|
||||
session.destroy()
|
||||
editor.setSession(new EditSession(lines, "ace/mode/latex"))
|
||||
resetSession()
|
||||
session = editor.getSession()
|
||||
|
||||
# see if we can lookup a suitable mode from ace
|
||||
# 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.on "change", onChange
|
||||
|
|
|
@ -233,8 +233,16 @@ define [
|
|||
start = aceDelta.start
|
||||
if !start?
|
||||
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, {
|
||||
aceDelta: JSON.stringify(aceDelta)
|
||||
aceDelta: JSONstringifyWithCycles(aceDelta)
|
||||
})
|
||||
throw error
|
||||
linesBefore = docLines.slice(0, start.row)
|
||||
|
|
|
@ -469,7 +469,7 @@ define [
|
|||
|
||||
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
|
||||
|
||||
App.factory "synctex", ["ide", "$http", "$q", (ide, $http, $q) ->
|
||||
|
|
|
@ -30,6 +30,10 @@ define [
|
|||
TEXTLAYER_TIMEOUT: 100
|
||||
|
||||
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
|
||||
window.PDFJS.disableFontFace = true
|
||||
else
|
||||
|
|
|
@ -17,7 +17,12 @@ define [
|
|||
# When we join the project:
|
||||
# index all references files
|
||||
# and don't broadcast to all clients
|
||||
@inited = false
|
||||
@$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)
|
||||
|
||||
setTimeout(
|
||||
|
|
|
@ -30,7 +30,7 @@ define [
|
|||
|
||||
$scope.$watch "settings.syntaxValidation", (syntaxValidation, oldSyntaxValidation) =>
|
||||
if syntaxValidation != oldSyntaxValidation
|
||||
settings.saveProjectSettings({syntaxValidation: syntaxValidation})
|
||||
settings.saveSettings({syntaxValidation: syntaxValidation})
|
||||
|
||||
$scope.$watch "project.spellCheckLanguage", (language, oldLanguage) =>
|
||||
return if @ignoreUpdates
|
||||
|
|
|
@ -32,7 +32,10 @@ define [
|
|||
$scope.state =
|
||||
isValid : false
|
||||
deleteText: ""
|
||||
password: ""
|
||||
inflight: false
|
||||
error: false
|
||||
invalidCredentials: false
|
||||
|
||||
$modalInstance.opened.then () ->
|
||||
$timeout () ->
|
||||
|
@ -40,20 +43,33 @@ define [
|
|||
, 700
|
||||
|
||||
$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.state.inflight = true
|
||||
|
||||
$scope.state.error = false
|
||||
$scope.state.invalidCredentials = false
|
||||
$http({
|
||||
method: "DELETE"
|
||||
url: "/user"
|
||||
method: "POST"
|
||||
url: "/user/delete"
|
||||
headers:
|
||||
"X-CSRF-Token": window.csrfToken
|
||||
"Content-Type": 'application/json'
|
||||
data:
|
||||
password: $scope.state.password
|
||||
})
|
||||
.success () ->
|
||||
$modalInstance.close()
|
||||
$scope.state.inflight = false
|
||||
$scope.state.error = false
|
||||
$scope.state.invalidCredentials = false
|
||||
window.location = "/"
|
||||
.error (data, status) ->
|
||||
$scope.state.inflight = false
|
||||
if status == 403
|
||||
$scope.state.invalidCredentials = true
|
||||
else
|
||||
$scope.state.error = true
|
||||
|
||||
$scope.cancel = () ->
|
||||
$modalInstance.dismiss('cancel')
|
||||
|
|
|
@ -6,14 +6,34 @@ define [
|
|||
$scope.buttonClass = "btn-primary"
|
||||
|
||||
$scope.startFreeTrial = (source, couponCode) ->
|
||||
event_tracking.sendMB "subscription-start-trial", { source }
|
||||
plan = 'collaborator_free_trial_7_days'
|
||||
|
||||
w = window.open()
|
||||
sixpack.convert "track-changes-discount", ->
|
||||
sixpack.participate 'in-editor-free-trial-plan', ['student', 'collaborator'], (planName, rawResponse)->
|
||||
go = () ->
|
||||
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?
|
||||
url = "#{url}&cc=#{couponCode}"
|
||||
$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
|
||||
|
||||
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.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 }
|
||||
|
||||
|
|
|
@ -10,6 +10,165 @@ define [
|
|||
|
||||
return {
|
||||
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:
|
||||
USD:
|
||||
symbol: "$"
|
||||
|
@ -141,30 +300,48 @@ define [
|
|||
professional:
|
||||
monthly: "$40"
|
||||
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.currencyCode = MultiCurrencyPricing.currencyCode
|
||||
|
||||
$scope.trial_len = 7
|
||||
|
||||
$scope.planQueryString = '_free_trial_7_days'
|
||||
|
||||
$scope.ui =
|
||||
view: "monthly"
|
||||
|
||||
|
||||
$scope.changeCurreny = (newCurrency)->
|
||||
$scope.currencyCode = newCurrency
|
||||
|
||||
$scope.signUpNowClicked = (plan, annual)->
|
||||
event_tracking.sendMB 'plans-page-start-trial', {plan}
|
||||
if $scope.ui.view == "annual"
|
||||
plan = "#{plan}_annual"
|
||||
|
||||
event_tracking.send 'subscription-funnel', 'sign_up_now_button', plan
|
||||
|
||||
$scope.switchToMonthly = ->
|
||||
|
|
|
@ -1,15 +1,21 @@
|
|||
define [
|
||||
"base"
|
||||
], (App)->
|
||||
|
||||
App.controller 'SuccessfulSubscriptionController', ($scope, sixpack) ->
|
||||
sixpack.convert 'plans-1610', () ->
|
||||
|
||||
|
||||
SUBSCRIPTION_URL = "/user/subscription/update"
|
||||
|
||||
setupReturly = _.once ->
|
||||
recurly?.configure window.recurlyApiKey
|
||||
PRICES = {}
|
||||
|
||||
|
||||
App.controller "CurrenyDropdownController", ($scope, MultiCurrencyPricing, $q)->
|
||||
|
||||
$scope.plans = MultiCurrencyPricing.plans
|
||||
# $scope.plans = MultiCurrencyPricing.plans
|
||||
$scope.currencyCode = MultiCurrencyPricing.currencyCode
|
||||
|
||||
$scope.changeCurrency = (newCurrency)->
|
||||
|
@ -31,7 +37,7 @@ define [
|
|||
$scope.currencyCode = MultiCurrencyPricing.currencyCode
|
||||
|
||||
$scope.pricing = MultiCurrencyPricing
|
||||
$scope.plans = MultiCurrencyPricing.plans
|
||||
# $scope.plans = MultiCurrencyPricing.plans
|
||||
$scope.currencySymbol = MultiCurrencyPricing.plans[MultiCurrencyPricing.currencyCode].symbol
|
||||
|
||||
$scope.currencyCode = MultiCurrencyPricing.currencyCode
|
||||
|
@ -87,6 +93,8 @@ define [
|
|||
|
||||
|
||||
App.controller "UserSubscriptionController", ($scope, MultiCurrencyPricing, $http, sixpack, $modal) ->
|
||||
$scope.plans = MultiCurrencyPricing.plans
|
||||
|
||||
freeTrialEndDate = new Date(subscription?.trial_ends_at)
|
||||
|
||||
sevenDaysTime = new Date()
|
||||
|
@ -96,6 +104,16 @@ define [
|
|||
freeTrialExpiresUnderSevenDays = freeTrialEndDate < sevenDaysTime
|
||||
|
||||
$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
|
||||
stillInFreeTrial = freeTrialInFuture and freeTrialExpiresUnderSevenDays
|
||||
|
||||
|
@ -166,6 +184,3 @@ define [
|
|||
location.reload()
|
||||
.error ->
|
||||
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 WorkerClient = require("ace/worker/worker_client").WorkerClient;
|
||||
|
||||
|
||||
var Mode = function() {
|
||||
this.HighlightRules = LatexHighlightRules;
|
||||
this.foldingRules = new LatexFoldMode();
|
||||
this.createWorker = function(session) {
|
||||
var createLatexWorker = function (session) {
|
||||
var doc = session.getDocument();
|
||||
var selection = session.getSelection();
|
||||
var cursorAnchor = selection.lead;
|
||||
|
||||
var savedRange = {};
|
||||
var suppressions = [];
|
||||
var hints = [];
|
||||
var changeHandler = null;
|
||||
var docChangePending = false;
|
||||
var firstPass = true;
|
||||
|
||||
var worker = new WorkerClient(["ace"], "ace/mode/latex_worker", "LatexWorker");
|
||||
worker.attachToDocument(doc);
|
||||
|
||||
doc.on("change", function () {
|
||||
var docChangeHandler = doc.on("change", function () {
|
||||
docChangePending = true;
|
||||
if(changeHandler) {
|
||||
clearTimeout(changeHandler);
|
||||
changeHandler = null;
|
||||
}
|
||||
});
|
||||
|
||||
selection.on("changeCursor", function () {
|
||||
if(suppressions.length > 0) {
|
||||
var cursorHandler = selection.on("changeCursor", function () {
|
||||
if (docChangePending) { return; } ;
|
||||
changeHandler = setTimeout(function () {
|
||||
updateMarkers();
|
||||
updateMarkers({cursorMoveOnly:true});
|
||||
suppressions = [];
|
||||
changeHandler = null;
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
|
||||
var updateMarkers = function () {
|
||||
var updateMarkers = function (options) {
|
||||
if (!options) { options = {};};
|
||||
var cursorMoveOnly = options.cursorMoveOnly;
|
||||
var annotations = [];
|
||||
var newRange = {};
|
||||
var cursor = selection.getCursor();
|
||||
suppressions = [];
|
||||
|
||||
for (var i = 0; i<hints.length; i++) {
|
||||
var data = hints[i];
|
||||
var start_row = data.start_row;
|
||||
var start_col = data.start_col;
|
||||
var end_row = data.end_row;
|
||||
var end_col = data.end_col;
|
||||
if (data.suppressIfEditing &&
|
||||
((cursor.row === start_row && cursor.column == start_col+1)
|
||||
|| (cursor.row === end_row && (cursor.column+1) == end_col))) {
|
||||
suppressions.push([start_row, start_col, end_row, end_col]);
|
||||
for (var i = 0, len = hints.length; i<len; i++) {
|
||||
var hint = hints[i];
|
||||
|
||||
var suppressedChanges = 0;
|
||||
var hintRange = new Range(hint.start_row, hint.start_col, hint.end_row, hint.end_col);
|
||||
|
||||
var cursorInRange = hintRange.insideStart(cursor.row, cursor.column);
|
||||
var cursorAtStart = hintRange.isStart(cursor.row, cursor.column);
|
||||
var cursorAtEnd = hintRange.isEnd(cursor.row, cursor.column);
|
||||
if (hint.suppressIfEditing && (cursorAtStart || cursorAtEnd)) {
|
||||
suppressions.push(hintRange);
|
||||
if (!hint.suppressed) { suppressedChanges++; };
|
||||
hint.suppressed = true;
|
||||
continue;
|
||||
}
|
||||
var suppress = false;
|
||||
for (var j = 0; j < suppressions.length; j++) {
|
||||
var e=suppressions[j];
|
||||
var fromRow=e[0], fromCol=e[1], toRow=e[2], toCol=e[3];
|
||||
if (start_row == fromRow && start_col >= fromCol && start_row === toRow && start_col <= toCol) {
|
||||
suppress = true;
|
||||
var isCascadeError = false;
|
||||
for (var j = 0, suplen = suppressions.length; j < suplen; j++) {
|
||||
var badRange = suppressions[j];
|
||||
if (badRange.intersects(hintRange)) {
|
||||
isCascadeError = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(suppress) { continue; };
|
||||
if(isCascadeError) {
|
||||
if (!hint.suppressed) { suppressedChanges++; };
|
||||
hint.suppressed = true;
|
||||
continue;
|
||||
};
|
||||
|
||||
var key = "(" + start_row + "," + start_col + ")" + ":" + "(" + end_row + "," + end_col + ")";
|
||||
newRange[key] = data;
|
||||
annotations.push(data);
|
||||
if (hint.suppressed) { suppressedChanges++; };
|
||||
hint.suppressed = false;
|
||||
|
||||
annotations.push(hint);
|
||||
if (hint.type === "info") {
|
||||
continue;
|
||||
};
|
||||
var key = hintRange.toString() + (cursorInRange ? "+cursor" : "");
|
||||
newRange[key] = {hint: hint, cursorInRange: cursorInRange, range: hintRange};
|
||||
}
|
||||
|
||||
var newKeys = Object.keys(newRange);
|
||||
var oldKeys = Object.keys(savedRange);
|
||||
var changes = 0;
|
||||
for (i = 0; i < newKeys.length; i++) {
|
||||
key = newKeys[i];
|
||||
if (!savedRange[key]) {
|
||||
var new_range = newRange[key];
|
||||
var a = doc.createAnchor(new_range.start_row, new_range.start_col);
|
||||
var b = doc.createAnchor(new_range.end_row, new_range.end_col);
|
||||
for (key in newRange) {
|
||||
if (!savedRange[key]) { // doesn't exist in already displayed errors
|
||||
var new_range = newRange[key].range;
|
||||
cursorInRange = newRange[key].cursorInRange;
|
||||
hint = newRange[key].hint;
|
||||
var errorAtStart = (hint.row === hint.start_row && hint.column === hint.start_col);
|
||||
var a = (cursorInRange && !errorAtStart) ? cursorAnchor : doc.createAnchor(new_range.start);
|
||||
var b = (cursorInRange && errorAtStart) ? cursorAnchor : doc.createAnchor(new_range.end);
|
||||
var range = new Range();
|
||||
range.start = a;
|
||||
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;
|
||||
changes++;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < oldKeys.length; i++) {
|
||||
key = oldKeys[i];
|
||||
if (!newRange[key]) {
|
||||
for (key in savedRange) {
|
||||
if (!newRange[key]) { // no longer present in list of errors to display
|
||||
range = savedRange[key];
|
||||
range.start.detach();
|
||||
range.end.detach();
|
||||
if (range.start !== cursorAnchor) { range.start.detach(); }
|
||||
if (range.end !== cursorAnchor) { range.end.detach(); }
|
||||
session.removeMarker(range.id);
|
||||
delete savedRange[key];
|
||||
changes++;
|
||||
}
|
||||
}
|
||||
|
||||
if (changes>0) {
|
||||
if (!cursorMoveOnly || suppressedChanges) {
|
||||
if (firstPass) {
|
||||
if (annotations.length > 0) {
|
||||
var originalAnnotations = session.getAnnotations();
|
||||
session.setAnnotations(originalAnnotations.concat(annotations));
|
||||
};
|
||||
firstPass = false;
|
||||
} else {
|
||||
session.setAnnotations(annotations);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
worker.on("lint", function(results) {
|
||||
if(docChangePending) { docChangePending = false; };
|
||||
hints = results.data;
|
||||
if (hints.length > 100) {
|
||||
hints = hints.slice(0, 100); // limit to 100 errors
|
||||
};
|
||||
updateMarkers();
|
||||
});
|
||||
|
||||
worker.on("terminate", function() {
|
||||
var oldKeys = Object.keys(savedRange);
|
||||
for (var i = 0; i < oldKeys.length; i++) {
|
||||
var key = oldKeys[i];
|
||||
var range = savedRange[key];
|
||||
session.removeMarker(range.id);
|
||||
delete savedRange[key];
|
||||
if(changeHandler) {
|
||||
clearTimeout(changeHandler);
|
||||
changeHandler = null;
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
var Mode = function() {
|
||||
this.HighlightRules = LatexHighlightRules;
|
||||
this.foldingRules = new LatexFoldMode();
|
||||
this.createWorker = createLatexWorker;
|
||||
};
|
||||
oop.inherits(Mode, TextMode);
|
||||
|
||||
|
|
|
@ -1419,107 +1419,28 @@ var LatexWorker = exports.LatexWorker = function(sender) {
|
|||
|
||||
oop.inherits(LatexWorker, Mirror);
|
||||
|
||||
var Parse = function (text) {
|
||||
var errors = [];
|
||||
var Comments = [];
|
||||
var Tokenise = function (text) {
|
||||
var Tokens = [];
|
||||
var Environments = [];
|
||||
var Comments = [];
|
||||
var pos = -1;
|
||||
var SPECIAL = /[\\\{\}\$\&\#\^\_\~\%]/g;
|
||||
var CS = /[^a-zA-Z]/g;
|
||||
var SPECIAL = /[\\\{\}\$\&\#\^\_\~\%]/g; // match TeX special characters
|
||||
var NEXTCS = /[^a-zA-Z]/g; // match characters which aren't part of a TeX control sequence
|
||||
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;
|
||||
|
||||
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 count = 0; // number of tokens parses
|
||||
var MAX_TOKENS = 100000;
|
||||
|
||||
while (true) {
|
||||
count++;
|
||||
if (count > MAX_TOKENS) {
|
||||
throw new Error("exceed max token count of " + MAX_TOKENS);
|
||||
break;
|
||||
};
|
||||
var result = SPECIAL.exec(text);
|
||||
if (result == null) {
|
||||
if (idx < text.length) {
|
||||
|
@ -1528,10 +1449,10 @@ var Parse = function (text) {
|
|||
break;
|
||||
}
|
||||
if (result && result.index <= pos) {
|
||||
throw new Error("infinite loop in parsing");
|
||||
break;
|
||||
};
|
||||
pos = result.index;
|
||||
var newIdx = SPECIAL.lastIndex;
|
||||
if (pos > idx) {
|
||||
Tokens.push([lineNumber, "Text", idx, pos]);
|
||||
}
|
||||
|
@ -1541,9 +1462,11 @@ var Parse = function (text) {
|
|||
linePosition[lineNumber] = i+1;
|
||||
}
|
||||
}
|
||||
|
||||
var newIdx = SPECIAL.lastIndex;
|
||||
idx = newIdx;
|
||||
var code = result[0];
|
||||
if (code === "%") {
|
||||
if (code === "%") { // comment character
|
||||
var newLinePos = text.indexOf("\n", idx);
|
||||
if (newLinePos === -1) {
|
||||
newLinePos = text.length;
|
||||
|
@ -1562,9 +1485,9 @@ var Parse = function (text) {
|
|||
linePosition[lineNumber] = idx;
|
||||
} else if (checkingDisabled) {
|
||||
continue;
|
||||
} else if (code === '\\') {
|
||||
CS.lastIndex = idx;
|
||||
var controlSequence = CS.exec(text);
|
||||
} else if (code === '\\') { // escape character
|
||||
NEXTCS.lastIndex = idx;
|
||||
var controlSequence = NEXTCS.exec(text);
|
||||
var nextSpecialPos = controlSequence === null ? idx : controlSequence.index;
|
||||
if (nextSpecialPos === idx) {
|
||||
Tokens.push([lineNumber, code, pos, idx + 1, text[idx]]);
|
||||
|
@ -1580,50 +1503,67 @@ var Parse = function (text) {
|
|||
}
|
||||
idx = SPECIAL.lastIndex = nextSpecialPos;
|
||||
}
|
||||
} else if (code === "{") {
|
||||
} else if (code === "{") { // open group
|
||||
Tokens.push([lineNumber, code, pos]);
|
||||
} else if (code === "}") {
|
||||
} else if (code === "}") { // close group
|
||||
Tokens.push([lineNumber, code, pos]);
|
||||
} else if (code === "$") {
|
||||
} else if (code === "$") { // math mode
|
||||
if (text[idx] === "$") {
|
||||
idx = SPECIAL.lastIndex = idx + 1;
|
||||
Tokens.push([lineNumber, "$$", pos]);
|
||||
} else {
|
||||
Tokens.push([lineNumber, code, pos]);
|
||||
}
|
||||
} else if (code === "&") {
|
||||
} else if (code === "&") { // tabalign
|
||||
Tokens.push([lineNumber, code, pos]);
|
||||
} else if (code === "#") {
|
||||
} else if (code === "#") { // macro parameter
|
||||
Tokens.push([lineNumber, code, pos]);
|
||||
} else if (code === "^") {
|
||||
} else if (code === "^") { // superscript
|
||||
Tokens.push([lineNumber, code, pos]);
|
||||
} else if (code === "_") {
|
||||
} else if (code === "_") { // subscript
|
||||
Tokens.push([lineNumber, code, pos]);
|
||||
} else if (code === "~") {
|
||||
} else if (code === "~") { // active character (space)
|
||||
Tokens.push([lineNumber, code, pos]);
|
||||
} else {
|
||||
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 env = Tokens[k+2];
|
||||
var close = Tokens[k+3];
|
||||
var envName;
|
||||
|
||||
if(open && open[1] === "\\") {
|
||||
envName = open[4];
|
||||
envName = open[4]; // array element 4 is command sequence
|
||||
return k + 1;
|
||||
} else if(open && open[1] === "{" && env && env[1] === "\\" && close && close[1] === "}") {
|
||||
envName = env[4];
|
||||
return k + 3;
|
||||
envName = env[4]; // NOTE: if we were actually using this, keep track of * above
|
||||
return k + 3; // array element 4 is command sequence
|
||||
} else {
|
||||
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 env = Tokens[k+2];
|
||||
var close = Tokens[k+3];
|
||||
|
@ -1631,35 +1571,57 @@ var Parse = function (text) {
|
|||
if(open && open[1] === "{" && env && env[1] === "Text" && close && close[1] === "}") {
|
||||
var envName = text.substring(env[2], env[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 {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
var readOptionalParams = function(TokeniseResult, k) {
|
||||
var Tokens = TokeniseResult.tokens;
|
||||
var text = TokeniseResult.text;
|
||||
|
||||
|
||||
var readOptionalParams = function(k) {
|
||||
var params = Tokens[k+1];
|
||||
|
||||
if(params && params[1] === "Text") {
|
||||
var paramNum = text.substring(params[2], params[3]);
|
||||
if (paramNum.match(/^\[\d+\]$/)) {
|
||||
return k + 1;
|
||||
if (paramNum.match(/^\[\d+\](\[[^\]]*\])*\s*$/)) {
|
||||
return k + 1; // got it
|
||||
};
|
||||
};
|
||||
return null;
|
||||
};
|
||||
|
||||
var readDefinition = function(k) {
|
||||
var readDefinition = function(TokeniseResult, k) {
|
||||
var Tokens = TokeniseResult.tokens;
|
||||
var text = TokeniseResult.text;
|
||||
|
||||
k = k + 1;
|
||||
var count = 0;
|
||||
var nextToken = Tokens[k];
|
||||
while (nextToken && nextToken[1] === "Text") {
|
||||
var start = nextToken[2], end = nextToken[3];
|
||||
for (i = start; i < end; i++) {
|
||||
for (var i = start; i < end; i++) {
|
||||
var char = text[i];
|
||||
if (char === ' ' || char === '\t' || char === '\r' || char === '\n') { continue; }
|
||||
return null;
|
||||
return null; // bail out, should begin with a {
|
||||
}
|
||||
k++;
|
||||
nextToken = Tokens[k];
|
||||
|
@ -1675,34 +1637,120 @@ var Parse = function (text) {
|
|||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
for (var _j = 0, _len = Tokens.length; _j < _len; _j++) {
|
||||
var token = Tokens[_j];
|
||||
var readVerb = function(TokeniseResult, k) {
|
||||
|
||||
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];
|
||||
if (type === "\\") {
|
||||
if (seq === "begin" || seq === "end") {
|
||||
var open = Tokens[_j+1];
|
||||
var env = Tokens[_j+2];
|
||||
var close = Tokens[_j+3];
|
||||
var open = Tokens[i+1];
|
||||
var env = Tokens[i+2];
|
||||
var close = Tokens[i+3];
|
||||
if(open && open[1] === "{" && env && env[1] === "Text" && close && close[1] === "}") {
|
||||
var envName = text.substring(env[2], env[3]);
|
||||
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 {
|
||||
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;
|
||||
if (open && open[1] === "{") {
|
||||
endToken = open;
|
||||
|
||||
endToken = open; // we've got a {
|
||||
if (env && env[1] === "Text") {
|
||||
endToken = env.slice();
|
||||
endToken = env.slice(); // we've got some text following the {
|
||||
start = endToken[2]; end = endToken[3];
|
||||
for (i = start; i < end; i++) {
|
||||
char = text[i];
|
||||
for (j = start; j < end; j++) {
|
||||
var char = text[j];
|
||||
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") {
|
||||
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);
|
||||
if (newPos === null) { continue; } else {_j = newPos;};
|
||||
} else if (seq === "newcolumntype") {
|
||||
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);
|
||||
if (newPos === null) { /* do nothing */ } else {_j = newPos;};
|
||||
|
||||
newPos = readDefinition(_j);
|
||||
if (newPos === null) { /* do nothing */ } else {_j = newPos;};
|
||||
|
||||
} else if (seq === "newenvironment") {
|
||||
|
||||
newPos = read1name(_j);
|
||||
if (newPos === null) { continue; } else {_j = newPos;};
|
||||
|
||||
newPos = readOptionalParams(_j);
|
||||
if (newPos === null) { /* do nothing */ } else {_j = newPos;};
|
||||
|
||||
newPos = readDefinition(_j);
|
||||
if (newPos === null) { /* do nothing */ } else {_j = newPos;};
|
||||
|
||||
newPos = readDefinition(_j);
|
||||
if (newPos === null) { /* do nothing */ } else {_j = newPos;};
|
||||
} else if (seq === "newenvironment" || seq === "renewenvironment") {
|
||||
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 = readDefinition(TokeniseResult, i);
|
||||
if (newPos === null) { /* do nothing */ } else {i = newPos;};
|
||||
} else if (seq === "verb") {
|
||||
newPos = readVerb(TokeniseResult, i);
|
||||
if (newPos === null) { TokenError(token, "invalid verbatim command"); } else {i = newPos;};
|
||||
} else if (seq === "url") {
|
||||
newPos = readUrl(TokeniseResult, i);
|
||||
if (newPos === null) { TokenError(token, "invalid url command"); } else {i = newPos;};
|
||||
}
|
||||
} else if (type === "{") {
|
||||
Environments.push({command:"{", token:token});
|
||||
} else if (type === "}") {
|
||||
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 = [];
|
||||
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];
|
||||
if(thisEnv.command === "begin" || thisEnv.command === "{") {
|
||||
if (inVerbatim) { continue; } // ignore anything in verbatim environments
|
||||
if (thisEnv.verbatim) {inVerbatim = true;};
|
||||
state.push(thisEnv);
|
||||
} else if (thisEnv.command === "end" || thisEnv.command === "}") {
|
||||
var lastEnv = state.pop();
|
||||
if (!lastEnv) {
|
||||
if (thisEnv.command === "}") {
|
||||
EnvErrorTo(thisEnv, "unexpected end group }");
|
||||
} else if (thisEnv.command === "end") {
|
||||
EnvErrorTo(thisEnv, "unexpected \\end{" + thisEnv.name + "}");
|
||||
|
||||
if (inVerbatim) {
|
||||
if (lastEnv && lastEnv.name === thisEnv.name) {
|
||||
inVerbatim = false;
|
||||
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) {
|
||||
continue; // closed environment correctly
|
||||
} else if (thisEnv.command === "}") {
|
||||
EnvErrorFromTo(lastEnv, thisEnv, "unexpected end group } after \\begin{" + lastEnv.name +"}");
|
||||
};
|
||||
|
||||
if (lastEnv && lastEnv.command === "{" && thisEnv.command === "}") {
|
||||
continue;
|
||||
} else if (lastEnv && lastEnv.name === thisEnv.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);
|
||||
} 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--;
|
||||
} else if (lastEnv.command === "begin" && thisEnv.command === "end") {
|
||||
EnvErrorFromTo(lastEnv, thisEnv, "unexpected \\end{" + thisEnv.name + "} after \\begin{" + lastEnv.name + "}");
|
||||
for (var j = i + 1; j < Environments.length; j++) {
|
||||
ErrorFromTo(lastEnv, thisEnv,
|
||||
"unclosed \\begin{" + lastEnv.name + "} found at \\end{" + thisEnv.name + "} " ,
|
||||
{errorAtStart: true});
|
||||
for (var j = i + 1; j < len; j++) {
|
||||
var futureEnv = Environments[j];
|
||||
if (futureEnv.command === "end" && futureEnv.name === lastEnv.name) {
|
||||
state.push(lastEnv);
|
||||
|
@ -1796,12 +1892,143 @@ var Parse = function (text) {
|
|||
while (state.length > 0) {
|
||||
thisEnv = state.pop();
|
||||
if (thisEnv.command === "{") {
|
||||
EnvErrorFrom(thisEnv, "unclosed group {");
|
||||
ErrorFrom(thisEnv, "unclosed group {", {type:"warning"});
|
||||
} 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() {
|
||||
|
|
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