mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge branch 'master' into ns-autocomplete
This commit is contained in:
commit
fb4182cbb1
54 changed files with 1942 additions and 5089 deletions
1
services/web/.gitignore
vendored
1
services/web/.gitignore
vendored
|
@ -60,6 +60,7 @@ public/js/services/
|
|||
public/js/utils/
|
||||
|
||||
public/stylesheets/style.css
|
||||
public/stylesheets/ol-style.css
|
||||
public/brand/plans.css
|
||||
public/minjs/
|
||||
|
||||
|
|
|
@ -139,6 +139,9 @@ module.exports = (grunt) ->
|
|||
app:
|
||||
files:
|
||||
"public/stylesheets/style.css": "public/stylesheets/style.less"
|
||||
ol:
|
||||
files:
|
||||
"public/stylesheets/ol-style.css": "public/stylesheets/ol-style.less"
|
||||
|
||||
postcss:
|
||||
options:
|
||||
|
|
116
services/web/Jenkinsfile
vendored
Normal file
116
services/web/Jenkinsfile
vendored
Normal file
|
@ -0,0 +1,116 @@
|
|||
pipeline {
|
||||
|
||||
agent {
|
||||
docker {
|
||||
image 'node:6.9.5'
|
||||
args "-v /var/lib/jenkins/.npm:/tmp/.npm"
|
||||
}
|
||||
}
|
||||
|
||||
environment {
|
||||
HOME = "/tmp"
|
||||
}
|
||||
|
||||
triggers {
|
||||
pollSCM('* * * * *')
|
||||
cron('@daily')
|
||||
}
|
||||
|
||||
stages {
|
||||
stage('Set up') {
|
||||
steps {
|
||||
// we need to disable logallrefupdates, else git clones during the npm install will require git to lookup the user id
|
||||
// which does not exist in the container's /etc/passwd file, causing the clone to fail.
|
||||
sh 'git config --global core.logallrefupdates false'
|
||||
|
||||
sh 'rm -rf node_modules/*'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Clone Dependencies') {
|
||||
steps {
|
||||
sh 'rm -rf public/brand modules'
|
||||
checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'public/brand'], [$class: 'CloneOption', shallow: true]], userRemoteConfigs: [[credentialsId: 'GIT_DEPLOY_KEY', url: 'git@github.com:sharelatex/brand-sharelatex']]])
|
||||
checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'app/views/external'], [$class: 'CloneOption', shallow: true]], userRemoteConfigs: [[credentialsId: 'GIT_DEPLOY_KEY', url: 'git@github.com:sharelatex/external-pages-sharelatex']]])
|
||||
checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'modules'], [$class: 'CloneOption', shallow: true]], userRemoteConfigs: [[credentialsId: 'GIT_DEPLOY_KEY', url: 'git@github.com:sharelatex/web-sharelatex-modules']]])
|
||||
checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'modules/admin-panel'], [$class: 'CloneOption', shallow: true]], userRemoteConfigs: [[credentialsId: 'GIT_DEPLOY_KEY', url: 'git@github.com:sharelatex/admin-panel']]])
|
||||
checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'modules/groovehq'], [$class: 'CloneOption', shallow: true]], userRemoteConfigs: [[credentialsId: 'GIT_DEPLOY_KEY', url: 'git@bitbucket.org:sharelatex/groovehq']]])
|
||||
checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'modules/references-search'], [$class: 'CloneOption', shallow: true]], userRemoteConfigs: [[credentialsId: 'GIT_DEPLOY_KEY', url: 'git@bitbucket.org:sharelatex/references-search.git']]])
|
||||
checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'modules/tpr-webmodule'], [$class: 'CloneOption', shallow: true]], userRemoteConfigs: [[credentialsId: 'GIT_DEPLOY_KEY', url: 'git@github.com:sharelatex/tpr-webmodule.git ']]])
|
||||
checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'modules/learn-wiki'], [$class: 'CloneOption', shallow: true]], userRemoteConfigs: [[credentialsId: 'GIT_DEPLOY_KEY', url: 'git@bitbucket.org:sharelatex/learn-wiki-web-module.git']]])
|
||||
checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'modules/templates'], [$class: 'CloneOption', shallow: true]], userRemoteConfigs: [[credentialsId: 'GIT_DEPLOY_KEY', url: 'git@github.com:sharelatex/templates-webmodule.git']]])
|
||||
checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'modules/track-changes'], [$class: 'CloneOption', shallow: true]], userRemoteConfigs: [[credentialsId: 'GIT_DEPLOY_KEY', url: 'git@github.com:sharelatex/track-changes-web-module.git']]])
|
||||
}
|
||||
}
|
||||
|
||||
stage('Install') {
|
||||
steps {
|
||||
sh 'mv app/views/external/robots.txt public/robots.txt'
|
||||
sh 'mv app/views/external/googlebdb0f8f7f4a17241.html public/googlebdb0f8f7f4a17241.html'
|
||||
sh 'npm install'
|
||||
sh 'npm rebuild'
|
||||
sh 'npm install --quiet grunt'
|
||||
sh 'npm install --quiet grunt-cli'
|
||||
sh 'ls -l node_modules/.bin'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Compile') {
|
||||
steps {
|
||||
sh 'node_modules/.bin/grunt compile --verbose'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Smoke Test') {
|
||||
steps {
|
||||
sh 'node_modules/.bin/grunt compile:smoke_tests'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Minify') {
|
||||
steps {
|
||||
sh 'node_modules/.bin/grunt compile:minify'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Unit Test') {
|
||||
steps {
|
||||
sh 'env NODE_ENV=development ./node_modules/.bin/grunt test:unit --reporter=tap'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Package') {
|
||||
steps {
|
||||
sh 'rm -rf ./node_modules/grunt*'
|
||||
sh 'touch build.tar.gz' // Avoid tar warning about files changing during read
|
||||
sh 'tar -czf build.tar.gz --exclude=build.tar.gz --exclude-vcs .'
|
||||
}
|
||||
}
|
||||
stage('Publish') {
|
||||
steps {
|
||||
withAWS(credentials:'S3_CI_BUILDS_AWS_KEYS', region:"${S3_REGION_BUILD_ARTEFACTS}") {
|
||||
s3Upload(file:'build.tar.gz', bucket:"${S3_BUCKET_BUILD_ARTEFACTS}", path:"${JOB_NAME}/${BUILD_NUMBER}.tar.gz")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post {
|
||||
failure {
|
||||
mail(from: "${EMAIL_ALERT_FROM}",
|
||||
to: "${EMAIL_ALERT_TO}",
|
||||
subject: "Jenkins build failed: ${JOB_NAME}:${BUILD_NUMBER}",
|
||||
body: "Build: ${BUILD_URL}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The options directive is for configuration that applies to the whole job.
|
||||
options {
|
||||
// we'd like to make sure remove old builds, so we don't fill up our storage!
|
||||
buildDiscarder(logRotator(numToKeepStr:'50'))
|
||||
|
||||
// And we'd really like to be sure that this build doesn't hang forever, so let's time it out after:
|
||||
timeout(time: 30, unit: 'MINUTES')
|
||||
}
|
||||
}
|
|
@ -29,8 +29,12 @@ module.exports = AuthenticationManager =
|
|||
callback null, null
|
||||
|
||||
setUserPassword: (user_id, password, callback = (error) ->) ->
|
||||
if Settings.passwordStrengthOptions?.length?.max? and Settings.passwordStrengthOptions?.length?.max < password.length
|
||||
if (Settings.passwordStrengthOptions?.length?.max? and
|
||||
Settings.passwordStrengthOptions?.length?.max < password.length)
|
||||
return callback("password is too long")
|
||||
if (Settings.passwordStrengthOptions?.length?.min? and
|
||||
Settings.passwordStrengthOptions?.length?.min > password.length)
|
||||
return callback("password is too short")
|
||||
|
||||
bcrypt.genSalt BCRYPT_ROUNDS, (error, salt) ->
|
||||
return callback(error) if error?
|
||||
|
|
|
@ -230,6 +230,24 @@ module.exports = ProjectController =
|
|||
return cb(null, false)
|
||||
else
|
||||
return cb(null, true)
|
||||
showPerUserTCNotice: (cb) ->
|
||||
cb = underscore.once(cb)
|
||||
if !user_id?
|
||||
return cb()
|
||||
timestamp = user_id.toString().substring(0,8)
|
||||
userSignupDate = new Date( parseInt( timestamp, 16 ) * 1000 )
|
||||
if userSignupDate > new Date("2017-08-09")
|
||||
# Don't show for users who registered after it was released
|
||||
return cb(null, false)
|
||||
timeout = setTimeout cb, 500
|
||||
AnalyticsManager.getLastOccurance user_id, "shown-per-user-tc-notice", (error, event) ->
|
||||
clearTimeout timeout
|
||||
if error?
|
||||
return cb(null, false)
|
||||
else if event?
|
||||
return cb(null, false)
|
||||
else
|
||||
return cb(null, true)
|
||||
}, (err, results)->
|
||||
if err?
|
||||
logger.err err:err, "error getting details for project page"
|
||||
|
@ -237,7 +255,7 @@ module.exports = ProjectController =
|
|||
project = results.project
|
||||
user = results.user
|
||||
subscription = results.subscription
|
||||
showTrackChangesOnboarding = results.showTrackChangesOnboarding
|
||||
{ showTrackChangesOnboarding, showPerUserTCNotice } = results
|
||||
|
||||
daysSinceLastUpdated = (new Date() - project.lastUpdated) /86400000
|
||||
logger.log project_id:project_id, daysSinceLastUpdated:daysSinceLastUpdated, "got db results for loading editor"
|
||||
|
@ -279,8 +297,9 @@ module.exports = ProjectController =
|
|||
pdfViewer : user.ace.pdfViewer
|
||||
syntaxValidation: user.ace.syntaxValidation
|
||||
}
|
||||
trackChangesEnabled: !!project.track_changes
|
||||
trackChangesState: project.track_changes
|
||||
showTrackChangesOnboarding: !!showTrackChangesOnboarding
|
||||
showPerUserTCNotice: !!showPerUserTCNotice
|
||||
privilegeLevel: privilegeLevel
|
||||
chatUrl: Settings.apis.chat.url
|
||||
anonymous: anonymous
|
||||
|
|
|
@ -62,6 +62,15 @@ module.exports = SubscriptionUpdater =
|
|||
invited_emails: email
|
||||
}, callback
|
||||
|
||||
deleteSubscription: (subscription_id, callback = (error) ->) ->
|
||||
SubscriptionLocator.getSubscription subscription_id, (err, subscription) ->
|
||||
return callback(err) if err?
|
||||
affected_user_ids = [subscription.admin_id].concat(subscription.member_ids or [])
|
||||
logger.log {subscription_id, affected_user_ids}, "deleting subscription and downgrading users"
|
||||
Subscription.remove {_id: ObjectId(subscription_id)}, (err) ->
|
||||
return callback(err) if err?
|
||||
async.mapSeries affected_user_ids, SubscriptionUpdater._setUsersMinimumFeatures, callback
|
||||
|
||||
_createNewSubscription: (adminUser_id, callback)->
|
||||
logger.log adminUser_id:adminUser_id, "creating new subscription"
|
||||
subscription = new Subscription(admin_id:adminUser_id)
|
||||
|
|
|
@ -17,23 +17,15 @@ module.exports = UserCreator =
|
|||
createNewUser: (opts, callback)->
|
||||
logger.log opts:opts, "creating new user"
|
||||
user = new User()
|
||||
user.email = opts.email
|
||||
user.holdingAccount = opts.holdingAccount
|
||||
user.ace.syntaxValidation = true
|
||||
|
||||
username = opts.email.match(/^[^@]*/)
|
||||
if opts.first_name? and opts.first_name.length != 0
|
||||
user.first_name = opts.first_name
|
||||
else if username?
|
||||
user.first_name = username[0]
|
||||
else
|
||||
user.first_name = ""
|
||||
|
||||
if opts.last_name?
|
||||
user.last_name = opts.last_name
|
||||
else
|
||||
user.last_name = ""
|
||||
if !opts.first_name? or opts.first_name == ""
|
||||
opts.first_name = username[0]
|
||||
|
||||
for key, value of opts
|
||||
user[key] = value
|
||||
|
||||
user.ace.syntaxValidation = true
|
||||
user.featureSwitches?.pdfng = true
|
||||
|
||||
user.save (err)->
|
||||
|
|
|
@ -11,6 +11,7 @@ async = require("async")
|
|||
Modules = require "./Modules"
|
||||
Url = require "url"
|
||||
PackageVersions = require "./PackageVersions"
|
||||
htmlEncoder = new require("node-html-encoder").Encoder("numerical")
|
||||
fingerprints = {}
|
||||
Path = require 'path'
|
||||
|
||||
|
@ -151,9 +152,10 @@ module.exports = (app, webRouter, privateApiRouter, publicApiRouter)->
|
|||
next()
|
||||
|
||||
webRouter.use (req, res, next)->
|
||||
res.locals.translate = (key, vars = {}) ->
|
||||
res.locals.translate = (key, vars = {}, htmlEncode = false) ->
|
||||
vars.appName = Settings.appName
|
||||
req.i18n.translate(key, vars)
|
||||
str = req.i18n.translate(key, vars)
|
||||
if htmlEncode then htmlEncoder.htmlEncode(str) else str
|
||||
# Don't include the query string parameters, otherwise Google
|
||||
# treats ?nocdn=true as the canonical version
|
||||
res.locals.currentUrl = Url.parse(req.originalUrl).pathname
|
||||
|
|
|
@ -32,7 +32,7 @@ ProjectSchema = new Schema
|
|||
archived : { type: Boolean }
|
||||
deletedDocs : [DeletedDocSchema]
|
||||
imageName : { type: String }
|
||||
track_changes : { type: Boolean }
|
||||
track_changes : { type: Object }
|
||||
|
||||
ProjectSchema.statics.getProject = (project_or_id, fields, callback)->
|
||||
if project_or_id._id?
|
||||
|
|
|
@ -59,6 +59,10 @@ UserSchema = new Schema
|
|||
zotero: Boolean
|
||||
}
|
||||
betaProgram: { type:Boolean, default: false}
|
||||
overleaf:
|
||||
id: { type: Number }
|
||||
accessToken: { type: String }
|
||||
refreshToken: { type: String }
|
||||
|
||||
conn = mongoose.createConnection(Settings.mongo.url, server: poolSize: 10)
|
||||
|
||||
|
|
|
@ -7,36 +7,61 @@ script(type='text/ng-template', id='supportModalTemplate')
|
|||
) ×
|
||||
h3 #{translate("contact_us")}
|
||||
.modal-body.contact-us-modal
|
||||
span(ng-show="sent == false")
|
||||
label
|
||||
| #{translate("subject")}
|
||||
.form-group
|
||||
input.field.text.medium.span8.form-control(
|
||||
ng-model="form.subject",
|
||||
ng-model-options="{ updateOn: 'default blur', debounce: {'default': 350, 'blur': 0} }"
|
||||
maxlength='255',
|
||||
tabindex='1',
|
||||
onkeyup='')
|
||||
.contact-suggestions(ng-show="suggestions.length")
|
||||
p.contact-suggestion-label !{translate("kb_suggestions_enquiry", { kbLink: "<a href='learn/kb' target='_blank'>__kb__</a>", kb: translate("knowledge_base") })}
|
||||
ul.contact-suggestion-list
|
||||
li(ng-repeat="suggestion in suggestions")
|
||||
a.contact-suggestion-list-item(ng-href="{{ suggestion.url }}", ng-click="clickSuggestionLink(suggestion.url);" target="_blank")
|
||||
span(ng-bind-html="suggestion.name")
|
||||
i.fa.fa-angle-right
|
||||
label.desc(ng-show="'"+getUserEmail()+"'.length < 1")
|
||||
| #{translate("email")}
|
||||
.form-group(ng-show="'"+getUserEmail()+"'.length < 1")
|
||||
input.field.text.medium.span8.form-control(ng-model="form.email", ng-init="form.email = '"+getUserEmail()+"'", type='email', spellcheck='false', value='', maxlength='255', tabindex='2')
|
||||
label#title12.desc
|
||||
| #{translate("project_url")} (#{translate("optional")})
|
||||
.form-group
|
||||
input.field.text.medium.span8.form-control(ng-model="form.project_url", tabindex='3', onkeyup='')
|
||||
label.desc
|
||||
| #{translate("contact_message_label")}
|
||||
.form-group
|
||||
textarea.field.text.medium.span8.form-control(ng-model="form.message",type='text', value='', tabindex='4', onkeyup='')
|
||||
.form-group.text-center
|
||||
input.btn-success.btn.btn-lg(type='submit', ng-disabled="sending", ng-click="contactUs()" value=translate("contact_us"))
|
||||
form(name="contactForm")
|
||||
span(ng-show="sent == false")
|
||||
.alert.alert-danger(ng-show="error") Something went wrong sending your request :(
|
||||
label
|
||||
| #{translate("subject")}
|
||||
.form-group
|
||||
input.field.text.medium.span8.form-control(
|
||||
name="subject",
|
||||
required
|
||||
ng-model="form.subject",
|
||||
ng-model-options="{ updateOn: 'default blur', debounce: {'default': 350, 'blur': 0} }"
|
||||
maxlength='255',
|
||||
tabindex='1',
|
||||
onkeyup='')
|
||||
.contact-suggestions(ng-show="suggestions.length")
|
||||
p.contact-suggestion-label !{translate("kb_suggestions_enquiry", { kbLink: "<a href='learn/kb' target='_blank'>__kb__</a>", kb: translate("knowledge_base") })}
|
||||
ul.contact-suggestion-list
|
||||
li(ng-repeat="suggestion in suggestions")
|
||||
a.contact-suggestion-list-item(ng-href="{{ suggestion.url }}", ng-click="clickSuggestionLink(suggestion.url);" target="_blank")
|
||||
span(ng-bind-html="suggestion.name")
|
||||
i.fa.fa-angle-right
|
||||
label.desc(ng-show="'"+getUserEmail()+"'.length < 1")
|
||||
| #{translate("email")}
|
||||
.form-group(ng-show="'"+getUserEmail()+"'.length < 1")
|
||||
input.field.text.medium.span8.form-control(
|
||||
name="email",
|
||||
required
|
||||
ng-model="form.email",
|
||||
ng-init="form.email = '"+getUserEmail()+"'",
|
||||
type='email', spellcheck='false',
|
||||
value='',
|
||||
maxlength='255',
|
||||
tabindex='2')
|
||||
label#title12.desc
|
||||
| #{translate("project_url")} (#{translate("optional")})
|
||||
.form-group
|
||||
input.field.text.medium.span8.form-control(ng-model="form.project_url", tabindex='3', onkeyup='')
|
||||
label.desc
|
||||
| #{translate("contact_message_label")}
|
||||
.form-group
|
||||
textarea.field.text.medium.span8.form-control(
|
||||
name="body",
|
||||
required
|
||||
ng-model="form.message",
|
||||
type='text',
|
||||
value='',
|
||||
tabindex='4',
|
||||
onkeyup=''
|
||||
)
|
||||
.form-group.text-center
|
||||
input.btn-success.btn.btn-lg(
|
||||
type='submit',
|
||||
ng-disabled="contactForm.$invalid || sending",
|
||||
ng-click="contactUs()"
|
||||
value=translate("contact_us")
|
||||
)
|
||||
span(ng-show="sent")
|
||||
p #{translate("request_sent_thank_you")}
|
||||
|
|
|
@ -14,16 +14,16 @@ html(itemscope, itemtype='http://schema.org/Product')
|
|||
|
||||
|
||||
-if (typeof(title) == "undefined")
|
||||
title= 'ShareLaTeX, '+ translate("online_latex_editor")
|
||||
title= settings.appName + ', '+ translate("online_latex_editor")
|
||||
-else
|
||||
title= translate(title) + ' - ShareLaTeX, ' + translate("online_latex_editor")
|
||||
title= translate(title) + ' - ' + settings.appName + ', ' + translate("online_latex_editor")
|
||||
|
||||
link(rel="icon", href="/favicon.ico")
|
||||
link(rel="icon", sizes="192x192", href="/touch-icon-192x192.png")
|
||||
link(rel="apple-touch-icon-precomposed", href="/apple-touch-icon-precomposed.png")
|
||||
link(rel="mask-icon", href="/mask-favicon.svg", color="#a93529")
|
||||
link(rel="icon", href="/" + settings.brandPrefix + "favicon.ico")
|
||||
link(rel="icon", sizes="192x192", href="/" + settings.brandPrefix + "touch-icon-192x192.png")
|
||||
link(rel="apple-touch-icon-precomposed", href="/" + settings.brandPrefix + "apple-touch-icon-precomposed.png")
|
||||
link(rel="mask-icon", href="/" + settings.brandPrefix + "mask-favicon.svg", color="#a93529")
|
||||
|
||||
link(rel='stylesheet', href=buildCssPath('/style.css'))
|
||||
link(rel='stylesheet', href=buildCssPath("/" + settings.brandPrefix + "style.css"))
|
||||
|
||||
block _headLinks
|
||||
|
||||
|
@ -33,14 +33,14 @@ html(itemscope, itemtype='http://schema.org/Product')
|
|||
link(rel="alternate", href=subdomainDetails.url+currentUrl, hreflang=subdomainDetails.lngCode)
|
||||
|
||||
|
||||
meta(itemprop="name", content="ShareLaTeX, the Online LaTeX Editor")
|
||||
meta(itemprop="name", content=settings.appName + ", the Online LaTeX Editor")
|
||||
|
||||
-if (typeof(meta) == "undefined")
|
||||
meta(itemprop="description", name="description", content=translate("site_description"))
|
||||
-else
|
||||
meta(itemprop="description", name="description" , content=meta)
|
||||
|
||||
meta(itemprop="image", name="image", content="https://www.sharelatex.com/favicon.ico")
|
||||
meta(itemprop="image", name="image", content="/" + settings.brandPrefix + "favicon.ico")
|
||||
|
||||
- if (typeof(gaToken) != "undefined")
|
||||
script(type='text/javascript').
|
||||
|
|
|
@ -8,8 +8,8 @@ block vars
|
|||
block content
|
||||
.editor(ng-controller="IdeController").full-size
|
||||
.loading-screen(ng-if="state.loading")
|
||||
.loading-screen-lion-container
|
||||
.loading-screen-lion(
|
||||
.loading-screen-brand-container
|
||||
.loading-screen-brand(
|
||||
style="height: 20%;"
|
||||
ng-style="{ 'height': state.load_progress + '%' }"
|
||||
)
|
||||
|
@ -106,7 +106,7 @@ block requirejs
|
|||
//- We need to do .replace(/\//g, '\\/') do that '</script>' -> '<\/script>'
|
||||
//- and doesn't prematurely end the script tag.
|
||||
script#data(type="application/json").
|
||||
!{JSON.stringify({userSettings: userSettings, user: user}).replace(/\//g, '\\/')}
|
||||
!{JSON.stringify({userSettings: userSettings, user: user, trackChangesState: trackChangesState}).replace(/\//g, '\\/')}
|
||||
|
||||
script(type="text/javascript").
|
||||
window.data = JSON.parse($("#data").text());
|
||||
|
@ -118,8 +118,9 @@ block requirejs
|
|||
window.csrfToken = "!{csrfToken}";
|
||||
window.anonymous = #{anonymous};
|
||||
window.maxDocLength = #{maxDocLength};
|
||||
window.trackChangesEnabled = #{trackChangesEnabled};
|
||||
window.trackChangesState = data.trackChangesState;
|
||||
window.showTrackChangesOnboarding = #{!!showTrackChangesOnboarding};
|
||||
window.showPerUserTCNotice = #{!!showPerUserTCNotice};
|
||||
window.wikiEnabled = #{!!(settings.apis.wiki && settings.apis.wiki.url)};
|
||||
window.requirejs = {
|
||||
"paths" : {
|
||||
|
|
|
@ -82,7 +82,7 @@ div.full-size(
|
|||
i.fa.fa-long-arrow-right
|
||||
br
|
||||
a.btn.btn-default.btn-xs(
|
||||
tooltip-html="'"+translate('go_to_pdf_location_in_code')+"'"
|
||||
tooltip-html="'"+translate('go_to_pdf_location_in_code', {}, true)+"'"
|
||||
tooltip-placement="right"
|
||||
tooltip-append-to-body="true"
|
||||
ng-click="syncToCode()"
|
||||
|
|
|
@ -90,8 +90,8 @@ script(type="text/ng-template", id="hotkeysModalTemplate")
|
|||
span.combination Ctrl + Space
|
||||
span.description Search References
|
||||
|
||||
h3 #{translate("review")}
|
||||
.row
|
||||
h3(ng-if="trackChangesVisible") #{translate("review")}
|
||||
.row(ng-if="trackChangesVisible")
|
||||
.col-xs-4
|
||||
.hotkey
|
||||
span.combination {{ctrl}} + J
|
||||
|
@ -108,4 +108,4 @@ script(type="text/ng-template", id="hotkeysModalTemplate")
|
|||
.modal-footer
|
||||
button.btn.btn-default(
|
||||
ng-click="cancel()"
|
||||
) #{translate("ok")}
|
||||
) #{translate("ok")}
|
||||
|
|
|
@ -46,21 +46,40 @@
|
|||
is-loading="reviewPanel.dropdown.loading"
|
||||
permissions="permissions"
|
||||
)
|
||||
span.review-panel-toolbar-label(ng-if="permissions.write")
|
||||
span(ng-click="toggleTrackChanges(true)", ng-if="editor.wantTrackChanges === false") !{translate("track_changes_is_off")}
|
||||
span(ng-click="toggleTrackChanges(false)", ng-if="editor.wantTrackChanges === true") !{translate("track_changes_is_on")}
|
||||
review-panel-toggle(
|
||||
ng-if="editor.wantTrackChanges == editor.trackChanges"
|
||||
ng-model="editor.wantTrackChanges"
|
||||
on-toggle="toggleTrackChanges"
|
||||
disabled="!project.features.trackChanges"
|
||||
on-disabled-click="openTrackChangesUpgradeModal"
|
||||
span.review-panel-toolbar-label
|
||||
span.review-panel-toolbar-icon-on(
|
||||
ng-if="editor.wantTrackChanges === true"
|
||||
)
|
||||
span.review-panel-toolbar-label.review-panel-toolbar-label-disabled(ng-if="!permissions.write")
|
||||
span(ng-if="editor.wantTrackChanges === false") !{translate("track_changes_is_off")}
|
||||
span(ng-if="editor.wantTrackChanges === true") !{translate("track_changes_is_on")}
|
||||
span.review-panel-toolbar-spinner(ng-if="editor.wantTrackChanges != editor.trackChanges")
|
||||
i.fa.fa-spin.fa-spinner
|
||||
i.fa.fa-circle
|
||||
span(ng-click="toggleFullTCStateCollapse();")
|
||||
span(ng-if="editor.wantTrackChanges === false") !{translate("track_changes_is_off")}
|
||||
span(ng-if="editor.wantTrackChanges === true") !{translate("track_changes_is_on")}
|
||||
span.rp-tc-state-collapse(
|
||||
ng-class="{ 'rp-tc-state-collapse-on': reviewPanel.fullTCStateCollapsed }"
|
||||
)
|
||||
i.fa.fa-angle-down
|
||||
ul.rp-tc-state(
|
||||
review-panel-collapse-height="reviewPanel.fullTCStateCollapsed"
|
||||
)
|
||||
li.rp-tc-state-item.rp-tc-state-item-everyone
|
||||
span.rp-tc-state-item-name !{translate("tc_everyone")}
|
||||
review-panel-toggle(
|
||||
ng-model="reviewPanel.trackChangesOnForEveryone"
|
||||
on-toggle="toggleTrackChangesForEveryone(isOn);"
|
||||
disabled="!project.features.trackChanges || !permissions.write"
|
||||
)
|
||||
li.rp-tc-state-item(
|
||||
ng-repeat="member in reviewPanel.formattedProjectMembers"
|
||||
)
|
||||
span.rp-tc-state-item-name(
|
||||
ng-class="{ 'rp-tc-state-item-name-disabled' : reviewPanel.trackChangesOnForEveryone}"
|
||||
style="color: hsl({{ member.hue }}, 70%, 40%);"
|
||||
) {{ member.name }}
|
||||
review-panel-toggle(
|
||||
ng-model="reviewPanel.trackChangesState[member.id].value"
|
||||
on-toggle="toggleTrackChangesForUser(isOn, member.id);"
|
||||
disabled="reviewPanel.trackChangesOnForEveryone || !project.features.trackChanges || !permissions.write"
|
||||
)
|
||||
|
||||
.rp-entry-list(
|
||||
review-panel-sorted
|
||||
|
@ -494,7 +513,7 @@ script(type="text/ng-template", id="trackChangesUpgradeModalTemplate")
|
|||
.modal-body
|
||||
.teaser-video-container
|
||||
video.teaser-video(autoplay, loop)
|
||||
source(src="/img/teasers/track-changes/teaser-track-changes.mp4", type="video/mp4")
|
||||
source(ng-src="{{ '/img/teasers/track-changes/teaser-track-changes.mp4' }}", type="video/mp4")
|
||||
img(src="/img/teasers/track-changes/teaser-track-changes.gif")
|
||||
|
||||
h4.teaser-title #{translate("see_changes_in_your_documents_live")}
|
||||
|
@ -532,6 +551,37 @@ script(type="text/ng-template", id="trackChangesUpgradeModalTemplate")
|
|||
)
|
||||
span #{translate("close")}
|
||||
|
||||
script(type="text/ng-template", id="perUserTCNoticeModalTemplate")
|
||||
.modal-header
|
||||
button.close(
|
||||
type="button"
|
||||
data-dismiss="modal"
|
||||
ng-click="$close()"
|
||||
) ×
|
||||
h3 #{translate("per_user_tc_title")}
|
||||
.modal-body
|
||||
.teaser-video-container
|
||||
video.teaser-video(autoplay, loop)
|
||||
source(ng-src="{{ '/img/teasers/track-changes/per-user-track-changes.mp4' }}", type="video/mp4")
|
||||
img(src="/img/teasers/track-changes/per-user-track-changes.gif")
|
||||
h4.teaser-title #{translate("you_can_use_per_user_tc")}
|
||||
.row
|
||||
.col-md-8.col-md-offset-2
|
||||
ul.list-unstyled
|
||||
li
|
||||
i.fa.fa-check
|
||||
| #{translate("turn_tc_on_individuals")}
|
||||
|
||||
li
|
||||
i.fa.fa-check
|
||||
| #{translate("keep_tc_on_like_before")}
|
||||
|
||||
.modal-footer()
|
||||
button.btn.btn-default(
|
||||
ng-click="$close()"
|
||||
)
|
||||
span #{translate("close")}
|
||||
|
||||
script(type="text/ng-template", id="bulkActionsModalTemplate")
|
||||
.modal-header
|
||||
button.close(
|
||||
|
|
|
@ -226,8 +226,8 @@ module.exports = settings =
|
|||
# passwordStrengthOptions:
|
||||
# pattern: "aA$3"
|
||||
# length:
|
||||
# min: 1
|
||||
# max: 10
|
||||
# min: 6
|
||||
# max: 128
|
||||
|
||||
# Email support
|
||||
# -------------
|
||||
|
@ -343,6 +343,8 @@ module.exports = settings =
|
|||
|
||||
appName: "ShareLaTeX (Community Edition)"
|
||||
adminEmail: "placeholder@example.com"
|
||||
|
||||
brandPrefix: "" # Set to 'ol-' for overleaf styles
|
||||
|
||||
nav:
|
||||
title: "ShareLaTeX Community Edition"
|
||||
|
|
26
services/web/npm-shrinkwrap.json
generated
26
services/web/npm-shrinkwrap.json
generated
|
@ -1509,7 +1509,7 @@
|
|||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"from": "minimatch@^3.0.4",
|
||||
"from": "minimatch@^3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -1678,7 +1678,7 @@
|
|||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.2.11",
|
||||
"from": "iconv-lite@>=0.2.11 <0.3.0",
|
||||
"from": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz"
|
||||
},
|
||||
"ieee754": {
|
||||
|
@ -1698,7 +1698,7 @@
|
|||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"from": "inherits@>=2.0.1 <2.1.0",
|
||||
"from": "inherits@>=2.0.0 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz"
|
||||
},
|
||||
"ini": {
|
||||
|
@ -2728,6 +2728,11 @@
|
|||
"from": "node-forge@0.2.24",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.2.24.tgz"
|
||||
},
|
||||
"node-html-encoder": {
|
||||
"version": "0.0.2",
|
||||
"from": "node-html-encoder@0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/node-html-encoder/-/node-html-encoder-0.0.2.tgz"
|
||||
},
|
||||
"node-pre-gyp": {
|
||||
"version": "0.6.30",
|
||||
"from": "node-pre-gyp@0.6.30",
|
||||
|
@ -2842,6 +2847,11 @@
|
|||
"from": "number-is-nan@>=1.0.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz"
|
||||
},
|
||||
"oauth": {
|
||||
"version": "0.9.15",
|
||||
"from": "oauth@>=0.9.0 <0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz"
|
||||
},
|
||||
"oauth-sign": {
|
||||
"version": "0.8.2",
|
||||
"from": "oauth-sign@>=0.8.1 <0.9.0",
|
||||
|
@ -3065,6 +3075,11 @@
|
|||
"from": "passport-local@>=1.0.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz"
|
||||
},
|
||||
"passport-oauth2": {
|
||||
"version": "1.4.0",
|
||||
"from": "passport-oauth2@latest",
|
||||
"resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.4.0.tgz"
|
||||
},
|
||||
"passport-saml": {
|
||||
"version": "0.15.0",
|
||||
"from": "passport-saml@>=0.15.0 <0.16.0",
|
||||
|
@ -4287,6 +4302,11 @@
|
|||
"from": "uid-safe@2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.4.tgz"
|
||||
},
|
||||
"uid2": {
|
||||
"version": "0.0.3",
|
||||
"from": "uid2@>=0.0.0 <0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz"
|
||||
},
|
||||
"underscore": {
|
||||
"version": "1.6.0",
|
||||
"from": "underscore@1.6.0",
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
"mongojs": "2.4.0",
|
||||
"mongoose": "4.11.4",
|
||||
"multer": "^0.1.8",
|
||||
"node-html-encoder": "0.0.2",
|
||||
"nodemailer": "2.1.0",
|
||||
"nodemailer-sendgrid-transport": "^0.2.0",
|
||||
"nodemailer-ses-transport": "^1.3.0",
|
||||
|
@ -48,6 +49,7 @@
|
|||
"passport": "^0.3.2",
|
||||
"passport-ldapauth": "^0.6.0",
|
||||
"passport-local": "^1.0.0",
|
||||
"passport-oauth2": "^1.4.0",
|
||||
"passport-saml": "^0.15.0",
|
||||
"pug": "^2.0.0-beta6",
|
||||
"redis": "0.10.1",
|
||||
|
|
|
@ -103,8 +103,8 @@ define [
|
|||
defaultPasswordOpts =
|
||||
pattern: ""
|
||||
length:
|
||||
min: 1
|
||||
max: 50
|
||||
min: 6
|
||||
max: 128
|
||||
allowEmpty: false
|
||||
allowAnyChars: false
|
||||
isMasked: true
|
||||
|
@ -127,8 +127,6 @@ define [
|
|||
[asyncFormCtrl, ngModelCtrl] = ctrl
|
||||
|
||||
ngModelCtrl.$parsers.unshift (modelValue) ->
|
||||
|
||||
|
||||
isValid = passField.validatePass()
|
||||
email = asyncFormCtrl.getEmail() || window.usersEmail
|
||||
if !isValid
|
||||
|
@ -141,5 +139,8 @@ define [
|
|||
if opts.length.max? and modelValue.length == opts.length.max
|
||||
isValid = false
|
||||
scope.complexPasswordErrorMessage = "Maximum password length #{opts.length.max} reached"
|
||||
if opts.length.min? and modelValue.length < opts.length.min
|
||||
isValid = false
|
||||
scope.complexPasswordErrorMessage = "Password too short, minimum #{opts.length.min}"
|
||||
ngModelCtrl.$setValidity('complexPassword', isValid)
|
||||
return modelValue
|
||||
|
|
|
@ -90,6 +90,8 @@ define [
|
|||
$scope.chat = {}
|
||||
|
||||
ide.toggleReviewPanel = $scope.toggleReviewPanel = () ->
|
||||
if !$scope.project.features.trackChangesVisible
|
||||
return
|
||||
$scope.ui.reviewPanelOpen = !$scope.ui.reviewPanelOpen
|
||||
event_tracking.sendMB "rp-toggle-panel", { value : $scope.ui.reviewPanelOpen }
|
||||
|
||||
|
|
|
@ -37,10 +37,6 @@ define [
|
|||
@$scope.$watch "editor.wantTrackChanges", (value) =>
|
||||
return if !value?
|
||||
@_syncTrackChangesState(@$scope.editor.sharejs_doc)
|
||||
|
||||
@$scope.$watch "project.features.trackChanges", (trackChangesFeature) =>
|
||||
return if !trackChangesFeature?
|
||||
@$scope.editor.wantTrackChanges = window.trackChangesEnabled and trackChangesFeature
|
||||
|
||||
autoOpenDoc: () ->
|
||||
open_doc_id =
|
||||
|
|
|
@ -11,6 +11,8 @@ define [
|
|||
"ide/editor/directives/aceEditor/track-changes/TrackChangesManager"
|
||||
"ide/editor/directives/aceEditor/labels/LabelsManager"
|
||||
"ide/labels/services/labels"
|
||||
"ide/graphics/services/graphics"
|
||||
"ide/preamble/services/preamble"
|
||||
], (App, Ace, SearchBox, ModeList, UndoManager, AutoCompleteManager, SpellCheckManager, HighlightsManager, CursorPositionManager, TrackChangesManager, LabelsManager) ->
|
||||
EditSession = ace.require('ace/edit_session').EditSession
|
||||
ModeList = ace.require('ace/ext/modelist')
|
||||
|
@ -33,9 +35,8 @@ define [
|
|||
url = ace.config._moduleUrl(args...) + "?fingerprint=#{window.aceFingerprint}"
|
||||
return url
|
||||
|
||||
App.directive "aceEditor", ($timeout, $compile, $rootScope, event_tracking, localStorage, $cacheFactory, labels) ->
|
||||
App.directive "aceEditor", ($timeout, $compile, $rootScope, event_tracking, localStorage, $cacheFactory, labels, graphics, preamble) ->
|
||||
monkeyPatchSearch($rootScope, $compile)
|
||||
|
||||
|
||||
return {
|
||||
scope: {
|
||||
|
@ -102,7 +103,7 @@ define [
|
|||
cursorPositionManager = new CursorPositionManager(scope, editor, element, localStorage)
|
||||
trackChangesManager = new TrackChangesManager(scope, editor, element)
|
||||
labelsManager = new LabelsManager(scope, editor, element, labels)
|
||||
autoCompleteManager = new AutoCompleteManager(scope, editor, element, labelsManager)
|
||||
autoCompleteManager = new AutoCompleteManager(scope, editor, element, labelsManager, graphics, preamble)
|
||||
|
||||
|
||||
# Prevert Ctrl|Cmd-S from triggering save dialog
|
||||
|
@ -346,10 +347,6 @@ define [
|
|||
catch
|
||||
mode = "ace/mode/plain_text"
|
||||
|
||||
# Give beta users the next release of the syntax checker
|
||||
if mode is "ace/mode/latex" and window.user?.betaProgram
|
||||
mode = "ace/mode/latex_beta"
|
||||
|
||||
# create our new session
|
||||
session = new EditSession(lines, mode)
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ define [
|
|||
commandFragment?.match(/\\(\w+)\{/)?[1]
|
||||
|
||||
class AutoCompleteManager
|
||||
constructor: (@$scope, @editor, @element, @labelsManager) ->
|
||||
constructor: (@$scope, @editor, @element, @labelsManager, @graphics, @preamble) ->
|
||||
@suggestionManager = new CommandManager()
|
||||
|
||||
@monkeyPatchAutocomplete()
|
||||
|
@ -44,6 +44,37 @@ define [
|
|||
|
||||
SnippetCompleter = new EnvironmentManager()
|
||||
|
||||
Graphics = @graphics
|
||||
Preamble = @preamble
|
||||
GraphicsCompleter =
|
||||
getCompletions: (editor, session, pos, prefix, callback) ->
|
||||
upToCursorRange = new Range(pos.row, 0, pos.row, pos.column)
|
||||
lineUpToCursor = editor.getSession().getTextRange(upToCursorRange)
|
||||
commandFragment = getLastCommandFragment(lineUpToCursor)
|
||||
if commandFragment
|
||||
match = commandFragment.match(/^~?\\(includegraphics(?:\[.*])?){([^}]*, *)?(\w*)/)
|
||||
if match
|
||||
beyondCursorRange = new Range(pos.row, pos.column, pos.row, 99999)
|
||||
lineBeyondCursor = editor.getSession().getTextRange(beyondCursorRange)
|
||||
needsClosingBrace = !lineBeyondCursor.match(/^[^{]*}/)
|
||||
commandName = match[1]
|
||||
currentArg = match[3]
|
||||
graphicsPaths = Preamble.getGraphicsPaths()
|
||||
result = []
|
||||
for graphic in Graphics.getGraphicsFiles()
|
||||
path = graphic.path
|
||||
for graphicsPath in graphicsPaths
|
||||
if path.indexOf(graphicsPath) == 0
|
||||
path = path.slice(graphicsPath.length)
|
||||
break
|
||||
result.push {
|
||||
caption: "\\#{commandName}{#{path}#{if needsClosingBrace then '}' else ''}",
|
||||
value: "\\#{commandName}{#{path}#{if needsClosingBrace then '}' else ''}",
|
||||
meta: "graphic",
|
||||
score: 50
|
||||
}
|
||||
callback null, result
|
||||
|
||||
labelsManager = @labelsManager
|
||||
LabelsCompleter =
|
||||
getCompletions: (editor, session, pos, prefix, callback) ->
|
||||
|
@ -113,11 +144,12 @@ define [
|
|||
callback null, result
|
||||
|
||||
@editor.completers = [
|
||||
@suggestionManager,
|
||||
SnippetCompleter,
|
||||
ReferencesCompleter,
|
||||
LabelsCompleter
|
||||
]
|
||||
@suggestionManager,
|
||||
SnippetCompleter,
|
||||
ReferencesCompleter,
|
||||
LabelsCompleter,
|
||||
GraphicsCompleter
|
||||
]
|
||||
|
||||
disable: () ->
|
||||
@editor.setOptions({
|
||||
|
@ -250,7 +282,22 @@ define [
|
|||
editor.completer.autoSelect = true
|
||||
editor.completer.showPopup(editor)
|
||||
editor.completer.cancelContextMenu()
|
||||
$(editor.completer.popup?.container).css({'font-size': @$scope.fontSize + 'px'})
|
||||
container = $(editor.completer.popup?.container)
|
||||
container.css({'font-size': @$scope.fontSize + 'px'})
|
||||
# Dynamically set width of autocomplete popup
|
||||
if filtered = editor?.completer?.completions?.filtered
|
||||
longestCaption = _.max(filtered.map( (c) -> c.caption.length ))
|
||||
longestMeta = _.max(filtered.map( (c) -> c.meta.length ))
|
||||
charWidth = editor.renderer.characterWidth
|
||||
# between 280 and 700 px
|
||||
width = Math.max(
|
||||
Math.min(
|
||||
Math.round(longestCaption*charWidth + longestMeta*charWidth + 5*charWidth),
|
||||
700
|
||||
),
|
||||
280
|
||||
)
|
||||
container.css({width: "#{width}px"})
|
||||
if editor.completer?.completions?.filtered?.length == 0
|
||||
editor.completer.detach()
|
||||
bindKey: "Ctrl-Space|Ctrl-Shift-Space|Alt-Space"
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
define [
|
||||
"base"
|
||||
], (App) ->
|
||||
|
||||
App.factory 'graphics', (ide) ->
|
||||
|
||||
Graphics =
|
||||
getGraphicsFiles: () ->
|
||||
graphicsFiles = []
|
||||
ide.fileTreeManager.forEachEntity (entity, folder, path) ->
|
||||
if entity.type == 'file' && entity?.name?.match?(/.*\.(png|jpg|jpeg|pdf|eps)/)
|
||||
cloned = _.clone(entity)
|
||||
cloned.path = path
|
||||
graphicsFiles.push cloned
|
||||
return graphicsFiles
|
||||
|
||||
return Graphics
|
|
@ -10,13 +10,16 @@ define [
|
|||
templateUrl: "hotkeysModalTemplate"
|
||||
controller: "HotkeysModalController"
|
||||
size: "lg"
|
||||
resolve:
|
||||
trackChangesVisible: () -> $scope.project.features.trackChangesVisible
|
||||
}
|
||||
|
||||
App.controller "HotkeysModalController", ($scope, $modalInstance)->
|
||||
App.controller "HotkeysModalController", ($scope, $modalInstance, trackChangesVisible)->
|
||||
$scope.trackChangesVisible = trackChangesVisible
|
||||
if ace.require("ace/lib/useragent").isMac
|
||||
$scope.ctrl = "Cmd"
|
||||
else
|
||||
$scope.ctrl = "Ctrl"
|
||||
|
||||
$scope.cancel = () ->
|
||||
$modalInstance.dismiss()
|
||||
$modalInstance.dismiss()
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
define [
|
||||
"base"
|
||||
], (App) ->
|
||||
|
||||
App.factory 'preamble', (ide) ->
|
||||
|
||||
Preamble =
|
||||
getPreambleText: () ->
|
||||
text = ide.editorManager.getCurrentDocValue().slice(0, 5000)
|
||||
preamble = text.match(/([^]*)^\\begin\{document\}/m)?[1] || ""
|
||||
return preamble
|
||||
|
||||
getGraphicsPaths: () ->
|
||||
preamble = Preamble.getPreambleText()
|
||||
graphicsPathsArgs = preamble.match(/\\graphicspath\{(.*)\}/)?[1] || ""
|
||||
paths = []
|
||||
re = /\{([^}]*)\}/g
|
||||
while match = re.exec(graphicsPathsArgs)
|
||||
paths.push(match[1])
|
||||
return paths
|
||||
|
||||
return Preamble
|
|
@ -11,7 +11,13 @@ define [
|
|||
CUR_FILE : "cur_file"
|
||||
OVERVIEW : "overview"
|
||||
|
||||
$scope.UserTCSyncState = UserTCSyncState =
|
||||
SYNCED : "synced"
|
||||
PENDING : "pending"
|
||||
|
||||
$scope.reviewPanel =
|
||||
trackChangesState: {}
|
||||
trackChangesOnForEveryone: false
|
||||
entries: {}
|
||||
resolvedComments: {}
|
||||
hasEntries: false
|
||||
|
@ -25,6 +31,8 @@ define [
|
|||
commentThreads: {}
|
||||
resolvedThreadIds: {}
|
||||
rendererData: {}
|
||||
formattedProjectMembers: {}
|
||||
fullTCStateCollapsed: true
|
||||
loadingThreads: false
|
||||
# All selected changes. If a aggregated change (insertion + deletion) is selection, the two ids
|
||||
# will be present. The length of this array will differ from the count below (see explanation).
|
||||
|
@ -32,6 +40,7 @@ define [
|
|||
# A count of user-facing selected changes. An aggregated change (insertion + deletion) will count
|
||||
# as only one.
|
||||
nVisibleSelectedChanges: 0
|
||||
showPerUserTCNotice: window.showPerUserTCNotice
|
||||
|
||||
window.addEventListener "beforeunload", () ->
|
||||
collapsedStates = {}
|
||||
|
@ -59,6 +68,15 @@ define [
|
|||
if !visible
|
||||
$scope.ui.reviewPanelOpen = false
|
||||
|
||||
$scope.$watch "project.members", (members) ->
|
||||
$scope.reviewPanel.formattedProjectMembers = {}
|
||||
if $scope.project?.owner?
|
||||
$scope.reviewPanel.formattedProjectMembers[$scope.project.owner._id] = formatUser($scope.project.owner)
|
||||
if $scope.project?.members?
|
||||
for member in members
|
||||
if member.privileges == "readAndWrite"
|
||||
$scope.reviewPanel.formattedProjectMembers[member._id] = formatUser(member)
|
||||
|
||||
$scope.commentState =
|
||||
adding: false
|
||||
content: ""
|
||||
|
@ -416,6 +434,8 @@ define [
|
|||
$scope.toggleReviewPanel()
|
||||
|
||||
$scope.addNewCommentFromKbdShortcut = () ->
|
||||
if !$scope.project.features.trackChangesVisible
|
||||
return
|
||||
$scope.$broadcast "comment:select_line"
|
||||
if !$scope.ui.reviewPanelOpen
|
||||
$scope.toggleReviewPanel()
|
||||
|
@ -565,24 +585,83 @@ define [
|
|||
|
||||
$scope.gotoEntry = (doc_id, entry) ->
|
||||
ide.editorManager.openDocId(doc_id, { gotoOffset: entry.offset })
|
||||
|
||||
$scope.toggleTrackChanges = (value) ->
|
||||
|
||||
$scope.toggleFullTCStateCollapse = () ->
|
||||
if $scope.project.features.trackChanges
|
||||
$scope.editor.wantTrackChanges = value
|
||||
$http.post "/project/#{$scope.project_id}/track_changes", {_csrf: window.csrfToken, on: value}
|
||||
event_tracking.sendMB "rp-trackchanges-toggle", { value }
|
||||
if $scope.reviewPanel.showPerUserTCNotice
|
||||
$scope.openPerUserTCNoticeModal()
|
||||
$scope.reviewPanel.fullTCStateCollapsed = !$scope.reviewPanel.fullTCStateCollapsed
|
||||
else
|
||||
$scope.openTrackChangesUpgradeModal()
|
||||
|
||||
$scope.toggleTrackChangesFromKbdShortcut = () ->
|
||||
if $scope.editor.wantTrackChanges
|
||||
$scope.toggleTrackChanges false
|
||||
else
|
||||
$scope.toggleTrackChanges true
|
||||
_setUserTCState = (userId, newValue, isLocal = false) ->
|
||||
$scope.reviewPanel.trackChangesState[userId] ?= {}
|
||||
state = $scope.reviewPanel.trackChangesState[userId]
|
||||
|
||||
if !state.syncState? or state.syncState == UserTCSyncState.SYNCED
|
||||
state.value = newValue
|
||||
state.syncState = UserTCSyncState.SYNCED
|
||||
else if state.syncState == UserTCSyncState.PENDING and state.value == newValue
|
||||
state.syncState = UserTCSyncState.SYNCED
|
||||
else if isLocal
|
||||
state.value = newValue
|
||||
state.syncState = UserTCSyncState.PENDING
|
||||
|
||||
if userId == ide.$scope.user.id
|
||||
$scope.editor.wantTrackChanges = newValue
|
||||
|
||||
_setEveryoneTCState = (newValue, isLocal = false) ->
|
||||
$scope.reviewPanel.trackChangesOnForEveryone = newValue
|
||||
for member in $scope.project.members
|
||||
_setUserTCState(member._id, newValue, isLocal)
|
||||
_setUserTCState($scope.project.owner._id, newValue, isLocal)
|
||||
|
||||
applyClientTrackChangesStateToServer = () ->
|
||||
if $scope.reviewPanel.trackChangesOnForEveryone
|
||||
data = {on : true}
|
||||
else
|
||||
data = {on_for: {}}
|
||||
for userId, userState of $scope.reviewPanel.trackChangesState
|
||||
data.on_for[userId] = userState.value
|
||||
data._csrf = window.csrfToken
|
||||
$http.post "/project/#{$scope.project_id}/track_changes", data
|
||||
|
||||
applyTrackChangesStateToClient = (state) ->
|
||||
if typeof state is "boolean"
|
||||
_setEveryoneTCState state
|
||||
else
|
||||
$scope.reviewPanel.trackChangesOnForEveryone = false
|
||||
for member in $scope.project.members
|
||||
_setUserTCState(member._id, state[member._id] ? false)
|
||||
_setUserTCState($scope.project.owner._id, state[$scope.project.owner._id] ? false)
|
||||
|
||||
ide.socket.on "toggle-track-changes", (value) ->
|
||||
$scope.toggleTrackChangesForEveryone = (onForEveryone) ->
|
||||
_setEveryoneTCState onForEveryone, true
|
||||
applyClientTrackChangesStateToServer()
|
||||
|
||||
$scope.toggleTrackChangesForUser = (onForUser, userId) ->
|
||||
_setUserTCState userId, onForUser, true
|
||||
applyClientTrackChangesStateToServer()
|
||||
|
||||
ide.socket.on "toggle-track-changes", (state) ->
|
||||
$scope.$apply () ->
|
||||
$scope.editor.wantTrackChanges = value
|
||||
applyTrackChangesStateToClient(state)
|
||||
|
||||
$scope.toggleTrackChangesFromKbdShortcut = () ->
|
||||
if !$scope.project.features.trackChangesVisible
|
||||
return
|
||||
$scope.toggleTrackChangesForUser !$scope.reviewPanel.trackChangesState[ide.$scope.user.id].value, ide.$scope.user.id
|
||||
|
||||
_inited = false
|
||||
ide.$scope.$on "project:joined", () ->
|
||||
return if _inited
|
||||
project = ide.$scope.project
|
||||
if project.features.trackChanges
|
||||
window.trackChangesState ?= false
|
||||
applyTrackChangesStateToClient(window.trackChangesState)
|
||||
else
|
||||
applyTrackChangesStateToClient(false)
|
||||
_inited = true
|
||||
|
||||
_refreshingRangeUsers = false
|
||||
_refreshedForUserIds = {}
|
||||
|
@ -676,3 +755,11 @@ define [
|
|||
controller: "TrackChangesUpgradeModalController"
|
||||
scope: $scope.$new()
|
||||
}
|
||||
|
||||
$scope.openPerUserTCNoticeModal = () ->
|
||||
$scope.reviewPanel.showPerUserTCNotice = false
|
||||
$modal.open({
|
||||
templateUrl: "perUserTCNoticeModalTemplate"
|
||||
}).result.finally () ->
|
||||
event_tracking.sendMB "shown-per-user-tc-notice"
|
||||
|
||||
|
|
|
@ -4,20 +4,23 @@ define [
|
|||
App.directive "reviewPanelToggle", () ->
|
||||
restrict: "E"
|
||||
scope:
|
||||
onToggle: '='
|
||||
onToggle: '&'
|
||||
ngModel: '='
|
||||
valWhenUndefined: '=?'
|
||||
disabled: '=?'
|
||||
onDisabledClick: '=?'
|
||||
onDisabledClick: '&?'
|
||||
link: (scope) ->
|
||||
if !scope.disabled?
|
||||
scope.disabled = false
|
||||
scope.onChange = (args...) ->
|
||||
scope.onToggle(scope.localModel)
|
||||
scope.onToggle({ isOn: scope.localModel })
|
||||
scope.handleClick = () ->
|
||||
if scope.disabled
|
||||
if scope.disabled and scope.onDisabledClick?
|
||||
scope.onDisabledClick()
|
||||
scope.localModel = scope.ngModel
|
||||
scope.$watch "ngModel", (value) ->
|
||||
if scope.valWhenUndefined? and !value?
|
||||
value = scope.valWhenUndefined
|
||||
scope.localModel = value
|
||||
|
||||
template: """
|
||||
|
|
|
@ -30,7 +30,7 @@ define [
|
|||
$scope.suggestions = suggestions
|
||||
|
||||
$scope.contactUs = ->
|
||||
if !$scope.form.email?
|
||||
if !$scope.form.email? or $scope.form.email == ""
|
||||
console.log "email not set"
|
||||
return
|
||||
$scope.sending = true
|
||||
|
@ -46,8 +46,16 @@ define [
|
|||
about: "<div>browser: #{platform?.name} #{platform?.version}</div>
|
||||
<div>os: #{platform?.os?.family} #{platform?.os?.version}</div>"
|
||||
|
||||
Groove.createTicket params, (err, json)->
|
||||
$scope.sent = true
|
||||
Groove.createTicket params, (response)->
|
||||
$scope.sending = false
|
||||
if response.responseText == "" # Blocked request or similar
|
||||
$scope.error = true
|
||||
else
|
||||
data = JSON.parse(response.responseText)
|
||||
if data.errors?
|
||||
$scope.error = true
|
||||
else
|
||||
$scope.sent = true
|
||||
$scope.$apply()
|
||||
|
||||
$scope.$watch "form.subject", (newVal, oldVal) ->
|
||||
|
|
|
@ -20,7 +20,7 @@ define [
|
|||
, 10
|
||||
|
||||
$scope.$watch((
|
||||
() -> $scope.projects.filter((project) -> !project.tags? or project.tags.length == 0).length
|
||||
() -> $scope.projects.filter((project) -> (!project.tags? or project.tags.length == 0) and !project.archived).length
|
||||
), (newVal) -> $scope.nUntagged = newVal)
|
||||
|
||||
storedUIOpts = JSON.parse(localStorage("project_list"))
|
||||
|
|
BIN
services/web/public/img/ol-brand/logo-horizontal.png
Normal file
BIN
services/web/public/img/ol-brand/logo-horizontal.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
18
services/web/public/img/ol-brand/overleaf-o-grey.svg
Normal file
18
services/web/public/img/ol-brand/overleaf-o-grey.svg
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 136 157" style="enable-background:new 0 0 136 157;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#9B9B9B;}
|
||||
</style>
|
||||
<g id="Page-1">
|
||||
<g id="overleaf">
|
||||
<g id="Group">
|
||||
<path id="Fill-1" class="st0" d="M37.2,39.7C14.8,54,0,77.3,0,102.3C0,132.5,24.5,157,54.7,157c30.2,0,54.7-24.5,54.7-54.7
|
||||
c0-23.3-14.6-43.3-35.2-51.1c-4-1.5-12.6-4.2-19.4-3.6c-9.8,6.2-21.8,19-27.4,31.8c8.4-10.1,21.5-14.5,33.2-12.6
|
||||
c17.1,2.8,30.2,17.6,30.2,35.6c0,19.9-16.1,36-36,36c-11,0-20.8-4.9-27.4-12.6C17.5,114.3,15,101.9,17,89.8
|
||||
c6.9-42.4,57.2-66.5,94.6-75.8C99.4,20.5,77.4,31.1,62,42.6c44.9,17.3,52.2-20.5,73.2-37.5C114-3.1,37.3-6.1,37.2,39.7z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 947 B |
10
services/web/public/img/ol-brand/overleaf-o.svg
Normal file
10
services/web/public/img/ol-brand/overleaf-o.svg
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="136px" height="157px" viewBox="0 0 136 157" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="overleaf" fill="#4F9C45">
|
||||
<g id="Group">
|
||||
<path d="M37.205,39.652 C14.822,53.982 0,77.339 0,102.326 C0,132.522 24.48,157 54.681,157 C84.879,157 109.355,132.522 109.355,102.326 C109.355,78.986 94.729,59.05 74.151,51.215 C70.193,49.71 61.595,47.002 54.73,47.58 C44.924,53.814 32.979,66.624 27.319,79.389 C35.735,69.296 48.856,64.901 60.489,66.77 C77.615,69.547 90.697,84.408 90.697,102.321 C90.697,122.217 74.571,138.342 54.681,138.342 C43.719,138.342 33.896,133.446 27.293,125.723 C17.516,114.299 15,101.91 16.975,89.809 C23.902,47.434 74.208,23.279 111.611,14.01 C99.404,20.468 77.384,31.084 61.985,42.64 C106.909,59.981 114.169,22.123 135.202,5.181 C114.038,-3.07 37.33,-6.117 37.205,39.652 Z" id="Fill-1"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1 KiB |
Binary file not shown.
After Width: | Height: | Size: 189 KiB |
Binary file not shown.
|
@ -606,7 +606,7 @@ var createLatexWorker = function (session) {
|
|||
};
|
||||
worker.on("lint", function(results) {
|
||||
if(docChangePending) { docChangePending = false; };
|
||||
hints = results.data;
|
||||
hints = results.data.errors;
|
||||
if (hints.length > 100) {
|
||||
hints = hints.slice(0, 100); // limit to 100 errors
|
||||
};
|
||||
|
|
|
@ -1,655 +0,0 @@
|
|||
ace.define("ace/mode/latex_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"], function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var oop = require("../lib/oop");
|
||||
var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
|
||||
|
||||
var LatexHighlightRules = function() {
|
||||
|
||||
this.$rules = {
|
||||
"start" : [{
|
||||
token : "comment",
|
||||
regex : "%.*$"
|
||||
}, {
|
||||
token : ["keyword", "lparen", "variable.parameter", "rparen", "lparen", "storage.type", "rparen"],
|
||||
regex : "(\\\\(?:documentclass|usepackage|input))(?:(\\[)([^\\]]*)(\\]))?({)([^}]*)(})"
|
||||
}, {
|
||||
token : ["keyword","lparen", "variable.parameter", "rparen"],
|
||||
regex : "(\\\\(?:label|v?ref|cite(?:[^{]*)))(?:({)([^}]*)(}))?"
|
||||
}, {
|
||||
token : ["storage.type", "lparen", "variable.parameter", "rparen"],
|
||||
regex : "(\\\\(?:begin|end))({)(\\w*)(})"
|
||||
}, {
|
||||
token : "storage.type",
|
||||
regex : "\\\\[a-zA-Z]+"
|
||||
}, {
|
||||
token : "lparen",
|
||||
regex : "[[({]"
|
||||
}, {
|
||||
token : "rparen",
|
||||
regex : "[\\])}]"
|
||||
}, {
|
||||
token : "constant.character.escape",
|
||||
regex : "\\\\[^a-zA-Z]?"
|
||||
}, {
|
||||
token : "string",
|
||||
regex : "\\${1,2}",
|
||||
next : "equation"
|
||||
}],
|
||||
"equation" : [{
|
||||
token : "comment",
|
||||
regex : "%.*$"
|
||||
}, {
|
||||
token : "string",
|
||||
regex : "\\${1,2}",
|
||||
next : "start"
|
||||
}, {
|
||||
token : "constant.character.escape",
|
||||
regex : "\\\\(?:[^a-zA-Z]|[a-zA-Z]+)"
|
||||
}, {
|
||||
token : "error",
|
||||
regex : "^\\s*$",
|
||||
next : "start"
|
||||
}, {
|
||||
defaultToken : "string"
|
||||
}]
|
||||
|
||||
};
|
||||
};
|
||||
oop.inherits(LatexHighlightRules, TextHighlightRules);
|
||||
|
||||
exports.LatexHighlightRules = LatexHighlightRules;
|
||||
|
||||
});
|
||||
|
||||
ace.define("ace/mode/folding/latex",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range","ace/token_iterator"], function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var oop = require("../../lib/oop");
|
||||
var BaseFoldMode = require("./fold_mode").FoldMode;
|
||||
var Range = require("../../range").Range;
|
||||
var TokenIterator = require("../../token_iterator").TokenIterator;
|
||||
|
||||
var FoldMode = exports.FoldMode = function() {};
|
||||
|
||||
oop.inherits(FoldMode, BaseFoldMode);
|
||||
|
||||
(function() {
|
||||
|
||||
this.foldingStartMarker = /^\s*\\(begin)|(section|subsection|paragraph)\b|{\s*$/;
|
||||
this.foldingStopMarker = /^\s*\\(end)\b|^\s*}/;
|
||||
|
||||
this.getFoldWidgetRange = function(session, foldStyle, row) {
|
||||
var line = session.doc.getLine(row);
|
||||
var match = this.foldingStartMarker.exec(line);
|
||||
if (match) {
|
||||
if (match[1])
|
||||
return this.latexBlock(session, row, match[0].length - 1);
|
||||
if (match[2])
|
||||
return this.latexSection(session, row, match[0].length - 1);
|
||||
|
||||
return this.openingBracketBlock(session, "{", row, match.index);
|
||||
}
|
||||
|
||||
var match = this.foldingStopMarker.exec(line);
|
||||
if (match) {
|
||||
if (match[1])
|
||||
return this.latexBlock(session, row, match[0].length - 1);
|
||||
|
||||
return this.closingBracketBlock(session, "}", row, match.index + match[0].length);
|
||||
}
|
||||
};
|
||||
|
||||
this.latexBlock = function(session, row, column) {
|
||||
var keywords = {
|
||||
"\\begin": 1,
|
||||
"\\end": -1
|
||||
};
|
||||
|
||||
var stream = new TokenIterator(session, row, column);
|
||||
var token = stream.getCurrentToken();
|
||||
if (!token || !(token.type == "storage.type" || token.type == "constant.character.escape"))
|
||||
return;
|
||||
|
||||
var val = token.value;
|
||||
var dir = keywords[val];
|
||||
|
||||
var getType = function() {
|
||||
var token = stream.stepForward();
|
||||
var type = token.type == "lparen" ?stream.stepForward().value : "";
|
||||
if (dir === -1) {
|
||||
stream.stepBackward();
|
||||
if (type)
|
||||
stream.stepBackward();
|
||||
}
|
||||
return type;
|
||||
};
|
||||
var stack = [getType()];
|
||||
var startColumn = dir === -1 ? stream.getCurrentTokenColumn() : session.getLine(row).length;
|
||||
var startRow = row;
|
||||
|
||||
stream.step = dir === -1 ? stream.stepBackward : stream.stepForward;
|
||||
while(token = stream.step()) {
|
||||
if (!token || !(token.type == "storage.type" || token.type == "constant.character.escape"))
|
||||
continue;
|
||||
var level = keywords[token.value];
|
||||
if (!level)
|
||||
continue;
|
||||
var type = getType();
|
||||
if (level === dir)
|
||||
stack.unshift(type);
|
||||
else if (stack.shift() !== type || !stack.length)
|
||||
break;
|
||||
}
|
||||
|
||||
if (stack.length)
|
||||
return;
|
||||
|
||||
var row = stream.getCurrentTokenRow();
|
||||
if (dir === -1)
|
||||
return new Range(row, session.getLine(row).length, startRow, startColumn);
|
||||
stream.stepBackward();
|
||||
return new Range(startRow, startColumn, row, stream.getCurrentTokenColumn());
|
||||
};
|
||||
|
||||
this.latexSection = function(session, row, column) {
|
||||
var keywords = ["\\subsection", "\\section", "\\begin", "\\end", "\\paragraph"];
|
||||
|
||||
var stream = new TokenIterator(session, row, column);
|
||||
var token = stream.getCurrentToken();
|
||||
if (!token || token.type != "storage.type")
|
||||
return;
|
||||
|
||||
var startLevel = keywords.indexOf(token.value);
|
||||
var stackDepth = 0
|
||||
var endRow = row;
|
||||
|
||||
while(token = stream.stepForward()) {
|
||||
if (token.type !== "storage.type")
|
||||
continue;
|
||||
var level = keywords.indexOf(token.value);
|
||||
|
||||
if (level >= 2) {
|
||||
if (!stackDepth)
|
||||
endRow = stream.getCurrentTokenRow() - 1;
|
||||
stackDepth += level == 2 ? 1 : - 1;
|
||||
if (stackDepth < 0)
|
||||
break
|
||||
} else if (level >= startLevel)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!stackDepth)
|
||||
endRow = stream.getCurrentTokenRow() - 1;
|
||||
|
||||
while (endRow > row && !/\S/.test(session.getLine(endRow)))
|
||||
endRow--;
|
||||
|
||||
return new Range(
|
||||
row, session.getLine(row).length,
|
||||
endRow, session.getLine(endRow).length
|
||||
);
|
||||
};
|
||||
|
||||
}).call(FoldMode.prototype);
|
||||
|
||||
});
|
||||
|
||||
ace.define("ace/mode/behaviour/latex",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"], function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var oop = require("../../lib/oop");
|
||||
var Behaviour = require("../behaviour").Behaviour;
|
||||
var TokenIterator = require("../../token_iterator").TokenIterator;
|
||||
var lang = require("../../lib/lang");
|
||||
|
||||
var SAFE_INSERT_IN_TOKENS =
|
||||
["text", "paren.rparen", "punctuation.operator"];
|
||||
var SAFE_INSERT_BEFORE_TOKENS =
|
||||
["text", "paren.rparen", "punctuation.operator", "comment"];
|
||||
|
||||
var context;
|
||||
var contextCache = {};
|
||||
var initContext = function(editor) {
|
||||
var id = -1;
|
||||
if (editor.multiSelect) {
|
||||
id = editor.selection.index;
|
||||
if (contextCache.rangeCount != editor.multiSelect.rangeCount)
|
||||
contextCache = {rangeCount: editor.multiSelect.rangeCount};
|
||||
}
|
||||
if (contextCache[id])
|
||||
return context = contextCache[id];
|
||||
context = contextCache[id] = {
|
||||
autoInsertedBrackets: 0,
|
||||
autoInsertedRow: -1,
|
||||
autoInsertedLineEnd: "",
|
||||
maybeInsertedBrackets: 0,
|
||||
maybeInsertedRow: -1,
|
||||
maybeInsertedLineStart: "",
|
||||
maybeInsertedLineEnd: ""
|
||||
};
|
||||
};
|
||||
|
||||
var getWrapped = function(selection, selected, opening, closing) {
|
||||
var rowDiff = selection.end.row - selection.start.row;
|
||||
return {
|
||||
text: opening + selected + closing,
|
||||
selection: [
|
||||
0,
|
||||
selection.start.column + 1,
|
||||
rowDiff,
|
||||
selection.end.column + (rowDiff ? 0 : 1)
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
var LatexBehaviour = function() {
|
||||
this.add("braces", "insertion", function(state, action, editor, session, text) {
|
||||
if (editor.completer && editor.completer.popup && editor.completer.popup.isOpen) {
|
||||
return;
|
||||
}
|
||||
var cursor = editor.getCursorPosition();
|
||||
var line = session.doc.getLine(cursor.row);
|
||||
var lastChar = line[cursor.column-1];
|
||||
if (lastChar === '\\') {
|
||||
return;
|
||||
}
|
||||
if (text == '{') {
|
||||
initContext(editor);
|
||||
var selection = editor.getSelectionRange();
|
||||
var selected = session.doc.getTextRange(selection);
|
||||
if (selected !== "" && editor.getWrapBehavioursEnabled()) {
|
||||
return getWrapped(selection, selected, '{', '}');
|
||||
} else if (LatexBehaviour.isSaneInsertion(editor, session)) {
|
||||
LatexBehaviour.recordAutoInsert(editor, session, "}");
|
||||
return {
|
||||
text: '{}',
|
||||
selection: [1, 1]
|
||||
};
|
||||
}
|
||||
} else if (text == '}') {
|
||||
initContext(editor);
|
||||
var rightChar = line.substring(cursor.column, cursor.column + 1);
|
||||
if (rightChar == '}') {
|
||||
var matching = session.$findOpeningBracket('}', {column: cursor.column + 1, row: cursor.row});
|
||||
if (matching !== null && LatexBehaviour.isAutoInsertedClosing(cursor, line, text)) {
|
||||
LatexBehaviour.popAutoInsertedClosing();
|
||||
return {
|
||||
text: '',
|
||||
selection: [1, 1]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.add("braces", "deletion", function(state, action, editor, session, range) {
|
||||
if (editor.completer && editor.completer.popup && editor.completer.popup.isOpen) {
|
||||
return;
|
||||
}
|
||||
var selected = session.doc.getTextRange(range);
|
||||
if (!range.isMultiLine() && selected == '{') {
|
||||
initContext(editor);
|
||||
var line = session.doc.getLine(range.start.row);
|
||||
var rightChar = line.substring(range.start.column + 1, range.start.column + 2);
|
||||
if (rightChar == '}') {
|
||||
range.end.column++;
|
||||
return range;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.add("brackets", "insertion", function(state, action, editor, session, text) {
|
||||
if (editor.completer && editor.completer.popup && editor.completer.popup.isOpen) {
|
||||
return;
|
||||
}
|
||||
var cursor = editor.getCursorPosition();
|
||||
var line = session.doc.getLine(cursor.row);
|
||||
var lastChar = line[cursor.column-1];
|
||||
if (lastChar === '\\') {
|
||||
return;
|
||||
}
|
||||
if (text == '[') {
|
||||
initContext(editor);
|
||||
var selection = editor.getSelectionRange();
|
||||
var selected = session.doc.getTextRange(selection);
|
||||
if (selected !== "" && editor.getWrapBehavioursEnabled()) {
|
||||
return getWrapped(selection, selected, '[', ']');
|
||||
} else if (LatexBehaviour.isSaneInsertion(editor, session)) {
|
||||
LatexBehaviour.recordAutoInsert(editor, session, "]");
|
||||
return {
|
||||
text: '[]',
|
||||
selection: [1, 1]
|
||||
};
|
||||
}
|
||||
} else if (text == ']') {
|
||||
initContext(editor);
|
||||
var rightChar = line.substring(cursor.column, cursor.column + 1);
|
||||
if (rightChar == ']') {
|
||||
var matching = session.$findOpeningBracket(']', {column: cursor.column + 1, row: cursor.row});
|
||||
if (matching !== null && LatexBehaviour.isAutoInsertedClosing(cursor, line, text)) {
|
||||
LatexBehaviour.popAutoInsertedClosing();
|
||||
return {
|
||||
text: '',
|
||||
selection: [1, 1]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.add("brackets", "deletion", function(state, action, editor, session, range) {
|
||||
if (editor.completer && editor.completer.popup && editor.completer.popup.isOpen) {
|
||||
return;
|
||||
}
|
||||
var selected = session.doc.getTextRange(range);
|
||||
if (!range.isMultiLine() && selected == '[') {
|
||||
initContext(editor);
|
||||
var line = session.doc.getLine(range.start.row);
|
||||
var rightChar = line.substring(range.start.column + 1, range.start.column + 2);
|
||||
if (rightChar == ']') {
|
||||
range.end.column++;
|
||||
return range;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.add("dollars", "insertion", function(state, action, editor, session, text) {
|
||||
var cursor = editor.getCursorPosition();
|
||||
var line = session.doc.getLine(cursor.row);
|
||||
var lastChar = line[cursor.column-1];
|
||||
if (lastChar === '\\') {
|
||||
return;
|
||||
}
|
||||
if (text == '$') {
|
||||
if (this.lineCommentStart && this.lineCommentStart.indexOf(text) != -1)
|
||||
return;
|
||||
initContext(editor);
|
||||
var quote = text;
|
||||
var selection = editor.getSelectionRange();
|
||||
var selected = session.doc.getTextRange(selection);
|
||||
if (selected !== "" && selected !== "$" && editor.getWrapBehavioursEnabled()) {
|
||||
return getWrapped(selection, selected, quote, quote);
|
||||
} else if (!selected) {
|
||||
var leftChar = line.substring(cursor.column-1, cursor.column);
|
||||
var rightChar = line.substring(cursor.column, cursor.column + 1);
|
||||
|
||||
var token = session.getTokenAt(cursor.row, cursor.column);
|
||||
var rightToken = session.getTokenAt(cursor.row, cursor.column + 1);
|
||||
|
||||
var stringBefore = token && /string|escape/.test(token.type);
|
||||
var stringAfter = !rightToken || /string|escape/.test(rightToken.type);
|
||||
|
||||
var pair;
|
||||
if (rightChar == quote) {
|
||||
pair = stringBefore !== stringAfter;
|
||||
if (pair && /string\.end/.test(rightToken.type))
|
||||
pair = false;
|
||||
} else {
|
||||
if (stringBefore && !stringAfter)
|
||||
return null; // wrap string with different quote
|
||||
if (stringBefore && stringAfter)
|
||||
return null; // do not pair quotes inside strings
|
||||
var wordRe = session.$mode.tokenRe;
|
||||
wordRe.lastIndex = 0;
|
||||
var isWordBefore = wordRe.test(leftChar);
|
||||
wordRe.lastIndex = 0;
|
||||
var isWordAfter = wordRe.test(leftChar);
|
||||
if (isWordBefore || isWordAfter)
|
||||
return null; // before or after alphanumeric
|
||||
if (rightChar && !/[\s;,.})\]\\]/.test(rightChar))
|
||||
return null; // there is rightChar and it isn't closing
|
||||
pair = true;
|
||||
}
|
||||
return {
|
||||
text: pair ? quote + quote : "",
|
||||
selection: [1,1]
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.add("dollars", "deletion", function(state, action, editor, session, range) {
|
||||
var selected = session.doc.getTextRange(range);
|
||||
if (!range.isMultiLine() && (selected == '$')) {
|
||||
initContext(editor);
|
||||
var line = session.doc.getLine(range.start.row);
|
||||
var rightChar = line.substring(range.start.column + 1, range.start.column + 2);
|
||||
if (rightChar == selected) {
|
||||
range.end.column++;
|
||||
return range;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
|
||||
LatexBehaviour.isSaneInsertion = function(editor, session) {
|
||||
var cursor = editor.getCursorPosition();
|
||||
var iterator = new TokenIterator(session, cursor.row, cursor.column);
|
||||
if (!this.$matchTokenType(iterator.getCurrentToken() || "text", SAFE_INSERT_IN_TOKENS)) {
|
||||
var iterator2 = new TokenIterator(session, cursor.row, cursor.column + 1);
|
||||
if (!this.$matchTokenType(iterator2.getCurrentToken() || "text", SAFE_INSERT_IN_TOKENS))
|
||||
return false;
|
||||
}
|
||||
iterator.stepForward();
|
||||
return iterator.getCurrentTokenRow() !== cursor.row ||
|
||||
this.$matchTokenType(iterator.getCurrentToken() || "text", SAFE_INSERT_BEFORE_TOKENS);
|
||||
};
|
||||
|
||||
LatexBehaviour.$matchTokenType = function(token, types) {
|
||||
return types.indexOf(token.type || token) > -1;
|
||||
};
|
||||
|
||||
LatexBehaviour.recordAutoInsert = function(editor, session, bracket) {
|
||||
var cursor = editor.getCursorPosition();
|
||||
var line = session.doc.getLine(cursor.row);
|
||||
if (!this.isAutoInsertedClosing(cursor, line, context.autoInsertedLineEnd[0]))
|
||||
context.autoInsertedBrackets = 0;
|
||||
context.autoInsertedRow = cursor.row;
|
||||
context.autoInsertedLineEnd = bracket + line.substr(cursor.column);
|
||||
context.autoInsertedBrackets++;
|
||||
};
|
||||
|
||||
LatexBehaviour.isAutoInsertedClosing = function(cursor, line, bracket) {
|
||||
return context.autoInsertedBrackets > 0 &&
|
||||
cursor.row === context.autoInsertedRow &&
|
||||
bracket === context.autoInsertedLineEnd[0] &&
|
||||
line.substr(cursor.column) === context.autoInsertedLineEnd;
|
||||
};
|
||||
|
||||
LatexBehaviour.popAutoInsertedClosing = function() {
|
||||
context.autoInsertedLineEnd = context.autoInsertedLineEnd.substr(1);
|
||||
context.autoInsertedBrackets--;
|
||||
};
|
||||
|
||||
|
||||
oop.inherits(LatexBehaviour, Behaviour);
|
||||
|
||||
exports.LatexBehaviour = LatexBehaviour;
|
||||
});
|
||||
|
||||
ace.define("ace/mode/latex_beta",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/latex_highlight_rules","ace/mode/folding/latex","ace/range","ace/worker/worker_client","ace/mode/behaviour/latex"], function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var oop = require("../lib/oop");
|
||||
var TextMode = require("./text").Mode;
|
||||
var LatexHighlightRules = require("./latex_highlight_rules").LatexHighlightRules;
|
||||
var LatexFoldMode = require("./folding/latex").FoldMode;
|
||||
var Range = require("../range").Range;
|
||||
var WorkerClient = require("ace/worker/worker_client").WorkerClient;
|
||||
var LatexBehaviour = require("./behaviour/latex").LatexBehaviour;
|
||||
|
||||
var createLatexWorker = function (session) {
|
||||
var doc = session.getDocument();
|
||||
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_beta_worker", "LatexWorker");
|
||||
worker.attachToDocument(doc);
|
||||
var docChangeHandler = doc.on("change", function () {
|
||||
docChangePending = true;
|
||||
if(changeHandler) {
|
||||
clearTimeout(changeHandler);
|
||||
changeHandler = null;
|
||||
}
|
||||
});
|
||||
|
||||
var cursorHandler = selection.on("changeCursor", function () {
|
||||
if (docChangePending) { return; } ;
|
||||
changeHandler = setTimeout(function () {
|
||||
updateMarkers({cursorMoveOnly:true});
|
||||
suppressions = [];
|
||||
changeHandler = null;
|
||||
}, 100);
|
||||
});
|
||||
|
||||
var updateMarkers = function (options) {
|
||||
if (!options) { options = {};};
|
||||
var cursorMoveOnly = options.cursorMoveOnly;
|
||||
var annotations = [];
|
||||
var newRange = {};
|
||||
var cursor = selection.getCursor();
|
||||
var maxRow = session.getLength() - 1;
|
||||
var maxCol = (maxRow > 0) ? session.getLine(maxRow).length : 0;
|
||||
var cursorAtEndOfDocument = (cursor.row == maxRow) && (cursor.column === maxCol);
|
||||
|
||||
suppressions = [];
|
||||
|
||||
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.insideEnd(cursor.row, cursor.column);
|
||||
var cursorAtStart = hintRange.isStart(cursor.row, cursor.column - 1); // cursor after start not before
|
||||
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 isCascadeError = false;
|
||||
for (var j = 0, suplen = suppressions.length; j < suplen; j++) {
|
||||
var badRange = suppressions[j];
|
||||
if (badRange.intersects(hintRange)) {
|
||||
isCascadeError = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(isCascadeError) {
|
||||
if (!hint.suppressed) { suppressedChanges++; };
|
||||
hint.suppressed = true;
|
||||
continue;
|
||||
};
|
||||
|
||||
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};
|
||||
}
|
||||
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 movableStart = (cursorInRange && !errorAtStart) && !cursorAtEndOfDocument;
|
||||
var movableEnd = (cursorInRange && errorAtStart) && !cursorAtEndOfDocument;
|
||||
var a = movableStart ? cursorAnchor : doc.createAnchor(new_range.start);
|
||||
var b = movableEnd ? cursorAnchor : doc.createAnchor(new_range.end);
|
||||
var range = new Range();
|
||||
range.start = a;
|
||||
range.end = b;
|
||||
var cssClass = "ace_error-marker";
|
||||
if (hint.type === "warning") { cssClass = "ace_highlight-marker"; };
|
||||
range.id = session.addMarker(range, cssClass, "text");
|
||||
savedRange[key] = range;
|
||||
}
|
||||
}
|
||||
for (key in savedRange) {
|
||||
if (!newRange[key]) { // no longer present in list of errors to display
|
||||
range = savedRange[key];
|
||||
if (range.start !== cursorAnchor) { range.start.detach(); }
|
||||
if (range.end !== cursorAnchor) { range.end.detach(); }
|
||||
session.removeMarker(range.id);
|
||||
delete savedRange[key];
|
||||
}
|
||||
}
|
||||
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() {
|
||||
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.$behaviour = new LatexBehaviour();
|
||||
this.createWorker = createLatexWorker;
|
||||
};
|
||||
oop.inherits(Mode, TextMode);
|
||||
|
||||
(function() {
|
||||
this.type = "text";
|
||||
|
||||
this.lineCommentStart = "%";
|
||||
|
||||
this.$id = "ace/mode/latex_beta";
|
||||
}).call(Mode.prototype);
|
||||
|
||||
exports.Mode = Mode;
|
||||
|
||||
});
|
|
@ -1419,6 +1419,34 @@ var LatexWorker = exports.LatexWorker = function(sender) {
|
|||
|
||||
oop.inherits(LatexWorker, Mirror);
|
||||
|
||||
(function() {
|
||||
var disabled = false;
|
||||
this.onUpdate = function() {
|
||||
if (disabled) { return ; };
|
||||
|
||||
var value = this.doc.getValue();
|
||||
var errors = [];
|
||||
var contexts = [];
|
||||
try {
|
||||
if (value) {
|
||||
var result = Parse(value);
|
||||
errors = result.errors;
|
||||
contexts = result.contexts;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
disabled = true;
|
||||
this.sender.emit("fatal-error", e);
|
||||
errors = [];
|
||||
}
|
||||
this.sender.emit("lint", {
|
||||
errors: errors,
|
||||
contexts: contexts
|
||||
});
|
||||
};
|
||||
|
||||
}).call(LatexWorker.prototype);
|
||||
|
||||
var Tokenise = function (text) {
|
||||
var Tokens = [];
|
||||
var Comments = [];
|
||||
|
@ -1503,22 +1531,8 @@ var Tokenise = function (text) {
|
|||
}
|
||||
idx = SPECIAL.lastIndex = nextSpecialPos;
|
||||
}
|
||||
} else if (code === "{") { // open group
|
||||
Tokens.push([lineNumber, code, pos]);
|
||||
} else if (code === "}") { // close group
|
||||
Tokens.push([lineNumber, code, pos]);
|
||||
} else if (code === "$") { // math mode
|
||||
Tokens.push([lineNumber, code, pos]);
|
||||
} else if (code === "&") { // tabalign
|
||||
Tokens.push([lineNumber, code, pos]);
|
||||
} else if (code === "#") { // macro parameter
|
||||
Tokens.push([lineNumber, code, pos]);
|
||||
} else if (code === "^") { // superscript
|
||||
Tokens.push([lineNumber, code, pos]);
|
||||
} else if (code === "_") { // subscript
|
||||
Tokens.push([lineNumber, code, pos]);
|
||||
} else if (code === "~") { // active character (space)
|
||||
Tokens.push([lineNumber, code, pos]);
|
||||
} else if (["{", "}", "$", "&", "#", "^", "_", "~"].indexOf(code) > -1) { // special characters
|
||||
Tokens.push([lineNumber, code, pos, pos+1]);
|
||||
} else {
|
||||
throw "unrecognised character " + code;
|
||||
}
|
||||
|
@ -1539,15 +1553,15 @@ var read1arg = function (TokeniseResult, k, options) {
|
|||
};
|
||||
|
||||
var open = Tokens[k+1];
|
||||
var env = Tokens[k+2];
|
||||
var delimiter = Tokens[k+2];
|
||||
var close = Tokens[k+3];
|
||||
var envName;
|
||||
var delimiterName;
|
||||
|
||||
if(open && open[1] === "\\") {
|
||||
envName = open[4]; // array element 4 is command sequence
|
||||
delimiterName = 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]; // NOTE: if we were actually using this, keep track of * above
|
||||
} else if(open && open[1] === "{" && delimiter && delimiter[1] === "\\" && close && close[1] === "}") {
|
||||
delimiterName = delimiter[4]; // NOTE: if we were actually using this, keep track of * above
|
||||
return k + 3; // array element 4 is command sequence
|
||||
} else {
|
||||
return null;
|
||||
|
@ -1579,21 +1593,21 @@ var read1name = function (TokeniseResult, k) {
|
|||
var text = TokeniseResult.text;
|
||||
|
||||
var open = Tokens[k+1];
|
||||
var env = Tokens[k+2];
|
||||
var delimiter = Tokens[k+2];
|
||||
var close = Tokens[k+3];
|
||||
|
||||
if(open && open[1] === "{" && env && env[1] === "Text" && close && close[1] === "}") {
|
||||
var envName = text.substring(env[2], env[3]);
|
||||
if(open && open[1] === "{" && delimiter && delimiter[1] === "Text" && close && close[1] === "}") {
|
||||
var delimiterName = text.substring(delimiter[2], delimiter[3]);
|
||||
return k + 3;
|
||||
} else if (open && open[1] === "{" && env && env[1] === "Text") {
|
||||
envName = "";
|
||||
} else if (open && open[1] === "{" && delimiter && delimiter[1] === "Text") {
|
||||
delimiterName = "";
|
||||
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;
|
||||
delimiterName = delimiterName + str;
|
||||
} else if (tok[1] === "_") {
|
||||
envName = envName + "_";
|
||||
delimiterName = delimiterName + "_";
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
@ -1644,6 +1658,7 @@ var readOptionalParams = function(TokeniseResult, k) {
|
|||
};
|
||||
var count = 0;
|
||||
var nextToken = Tokens[k+1];
|
||||
if (!nextToken) { return null };
|
||||
var pos = nextToken[2];
|
||||
|
||||
for (var i = pos, end = text.length; i < end; i++) {
|
||||
|
@ -1788,7 +1803,7 @@ var InterpretTokens = function (TokeniseResult, ErrorReporter) {
|
|||
|
||||
var TokenErrorFromTo = ErrorReporter.TokenErrorFromTo;
|
||||
var TokenError = ErrorReporter.TokenError;
|
||||
var Environments = new EnvHandler(ErrorReporter);
|
||||
var Environments = new EnvHandler(TokeniseResult, ErrorReporter);
|
||||
|
||||
var nextGroupMathMode = null; // if the next group should have
|
||||
var nextGroupMathModeStack = [] ; // tracking all nextGroupMathModes
|
||||
|
@ -1815,28 +1830,28 @@ var InterpretTokens = function (TokeniseResult, ErrorReporter) {
|
|||
if (type === "\\") {
|
||||
if (seq === "begin" || seq === "end") {
|
||||
var open = Tokens[i+1];
|
||||
var env = Tokens[i+2];
|
||||
var delimiter = 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});
|
||||
if(open && open[1] === "{" && delimiter && delimiter[1] === "Text" && close && close[1] === "}") {
|
||||
var delimiterName = text.substring(delimiter[2], delimiter[3]);
|
||||
Environments.push({command: seq, name: delimiterName, token: token, closeToken: close});
|
||||
i = i + 3; // advance past these tokens
|
||||
} else {
|
||||
if (open && open[1] === "{" && env && env[1] === "Text") {
|
||||
envName = "";
|
||||
if (open && open[1] === "{" && delimiter && delimiter[1] === "Text") {
|
||||
delimiterName = "";
|
||||
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;
|
||||
delimiterName = delimiterName + str;
|
||||
} else if (tok[1] === "_") {
|
||||
envName = envName + "_";
|
||||
delimiterName = delimiterName + "_";
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tok && tok[1] === "}") {
|
||||
Environments.push({command: seq, name: envName, token: token, closeToken: close});
|
||||
Environments.push({command: seq, name: delimiterName, token: token, closeToken: close});
|
||||
i = j; // advance past these tokens
|
||||
continue;
|
||||
}
|
||||
|
@ -1844,8 +1859,8 @@ var InterpretTokens = function (TokeniseResult, ErrorReporter) {
|
|||
var endToken = null;
|
||||
if (open && open[1] === "{") {
|
||||
endToken = open; // we've got a {
|
||||
if (env && env[1] === "Text") {
|
||||
endToken = env.slice(); // we've got some text following the {
|
||||
if (delimiter && delimiter[1] === "Text") {
|
||||
endToken = delimiter.slice(); // we've got some text following the {
|
||||
start = endToken[2]; end = endToken[3];
|
||||
for (j = start; j < end; j++) {
|
||||
var char = text[j];
|
||||
|
@ -1978,7 +1993,12 @@ var InterpretTokens = function (TokeniseResult, ErrorReporter) {
|
|||
var nextIsDollar = lookAhead && lookAhead[1] === "$";
|
||||
currentMathMode = Environments.getMathMode() ; // returns null / $(inline) / $$(display)
|
||||
if (nextIsDollar && (!currentMathMode || currentMathMode.command == "$$")) {
|
||||
Environments.push({command:"$$", token:token});
|
||||
if (currentMathMode && currentMathMode.command == "$$") {
|
||||
var delimiterToken = lookAhead;
|
||||
} else {
|
||||
var delimiterToken = token;
|
||||
}
|
||||
Environments.push({command:"$$", token:delimiterToken});
|
||||
i = i + 1;
|
||||
} else {
|
||||
Environments.push({command:"$", token:token});
|
||||
|
@ -1999,74 +2019,174 @@ var InterpretTokens = function (TokeniseResult, ErrorReporter) {
|
|||
return Environments;
|
||||
};
|
||||
|
||||
var EnvHandler = function (ErrorReporter) {
|
||||
var DocumentTree = function(TokeniseResult) {
|
||||
var tree = {
|
||||
children: []
|
||||
};
|
||||
var stack = [tree];
|
||||
|
||||
this.openEnv = function(startDelimiter) {
|
||||
var currentNode = this.getCurrentNode();
|
||||
var newNode = {
|
||||
startDelimiter: startDelimiter,
|
||||
children: []
|
||||
};
|
||||
currentNode.children.push(newNode);
|
||||
stack.push(newNode);
|
||||
};
|
||||
|
||||
this.closeEnv = function(endDelimiter) {
|
||||
if (stack.length == 1) {
|
||||
return null
|
||||
}
|
||||
var currentNode = stack.pop();
|
||||
currentNode.endDelimiter = endDelimiter;
|
||||
return currentNode.startDelimiter;
|
||||
};
|
||||
|
||||
this.getNthPreviousNode = function(n) {
|
||||
var offset = stack.length - n - 1;
|
||||
if (offset < 0)
|
||||
return null;
|
||||
return stack[offset];
|
||||
}
|
||||
|
||||
this.getCurrentNode = function() {
|
||||
return this.getNthPreviousNode(0);
|
||||
}
|
||||
|
||||
this.getCurrentDelimiter = function() {
|
||||
return this.getCurrentNode().startDelimiter;
|
||||
};
|
||||
|
||||
this.getPreviousDelimiter = function() {
|
||||
var node = this.getNthPreviousNode(1);
|
||||
if (!node)
|
||||
return null
|
||||
return node.startDelimiter;
|
||||
}
|
||||
|
||||
this.getDepth = function() {
|
||||
return (stack.length - 1) // Root node doesn't count
|
||||
}
|
||||
|
||||
this.getContexts = function() {
|
||||
var linePosition = TokeniseResult.linePosition;
|
||||
|
||||
function tokenToRange(token) {
|
||||
var line = token[0], start = token[2], end = token[3];
|
||||
var start_col = start - linePosition[line];
|
||||
if (!end) { end = start + 1; } ;
|
||||
var end_col = end - linePosition[line];
|
||||
return {
|
||||
start: {
|
||||
row: line,
|
||||
column: start_col
|
||||
},
|
||||
end: {
|
||||
row: line,
|
||||
column: end_col
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function getContextsFromNode(node) {
|
||||
if (node.startDelimiter && node.startDelimiter.mathMode) {
|
||||
var context = {
|
||||
type: "math",
|
||||
range: {
|
||||
start: tokenToRange(node.startDelimiter.token).start
|
||||
}
|
||||
};
|
||||
if (node.endDelimiter) {
|
||||
var closeToken = node.endDelimiter.closeToken || node.endDelimiter.token;
|
||||
context.range.end = tokenToRange(closeToken).end;
|
||||
};
|
||||
return [context];
|
||||
} else {
|
||||
var contexts = [];
|
||||
for (var i = 0; i < node.children.length; i++) {
|
||||
var child = node.children[i];
|
||||
contexts = contexts.concat(getContextsFromNode(child));
|
||||
}
|
||||
return contexts;
|
||||
}
|
||||
};
|
||||
|
||||
return getContextsFromNode(tree);
|
||||
}
|
||||
}
|
||||
|
||||
var EnvHandler = function (TokeniseResult, ErrorReporter) {
|
||||
var ErrorTo = ErrorReporter.EnvErrorTo;
|
||||
var ErrorFromTo = ErrorReporter.EnvErrorFromTo;
|
||||
var ErrorFrom = ErrorReporter.EnvErrorFrom;
|
||||
|
||||
var envs = [];
|
||||
var delimiters = [];
|
||||
|
||||
var state = [];
|
||||
var document = new DocumentTree(TokeniseResult);
|
||||
var documentClosed = null;
|
||||
var inVerbatim = false;
|
||||
var verbatimRanges = [];
|
||||
|
||||
this.Environments = envs;
|
||||
|
||||
this.push = function (newEnv) {
|
||||
this.setEnvProps(newEnv);
|
||||
this.checkAndUpdateState(newEnv);
|
||||
envs.push(newEnv);
|
||||
|
||||
this.getDocument = function() {
|
||||
return document;
|
||||
};
|
||||
|
||||
this._endVerbatim = function (thisEnv) {
|
||||
var lastEnv = state.pop();
|
||||
if (lastEnv && lastEnv.name === thisEnv.name) {
|
||||
this.push = function (newDelimiter) {
|
||||
this.setDelimiterProps(newDelimiter);
|
||||
this.checkAndUpdateState(newDelimiter);
|
||||
delimiters.push(newDelimiter);
|
||||
};
|
||||
|
||||
this._endVerbatim = function (thisDelimiter) {
|
||||
var lastDelimiter = document.getCurrentDelimiter();
|
||||
if (lastDelimiter && lastDelimiter.name === thisDelimiter.name) {
|
||||
inVerbatim = false;
|
||||
verbatimRanges.push({start: lastEnv.token[2], end: thisEnv.token[2]});
|
||||
} else {
|
||||
if(lastEnv) { state.push(lastEnv); } ;
|
||||
document.closeEnv(thisDelimiter);
|
||||
verbatimRanges.push({start: lastDelimiter.token[2], end: thisDelimiter.token[2]});
|
||||
}
|
||||
};
|
||||
|
||||
var invalidEnvs = [];
|
||||
|
||||
this._end = function (thisEnv) {
|
||||
this._end = function (thisDelimiter) {
|
||||
do {
|
||||
var lastEnv = state.pop();
|
||||
var lastDelimiter = document.getCurrentDelimiter();
|
||||
var retry = false;
|
||||
var i;
|
||||
|
||||
if (closedBy(lastEnv, thisEnv)) {
|
||||
if (thisEnv.command === "end" && thisEnv.name === "document" && !documentClosed) {
|
||||
documentClosed = thisEnv;
|
||||
if (closedBy(lastDelimiter, thisDelimiter)) {
|
||||
document.closeEnv(thisDelimiter);
|
||||
if (thisDelimiter.command === "end" && thisDelimiter.name === "document" && !documentClosed) {
|
||||
documentClosed = thisDelimiter;
|
||||
};
|
||||
return;
|
||||
} else if (!lastEnv) {
|
||||
} else if (!lastDelimiter) {
|
||||
if (documentClosed) {
|
||||
ErrorFromTo(documentClosed, thisEnv, "\\end{" + documentClosed.name + "} is followed by unexpected content",{errorAtStart: true, type: "info"});
|
||||
ErrorFromTo(documentClosed, thisDelimiter, "\\end{" + documentClosed.name + "} is followed by unexpected content",{errorAtStart: true, type: "info"});
|
||||
} else {
|
||||
ErrorTo(thisEnv, "unexpected " + getName(thisEnv));
|
||||
ErrorTo(thisDelimiter, "unexpected " + getName(thisDelimiter));
|
||||
}
|
||||
} else if (invalidEnvs.length > 0 && (i = indexOfClosingEnvInArray(invalidEnvs, thisEnv) > -1)) {
|
||||
} else if (invalidEnvs.length > 0 && (i = indexOfClosingEnvInArray(invalidEnvs, thisDelimiter) > -1)) {
|
||||
invalidEnvs.splice(i, 1);
|
||||
if (lastEnv) { state.push(lastEnv); } ;
|
||||
return;
|
||||
} else {
|
||||
var status = reportError(lastEnv, thisEnv);
|
||||
if (envPrecedence(lastEnv) < envPrecedence(thisEnv)) {
|
||||
invalidEnvs.push(lastEnv);
|
||||
var status = reportError(lastDelimiter, thisDelimiter);
|
||||
if (delimiterPrecedence(lastDelimiter) < delimiterPrecedence(thisDelimiter)) {
|
||||
document.closeEnv();
|
||||
invalidEnvs.push(lastDelimiter);
|
||||
retry = true;
|
||||
} else {
|
||||
var prevLastEnv = state.pop();
|
||||
if(prevLastEnv) {
|
||||
if (thisEnv.name === prevLastEnv.name) {
|
||||
var prevDelimiter = document.getPreviousDelimiter();
|
||||
if(prevDelimiter) {
|
||||
if (thisDelimiter.name === prevDelimiter.name) {
|
||||
document.closeEnv() // Close current env
|
||||
document.closeEnv(thisDelimiter) // Close previous env
|
||||
return;
|
||||
} else {
|
||||
state.push(prevLastEnv);
|
||||
}
|
||||
}
|
||||
invalidEnvs.push(lastEnv);
|
||||
invalidEnvs.push(lastDelimiter);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2082,28 +2202,28 @@ var EnvHandler = function (ErrorReporter) {
|
|||
"$$": "$$"
|
||||
};
|
||||
|
||||
var closedBy = function (lastEnv, thisEnv) {
|
||||
if (!lastEnv) {
|
||||
var closedBy = function (lastDelimiter, thisDelimiter) {
|
||||
if (!lastDelimiter) {
|
||||
return false ;
|
||||
} else if (thisEnv.command === "end") {
|
||||
return lastEnv.command === "begin" && lastEnv.name === thisEnv.name;
|
||||
} else if (thisEnv.command === CLOSING_DELIMITER[lastEnv.command]) {
|
||||
} else if (thisDelimiter.command === "end") {
|
||||
return lastDelimiter.command === "begin" && lastDelimiter.name === thisDelimiter.name;
|
||||
} else if (thisDelimiter.command === CLOSING_DELIMITER[lastDelimiter.command]) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
var indexOfClosingEnvInArray = function (envs, thisEnv) {
|
||||
for (var i = 0, n = envs.length; i < n ; i++) {
|
||||
if (closedBy(envs[i], thisEnv)) {
|
||||
var indexOfClosingEnvInArray = function (delimiters, thisDelimiter) {
|
||||
for (var i = 0, n = delimiters.length; i < n ; i++) {
|
||||
if (closedBy(delimiters[i], thisDelimiter)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
var envPrecedence = function (env) {
|
||||
var delimiterPrecedence = function (delimiter) {
|
||||
var openScore = {
|
||||
"{" : 1,
|
||||
"left" : 2,
|
||||
|
@ -2118,14 +2238,14 @@ var EnvHandler = function (ErrorReporter) {
|
|||
"$$" : 5,
|
||||
"end": 4
|
||||
};
|
||||
if (env.command) {
|
||||
return openScore[env.command] || closeScore[env.command];
|
||||
if (delimiter.command) {
|
||||
return openScore[delimiter.command] || closeScore[delimiter.command];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
var getName = function(env) {
|
||||
var getName = function(delimiter) {
|
||||
var description = {
|
||||
"{" : "open group {",
|
||||
"}" : "close group }",
|
||||
|
@ -2138,12 +2258,12 @@ var EnvHandler = function (ErrorReporter) {
|
|||
"left" : "\\left",
|
||||
"right" : "\\right"
|
||||
};
|
||||
if (env.command === "begin" || env.command === "end") {
|
||||
return "\\" + env.command + "{" + env.name + "}";
|
||||
} else if (env.command in description) {
|
||||
return description[env.command];
|
||||
if (delimiter.command === "begin" || delimiter.command === "end") {
|
||||
return "\\" + delimiter.command + "{" + delimiter.name + "}";
|
||||
} else if (delimiter.command in description) {
|
||||
return description[delimiter.command];
|
||||
} else {
|
||||
return env.command;
|
||||
return delimiter.command;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2151,81 +2271,81 @@ var EnvHandler = function (ErrorReporter) {
|
|||
var UNCLOSED_GROUP = 2;
|
||||
var UNCLOSED_ENV = 3;
|
||||
|
||||
var reportError = function(lastEnv, thisEnv) {
|
||||
if (!lastEnv) { // unexpected close, nothing was open!
|
||||
var reportError = function(lastDelimiter, thisDelimiter) {
|
||||
if (!lastDelimiter) { // unexpected close, nothing was open!
|
||||
if (documentClosed) {
|
||||
ErrorFromTo(documentClosed, thisEnv, "\\end{" + documentClosed.name + "} is followed by unexpected end group }",{errorAtStart: true, type: "info"});
|
||||
ErrorFromTo(documentClosed, thisDelimiter, "\\end{" + documentClosed.name + "} is followed by unexpected end group }",{errorAtStart: true, type: "info"});
|
||||
} else {
|
||||
ErrorTo(thisEnv, "unexpected " + getName(thisEnv));
|
||||
ErrorTo(thisDelimiter, "unexpected " + getName(thisDelimiter));
|
||||
};
|
||||
return EXTRA_CLOSE;
|
||||
} else if (lastEnv.command === "{" && thisEnv.command === "end") {
|
||||
ErrorFromTo(lastEnv, thisEnv, "unclosed " + getName(lastEnv) + " found at " + getName(thisEnv),
|
||||
} else if (lastDelimiter.command === "{" && thisDelimiter.command === "end") {
|
||||
ErrorFromTo(lastDelimiter, thisDelimiter, "unclosed " + getName(lastDelimiter) + " found at " + getName(thisDelimiter),
|
||||
{suppressIfEditing:true, errorAtStart: true, type:"warning"});
|
||||
return UNCLOSED_GROUP;
|
||||
} else {
|
||||
var pLast = envPrecedence(lastEnv);
|
||||
var pThis = envPrecedence(thisEnv);
|
||||
var pLast = delimiterPrecedence(lastDelimiter);
|
||||
var pThis = delimiterPrecedence(thisDelimiter);
|
||||
if (pThis > pLast) {
|
||||
ErrorFromTo(lastEnv, thisEnv, "unclosed " + getName(lastEnv) + " found at " + getName(thisEnv),
|
||||
ErrorFromTo(lastDelimiter, thisDelimiter, "unclosed " + getName(lastDelimiter) + " found at " + getName(thisDelimiter),
|
||||
{suppressIfEditing:true, errorAtStart: true});
|
||||
} else {
|
||||
ErrorFromTo(lastEnv, thisEnv, "unexpected " + getName(thisEnv) + " after " + getName(lastEnv));
|
||||
ErrorFromTo(lastDelimiter, thisDelimiter, "unexpected " + getName(thisDelimiter) + " after " + getName(lastDelimiter));
|
||||
}
|
||||
return UNCLOSED_ENV;
|
||||
};
|
||||
};
|
||||
|
||||
this._beginMathMode = function (thisEnv) {
|
||||
this._beginMathMode = function (thisDelimiter) {
|
||||
var currentMathMode = this.getMathMode(); // undefined, null, $, $$, name of mathmode env
|
||||
if (currentMathMode) {
|
||||
ErrorFrom(thisEnv, getName(thisEnv) + " used inside existing math mode " + getName(currentMathMode),
|
||||
ErrorFrom(thisDelimiter, getName(thisDelimiter) + " used inside existing math mode " + getName(currentMathMode),
|
||||
{suppressIfEditing:true, errorAtStart: true, mathMode:true});
|
||||
};
|
||||
thisEnv.mathMode = thisEnv;
|
||||
state.push(thisEnv);
|
||||
thisDelimiter.mathMode = thisDelimiter;
|
||||
document.openEnv(thisDelimiter);
|
||||
};
|
||||
|
||||
this._toggleMathMode = function (thisEnv) {
|
||||
var lastEnv = state.pop();
|
||||
if (closedBy(lastEnv, thisEnv)) {
|
||||
this._toggleMathMode = function (thisDelimiter) {
|
||||
var lastDelimiter = document.getCurrentDelimiter();
|
||||
if (closedBy(lastDelimiter, thisDelimiter)) {
|
||||
document.closeEnv(thisDelimiter)
|
||||
return;
|
||||
} else {
|
||||
if (lastEnv) {state.push(lastEnv);}
|
||||
if (lastEnv && lastEnv.mathMode) {
|
||||
this._end(thisEnv);
|
||||
if (lastDelimiter && lastDelimiter.mathMode) {
|
||||
this._end(thisDelimiter);
|
||||
} else {
|
||||
thisEnv.mathMode = thisEnv;
|
||||
state.push(thisEnv);
|
||||
thisDelimiter.mathMode = thisDelimiter;
|
||||
document.openEnv(thisDelimiter);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
this.getMathMode = function () {
|
||||
var n = state.length;
|
||||
if (n > 0) {
|
||||
return state[n-1].mathMode;
|
||||
var currentDelimiter = document.getCurrentDelimiter();
|
||||
if (currentDelimiter) {
|
||||
return currentDelimiter.mathMode;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
this.insideGroup = function () {
|
||||
var n = state.length;
|
||||
if (n > 0) {
|
||||
return (state[n-1].command === "{");
|
||||
var currentDelimiter = document.getCurrentDelimiter();
|
||||
if (currentDelimiter) {
|
||||
return (currentDelimiter.command === "{");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
var resetMathMode = function () {
|
||||
var n = state.length;
|
||||
if (n > 0) {
|
||||
var lastMathMode = state[n-1].mathMode;
|
||||
var currentDelimiter = document.getCurrentDelimiter();
|
||||
if (currentDelimiter) {
|
||||
var lastMathMode = currentDelimiter.mathMode;
|
||||
do {
|
||||
var lastEnv = state.pop();
|
||||
} while (lastEnv && lastEnv !== lastMathMode);
|
||||
var lastDelimiter = document.closeEnv();
|
||||
} while (lastDelimiter && lastDelimiter !== lastMathMode);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
@ -2233,42 +2353,42 @@ var EnvHandler = function (ErrorReporter) {
|
|||
|
||||
this.resetMathMode = resetMathMode;
|
||||
|
||||
var getNewMathMode = function (currentMathMode, thisEnv) {
|
||||
var getNewMathMode = function (currentMathMode, thisDelimiter) {
|
||||
var newMathMode = null;
|
||||
|
||||
if (thisEnv.command === "{") {
|
||||
if (thisEnv.mathMode !== null) {
|
||||
newMathMode = thisEnv.mathMode;
|
||||
if (thisDelimiter.command === "{") {
|
||||
if (thisDelimiter.mathMode !== null) {
|
||||
newMathMode = thisDelimiter.mathMode;
|
||||
} else {
|
||||
newMathMode = currentMathMode;
|
||||
}
|
||||
} else if (thisEnv.command === "left") {
|
||||
} else if (thisDelimiter.command === "left") {
|
||||
if (currentMathMode === null) {
|
||||
ErrorFrom(thisEnv, "\\left can only be used in math mode", {mathMode: true});
|
||||
ErrorFrom(thisDelimiter, "\\left can only be used in math mode", {mathMode: true});
|
||||
};
|
||||
newMathMode = currentMathMode;
|
||||
} else if (thisEnv.command === "begin") {
|
||||
var name = thisEnv.name;
|
||||
} else if (thisDelimiter.command === "begin") {
|
||||
var name = thisDelimiter.name;
|
||||
if (name) {
|
||||
if (name.match(/^(document|figure|center|enumerate|itemize|table|abstract|proof|lemma|theorem|definition|proposition|corollary|remark|notation|thebibliography)$/)) {
|
||||
if (currentMathMode) {
|
||||
ErrorFromTo(currentMathMode, thisEnv, thisEnv.name + " used inside " + getName(currentMathMode),
|
||||
ErrorFromTo(currentMathMode, thisDelimiter, thisDelimiter.name + " used inside " + getName(currentMathMode),
|
||||
{suppressIfEditing:true, errorAtStart: true, mathMode: true});
|
||||
resetMathMode();
|
||||
};
|
||||
newMathMode = null;
|
||||
} else if (name.match(/^(array|gathered|split|aligned|alignedat)\*?$/)) {
|
||||
if (currentMathMode === null) {
|
||||
ErrorFrom(thisEnv, thisEnv.name + " not inside math mode", {mathMode: true});
|
||||
ErrorFrom(thisDelimiter, thisDelimiter.name + " not inside math mode", {mathMode: true});
|
||||
};
|
||||
newMathMode = currentMathMode;
|
||||
} else if (name.match(/^(math|displaymath|equation|eqnarray|multline|align|gather|flalign|alignat)\*?$/)) {
|
||||
if (currentMathMode) {
|
||||
ErrorFromTo(currentMathMode, thisEnv, thisEnv.name + " used inside " + getName(currentMathMode),
|
||||
ErrorFromTo(currentMathMode, thisDelimiter, thisDelimiter.name + " used inside " + getName(currentMathMode),
|
||||
{suppressIfEditing:true, errorAtStart: true, mathMode: true});
|
||||
resetMathMode();
|
||||
};
|
||||
newMathMode = thisEnv;
|
||||
newMathMode = thisDelimiter;
|
||||
} else {
|
||||
newMathMode = undefined; // undefined means we don't know if we are in math mode or not
|
||||
}
|
||||
|
@ -2277,41 +2397,41 @@ var EnvHandler = function (ErrorReporter) {
|
|||
return newMathMode;
|
||||
};
|
||||
|
||||
this.checkAndUpdateState = function (thisEnv) {
|
||||
this.checkAndUpdateState = function (thisDelimiter) {
|
||||
if (inVerbatim) {
|
||||
if (thisEnv.command === "end") {
|
||||
this._endVerbatim(thisEnv);
|
||||
if (thisDelimiter.command === "end") {
|
||||
this._endVerbatim(thisDelimiter);
|
||||
} else {
|
||||
return; // ignore anything in verbatim environments
|
||||
}
|
||||
} else if(thisEnv.command === "begin" || thisEnv.command === "{" || thisEnv.command === "left") {
|
||||
if (thisEnv.verbatim) {inVerbatim = true;};
|
||||
} else if(thisDelimiter.command === "begin" || thisDelimiter.command === "{" || thisDelimiter.command === "left") {
|
||||
if (thisDelimiter.verbatim) {inVerbatim = true;};
|
||||
var currentMathMode = this.getMathMode(); // undefined, null, $, $$, name of mathmode env
|
||||
var newMathMode = getNewMathMode(currentMathMode, thisEnv);
|
||||
thisEnv.mathMode = newMathMode;
|
||||
state.push(thisEnv);
|
||||
} else if (thisEnv.command === "end") {
|
||||
this._end(thisEnv);
|
||||
} else if (thisEnv.command === "(" || thisEnv.command === "[") {
|
||||
this._beginMathMode(thisEnv);
|
||||
} else if (thisEnv.command === ")" || thisEnv.command === "]") {
|
||||
this._end(thisEnv);
|
||||
} else if (thisEnv.command === "}") {
|
||||
this._end(thisEnv);
|
||||
} else if (thisEnv.command === "right") {
|
||||
this._end(thisEnv);
|
||||
} else if (thisEnv.command === "$" || thisEnv.command === "$$") {
|
||||
this._toggleMathMode(thisEnv);
|
||||
var newMathMode = getNewMathMode(currentMathMode, thisDelimiter);
|
||||
thisDelimiter.mathMode = newMathMode;
|
||||
document.openEnv(thisDelimiter);
|
||||
} else if (thisDelimiter.command === "end") {
|
||||
this._end(thisDelimiter);
|
||||
} else if (thisDelimiter.command === "(" || thisDelimiter.command === "[") {
|
||||
this._beginMathMode(thisDelimiter);
|
||||
} else if (thisDelimiter.command === ")" || thisDelimiter.command === "]") {
|
||||
this._end(thisDelimiter);
|
||||
} else if (thisDelimiter.command === "}") {
|
||||
this._end(thisDelimiter);
|
||||
} else if (thisDelimiter.command === "right") {
|
||||
this._end(thisDelimiter);
|
||||
} else if (thisDelimiter.command === "$" || thisDelimiter.command === "$$") {
|
||||
this._toggleMathMode(thisDelimiter);
|
||||
}
|
||||
};
|
||||
|
||||
this.close = function () {
|
||||
while (state.length > 0) {
|
||||
var thisEnv = state.pop();
|
||||
if (thisEnv.command === "{") {
|
||||
ErrorFrom(thisEnv, "unclosed group {", {type:"warning"});
|
||||
while (document.getDepth() > 0) {
|
||||
var thisDelimiter = document.closeEnv();
|
||||
if (thisDelimiter.command === "{") {
|
||||
ErrorFrom(thisDelimiter, "unclosed group {", {type:"warning"});
|
||||
} else {
|
||||
ErrorFrom(thisEnv, "unclosed " + getName(thisEnv));
|
||||
ErrorFrom(thisDelimiter, "unclosed " + getName(thisDelimiter));
|
||||
}
|
||||
}
|
||||
var vlen = verbatimRanges.length;
|
||||
|
@ -2331,10 +2451,10 @@ var EnvHandler = function (ErrorReporter) {
|
|||
}
|
||||
};
|
||||
|
||||
this.setEnvProps = function (env) {
|
||||
var name = env.name ;
|
||||
this.setDelimiterProps = function (delimiter) {
|
||||
var name = delimiter.name ;
|
||||
if (name && name.match(/^(verbatim|boxedverbatim|lstlisting|minted|Verbatim)$/)) {
|
||||
env.verbatim = true;
|
||||
delimiter.verbatim = true;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -2458,9 +2578,9 @@ var ErrorReporter = function (TokeniseResult) {
|
|||
errors.push(err);
|
||||
};
|
||||
|
||||
this.EnvErrorFrom = function (env, message, options) {
|
||||
this.EnvErrorFrom = function (delimiter, message, options) {
|
||||
if(!options) { options = {} ; };
|
||||
var token = env.token;
|
||||
var token = delimiter.token;
|
||||
var line = token[0], type = token[1], start = token[2], end = token[3];
|
||||
var start_col = start - linePosition[line];
|
||||
var end_col = Infinity;
|
||||
|
@ -2481,29 +2601,11 @@ var Parse = function (text) {
|
|||
var Reporter = new ErrorReporter(TokeniseResult);
|
||||
var Environments = InterpretTokens(TokeniseResult, Reporter);
|
||||
Environments.close();
|
||||
return Reporter.getErrors();
|
||||
return {
|
||||
errors: Reporter.getErrors(),
|
||||
contexts: Environments.getDocument().getContexts()
|
||||
}
|
||||
};
|
||||
|
||||
(function() {
|
||||
var disabled = false;
|
||||
|
||||
this.onUpdate = function() {
|
||||
if (disabled) { return ; };
|
||||
|
||||
var value = this.doc.getValue();
|
||||
var errors = [];
|
||||
try {
|
||||
if (value)
|
||||
errors = Parse(value);
|
||||
} catch (e) {
|
||||
disabled = true;
|
||||
errors = [];
|
||||
}
|
||||
this.sender.emit("lint", errors);
|
||||
};
|
||||
|
||||
}).call(LatexWorker.prototype);
|
||||
|
||||
});
|
||||
|
||||
ace.define("ace/lib/es5-shim",["require","exports","module"], function(require, exports, module) {
|
||||
|
|
File diff suppressed because it is too large
Load diff
BIN
services/web/public/ol-favicon.ico
Normal file
BIN
services/web/public/ol-favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 938 B |
84
services/web/public/stylesheets/_style_includes.less
Normal file
84
services/web/public/stylesheets/_style_includes.less
Normal file
|
@ -0,0 +1,84 @@
|
|||
@import url(https://netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css);
|
||||
|
||||
@import "core/mixins.less";
|
||||
|
||||
// Reset
|
||||
@import "core/normalize.less";
|
||||
@import "core/print.less";
|
||||
|
||||
// Core CSS
|
||||
@import "core/scaffolding.less";
|
||||
@import "core/type.less";
|
||||
@import "core/grid.less";
|
||||
|
||||
// Components
|
||||
@import "components/tables.less";
|
||||
@import "components/forms.less";
|
||||
@import "components/buttons.less";
|
||||
@import "components/card.less";
|
||||
//@import "components/code.less";
|
||||
@import "components/component-animations.less";
|
||||
@import "components/glyphicons.less";
|
||||
@import "components/dropdowns.less";
|
||||
@import "components/button-groups.less";
|
||||
@import "components/input-groups.less";
|
||||
@import "components/navs.less";
|
||||
@import "components/navbar.less";
|
||||
@import "components/footer.less";
|
||||
//@import "components/breadcrumbs.less";
|
||||
//@import "components/pagination.less";
|
||||
@import "components/pager.less";
|
||||
@import "components/labels.less";
|
||||
//@import "components/badges.less";
|
||||
//@import "components/jumbotron.less";
|
||||
@import "components/thumbnails.less";
|
||||
@import "components/alerts.less";
|
||||
@import "components/progress-bars.less";
|
||||
// @import "components/media.less";
|
||||
// @import "components/list-group.less";
|
||||
// @import "components/panels.less";
|
||||
// @import "components/wells.less";
|
||||
@import "components/close.less";
|
||||
@import "components/fineupload.less";
|
||||
@import "components/hover.less";
|
||||
|
||||
// Components w/ JavaScript
|
||||
@import "components/modals.less";
|
||||
@import "components/tooltip.less";
|
||||
@import "components/popovers.less";
|
||||
@import "components/carousel.less";
|
||||
|
||||
// ngTagsInput
|
||||
@import "components/tags-input.less";
|
||||
|
||||
// Utility classes
|
||||
@import "core/utilities.less";
|
||||
@import "core/responsive-utilities.less";
|
||||
|
||||
// ShareLaTeX app classes
|
||||
@import "app/base.less";
|
||||
@import "app/account-settings.less";
|
||||
@import "app/beta-program.less";
|
||||
@import "app/about-page.less";
|
||||
@import "app/project-list.less";
|
||||
@import "app/editor.less";
|
||||
@import "app/homepage.less";
|
||||
@import "app/plans.less";
|
||||
@import "app/recurly.less";
|
||||
@import "app/bonus.less";
|
||||
@import "app/register.less";
|
||||
@import "app/blog.less";
|
||||
@import "app/features.less";
|
||||
@import "app/templates.less";
|
||||
@import "app/wiki.less";
|
||||
@import "app/translations.less";
|
||||
@import "app/contact-us.less";
|
||||
@import "app/subscription.less";
|
||||
@import "app/sprites.less";
|
||||
@import "app/invite.less";
|
||||
@import "app/review-features-page.less";
|
||||
@import "app/error-pages.less";
|
||||
|
||||
@import "../js/libs/pdfListView/TextLayer.css";
|
||||
@import "../js/libs/pdfListView/AnnotationsLayer.css";
|
||||
@import "../js/libs/pdfListView/HighlightsLayer.css";
|
|
@ -72,17 +72,17 @@
|
|||
height: 100%;
|
||||
background-color: #FFF;
|
||||
}
|
||||
.loading-screen-lion-container {
|
||||
.loading-screen-brand-container {
|
||||
width: 15%;
|
||||
min-width: 200px;
|
||||
text-align: center;
|
||||
}
|
||||
.loading-screen-lion {
|
||||
.loading-screen-brand {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding-top: 86.2%;
|
||||
padding-top: @editor-loading-logo-padding-top;
|
||||
height: 0;
|
||||
background: url(/img/brand/lion-grey.svg) no-repeat bottom / 100%;
|
||||
background: @editor-loading-logo-background-url no-repeat bottom / 100%;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
|
@ -91,7 +91,7 @@
|
|||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: url(/img/brand/lion.svg) no-repeat bottom / 100%;
|
||||
background: @editor-loading-logo-foreground-url no-repeat bottom / 100%;
|
||||
transition: height .5s;
|
||||
}
|
||||
}
|
||||
|
@ -504,3 +504,4 @@
|
|||
height: auto;
|
||||
border-bottom: 1px solid @modal-header-border-color;
|
||||
}
|
||||
|
||||
|
|
|
@ -57,6 +57,15 @@
|
|||
}
|
||||
}
|
||||
|
||||
.rp-collapse-arrow() {
|
||||
display: inline-block;
|
||||
transform: rotateZ(0deg);
|
||||
transition: transform 0.15s ease;
|
||||
&-on {
|
||||
transform: rotateZ(-90deg);
|
||||
}
|
||||
}
|
||||
|
||||
.triangle(@_, @width, @height, @color) {
|
||||
position: absolute;
|
||||
border-color: transparent;
|
||||
|
@ -131,7 +140,6 @@
|
|||
}
|
||||
|
||||
position: relative;
|
||||
height: @rp-toolbar-height;
|
||||
border-bottom: 1px solid @rp-border-grey;
|
||||
background-color: @rp-bg-dim-blue;
|
||||
text-align: center;
|
||||
|
@ -144,6 +152,10 @@
|
|||
text-align: right;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.review-panel-toolbar-icon-on {
|
||||
margin-right: 5px;
|
||||
color: @red;
|
||||
}
|
||||
.review-panel-toolbar-label-disabled {
|
||||
cursor: auto;
|
||||
margin-right: 5px;
|
||||
|
@ -151,6 +163,45 @@
|
|||
.review-panel-toolbar-spinner {
|
||||
margin-left: 5px;
|
||||
}
|
||||
.rp-tc-state {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
overflow: hidden;
|
||||
list-style: none;
|
||||
padding: 0 5px;
|
||||
margin: 0;
|
||||
border-bottom: 1px solid @rp-border-grey;
|
||||
background-color: @rp-bg-dim-blue;
|
||||
text-align: left;
|
||||
}
|
||||
.rp-tc-state-collapse {
|
||||
.rp-collapse-arrow;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.rp-tc-state-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 3px 0;
|
||||
&:last-of-type {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
}
|
||||
.rp-tc-state-item-everyone {
|
||||
border-bottom: 1px solid @rp-border-grey;
|
||||
color: @red;
|
||||
}
|
||||
.rp-tc-state-item-name {
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-weight: @rp-semibold-weight;
|
||||
}
|
||||
.rp-tc-state-item-name-disabled {
|
||||
opacity: .35;
|
||||
}
|
||||
|
||||
.rp-entry-list {
|
||||
display: none;
|
||||
|
@ -629,16 +680,9 @@
|
|||
font-size: 0.9em;
|
||||
}
|
||||
.rp-overview-file-header-collapse {
|
||||
display: inline-block;
|
||||
.rp-collapse-arrow;
|
||||
float: left;
|
||||
transform: rotateZ(0deg);
|
||||
transition: transform 0.15s ease
|
||||
}
|
||||
|
||||
.rp-overview-file-header-collapse-on {
|
||||
transform: rotateZ(-90deg);
|
||||
}
|
||||
|
||||
.rp-overview-file-entries {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
@ -771,6 +815,11 @@
|
|||
background-color: #FFF;
|
||||
}
|
||||
}
|
||||
|
||||
&:disabled + .rp-toggle-btn {
|
||||
cursor: default;
|
||||
opacity: .35;
|
||||
}
|
||||
}
|
||||
|
||||
.ace-editor-wrapper {
|
||||
|
|
|
@ -386,7 +386,7 @@
|
|||
bottom: 5px;
|
||||
width: 180px;
|
||||
padding: 0;
|
||||
background-image: url('/img/brand/logo-horizontal.svg');
|
||||
background-image: @navbar-brand-image-url;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: left center;
|
||||
|
|
|
@ -225,6 +225,10 @@
|
|||
|
||||
// Hide tabbable panes to start, show them when `.active`
|
||||
.tab-content {
|
||||
background-color: @nav-tabs-active-link-hover-bg;
|
||||
border: 1px solid @nav-tabs-border-color;
|
||||
border-top: none;
|
||||
padding: @line-height-computed / 2;
|
||||
> .tab-pane {
|
||||
display: none;
|
||||
}
|
||||
|
|
805
services/web/public/stylesheets/core/_common-variables.less
Normal file
805
services/web/public/stylesheets/core/_common-variables.less
Normal file
|
@ -0,0 +1,805 @@
|
|||
//
|
||||
// Variables
|
||||
// --------------------------------------------------
|
||||
|
||||
//== Scaffolding
|
||||
//
|
||||
// ## Settings for some of the most global styles.
|
||||
|
||||
//** Background color for `<body>`.
|
||||
@body-bg: #fff;
|
||||
//** Global text color on `<body>`.
|
||||
@text-color: @gray-dark;
|
||||
|
||||
//** Global textual link color.
|
||||
@link-color: @brand-primary;
|
||||
//** Link hover color set via `darken()` function.
|
||||
@link-hover-color: darken(@link-color, 15%);
|
||||
|
||||
|
||||
//== Typography
|
||||
//
|
||||
//## Font, line-height, and color for body text, headings, and more.
|
||||
|
||||
@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);
|
||||
//@import url(https://fonts.googleapis.com/css?family=PT+Serif:400,600,700);
|
||||
//@import url(https://fonts.googleapis.com/css?family=PT+Serif:400,400i,700,700i);
|
||||
@import url(https://fonts.googleapis.com/css?family=Merriweather:400,400i,700,700i);
|
||||
|
||||
@font-family-sans-serif: "Open Sans", sans-serif;
|
||||
@font-family-serif: "Merriweather", serif;
|
||||
//** Default monospace fonts for `<code>`, `<kbd>`, and `<pre>`.
|
||||
@font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
@font-family-base: @font-family-sans-serif;
|
||||
|
||||
@font-size-base: 16px;
|
||||
@font-size-large: ceil((@font-size-base * 1.25)); // ~18px
|
||||
@font-size-small: ceil((@font-size-base * 0.85)); // ~12px
|
||||
|
||||
@font-size-h1: floor((@font-size-base * 2)); // ~36px
|
||||
@font-size-h2: floor((@font-size-base * 1.6)); // ~30px
|
||||
@font-size-h3: ceil((@font-size-base * 1.25)); // ~24px
|
||||
@font-size-h4: ceil((@font-size-base * 1.1)); // ~18px
|
||||
@font-size-h5: @font-size-base;
|
||||
@font-size-h6: ceil((@font-size-base * 0.85)); // ~12px
|
||||
|
||||
//** Unit-less `line-height` for use in components like buttons.
|
||||
@line-height-base: 1.5625; // 20/14
|
||||
//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
|
||||
@line-height-computed: floor((@font-size-base * @line-height-base)); // ~20px
|
||||
|
||||
//** By default, this inherits from the `<body>`.
|
||||
@headings-font-family: @font-family-serif;
|
||||
@headings-font-weight: 500;
|
||||
@headings-line-height: 1.1;
|
||||
@headings-color: @gray-dark;
|
||||
|
||||
|
||||
//-- Iconography
|
||||
//
|
||||
//## Specify custom locations of the include Glyphicons icon font. Useful for those including Bootstrap via Bower.
|
||||
|
||||
@icon-font-path: "../fonts/";
|
||||
@icon-font-name: "glyphicons-halflings-regular";
|
||||
@icon-font-svg-id: "glyphicons_halflingsregular";
|
||||
|
||||
//== Components
|
||||
//
|
||||
//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
|
||||
|
||||
@padding-base-vertical: 5px;
|
||||
@padding-base-horizontal: 16px;
|
||||
|
||||
@padding-large-vertical: 10px;
|
||||
@padding-large-horizontal: 16px;
|
||||
|
||||
@padding-small-vertical: 5px;
|
||||
@padding-small-horizontal: 10px;
|
||||
|
||||
@padding-xs-vertical: 1px;
|
||||
@padding-xs-horizontal: 5px;
|
||||
|
||||
@line-height-large: 1.33;
|
||||
@line-height-small: 1.5;
|
||||
|
||||
@border-radius-base: 3px;
|
||||
@border-radius-large: 5px;
|
||||
@border-radius-small: 2px;
|
||||
|
||||
//** Global color for active items (e.g., navs or dropdowns).
|
||||
@component-active-color: #fff;
|
||||
//** Global background color for active items (e.g., navs or dropdowns).
|
||||
@component-active-bg: @brand-primary;
|
||||
|
||||
//** Width of the `border` for generating carets that indicator dropdowns.
|
||||
@caret-width-base: 4px;
|
||||
//** Carets increase slightly in size for larger components.
|
||||
@caret-width-large: 5px;
|
||||
|
||||
|
||||
//== Tables
|
||||
//
|
||||
//## Customizes the `.table` component with basic values, each used across all table variations.
|
||||
|
||||
//** Padding for `<th>`s and `<td>`s.
|
||||
@table-cell-padding: 8px;
|
||||
//** Padding for cells in `.table-condensed`.
|
||||
@table-condensed-cell-padding: 5px;
|
||||
|
||||
//** Default background color used for all tables.
|
||||
@table-bg: transparent;
|
||||
//** Background color used for `.table-striped`.
|
||||
@table-bg-accent: #f9f9f9;
|
||||
//** Background color used for `.table-hover`.
|
||||
@table-bg-hover: #f5f5f5;
|
||||
@table-bg-active: @table-bg-hover;
|
||||
|
||||
//** Border color for table and cell borders.
|
||||
@table-border-color: #ddd;
|
||||
|
||||
|
||||
//== Buttons
|
||||
//
|
||||
//## For each of Bootstrap's buttons, define text, background and border color.
|
||||
|
||||
@btn-font-weight: 700;
|
||||
|
||||
@btn-default-color: #333;
|
||||
@btn-default-bg: #fff;
|
||||
@btn-default-border: #ccc;
|
||||
|
||||
@btn-primary-color: #fff;
|
||||
@btn-primary-bg: @brand-primary;
|
||||
@btn-primary-border: darken(@btn-primary-bg, 10%);
|
||||
|
||||
@btn-success-color: #fff;
|
||||
@btn-success-bg: @brand-success;
|
||||
@btn-success-border: darken(@btn-success-bg, 10%);
|
||||
|
||||
@btn-info-color: #fff;
|
||||
@btn-info-bg: @brand-info;
|
||||
@btn-info-border: darken(@btn-info-bg, 15%);
|
||||
|
||||
@btn-warning-color: #fff;
|
||||
@btn-warning-bg: @brand-warning;
|
||||
@btn-warning-border: darken(@btn-warning-bg, 10%);
|
||||
|
||||
@btn-danger-color: #fff;
|
||||
@btn-danger-bg: @brand-danger;
|
||||
@btn-danger-border: darken(@btn-danger-bg, 10%);
|
||||
|
||||
@btn-link-disabled-color: @gray-light;
|
||||
|
||||
|
||||
//== Forms
|
||||
//
|
||||
//##
|
||||
|
||||
//** `<input>` background color
|
||||
@input-bg: #fff;
|
||||
//** `<input disabled>` background color
|
||||
@input-bg-disabled: @gray-lighter;
|
||||
|
||||
//** Text color for `<input>`s
|
||||
@input-color: @gray;
|
||||
//** `<input>` border color
|
||||
@input-border: #ccc;
|
||||
//** `<input>` border radius
|
||||
@input-border-radius: @border-radius-base;
|
||||
//** Border color for inputs on focus
|
||||
@input-border-focus: #66afe9;
|
||||
|
||||
//** Placeholder text color
|
||||
@input-color-placeholder: @gray-light;
|
||||
|
||||
//** Default `.form-control` height
|
||||
@input-height-base: (@line-height-computed + (@padding-base-vertical * 2) + 2);
|
||||
//** Large `.form-control` height
|
||||
@input-height-large: (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2);
|
||||
//** Small `.form-control` height
|
||||
@input-height-small: (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2);
|
||||
|
||||
@legend-color: @gray-dark;
|
||||
@legend-border-color: #e5e5e5;
|
||||
|
||||
//** Background color for textual input addons
|
||||
@input-group-addon-bg: @gray-lighter;
|
||||
//** Border color for textual input addons
|
||||
@input-group-addon-border-color: @input-border;
|
||||
|
||||
|
||||
//== Dropdowns
|
||||
//
|
||||
//## Dropdown menu container and contents.
|
||||
|
||||
//** Background for the dropdown menu.
|
||||
@dropdown-bg: #fff;
|
||||
//** Dropdown menu `border-color`.
|
||||
@dropdown-border: rgba(0,0,0,.15);
|
||||
//** Dropdown menu `border-color` **for IE8**.
|
||||
@dropdown-fallback-border: #ccc;
|
||||
//** Divider color for between dropdown items.
|
||||
@dropdown-divider-bg: #e5e5e5;
|
||||
|
||||
//** Dropdown link text color.
|
||||
@dropdown-link-color: @gray-dark;
|
||||
//** Hover color for dropdown links.
|
||||
@dropdown-link-hover-color: #fff;
|
||||
//** Hover background for dropdown links.
|
||||
@dropdown-link-hover-bg: @brand-primary;
|
||||
|
||||
//** Active dropdown menu item text color.
|
||||
@dropdown-link-active-color: @component-active-color;
|
||||
//** Active dropdown menu item background color.
|
||||
@dropdown-link-active-bg: @component-active-bg;
|
||||
|
||||
//** Disabled dropdown menu item background color.
|
||||
@dropdown-link-disabled-color: @gray-light;
|
||||
|
||||
//** Text color for headers within dropdown menus.
|
||||
@dropdown-header-color: @gray-light;
|
||||
|
||||
// Note: Deprecated @dropdown-caret-color as of v3.1.0
|
||||
@dropdown-caret-color: #000;
|
||||
|
||||
|
||||
//-- Z-index master list
|
||||
//
|
||||
// Warning: Avoid customizing these values. They're used for a bird's eye view
|
||||
// of components dependent on the z-axis and are designed to all work together.
|
||||
//
|
||||
// Note: These variables are not generated into the Customizer.
|
||||
|
||||
@zindex-navbar: 1000;
|
||||
@zindex-dropdown: 1000;
|
||||
@zindex-popover: 1010;
|
||||
@zindex-tooltip: 1030;
|
||||
@zindex-navbar-fixed: 1030;
|
||||
@zindex-modal-background: 1040;
|
||||
@zindex-modal: 1050;
|
||||
|
||||
|
||||
//== Media queries breakpoints
|
||||
//
|
||||
//## Define the breakpoints at which your layout will change, adapting to different screen sizes.
|
||||
|
||||
// Extra small screen / phone
|
||||
// Note: Deprecated @screen-xs and @screen-phone as of v3.0.1
|
||||
@screen-xs: 480px;
|
||||
@screen-xs-min: @screen-xs;
|
||||
@screen-phone: @screen-xs-min;
|
||||
|
||||
// Small screen / tablet
|
||||
// Note: Deprecated @screen-sm and @screen-tablet as of v3.0.1
|
||||
@screen-sm: 768px;
|
||||
@screen-sm-min: @screen-sm;
|
||||
@screen-tablet: @screen-sm-min;
|
||||
|
||||
// Medium screen / desktop
|
||||
// Note: Deprecated @screen-md and @screen-desktop as of v3.0.1
|
||||
@screen-md: 992px;
|
||||
@screen-md-min: @screen-md;
|
||||
@screen-desktop: @screen-md-min;
|
||||
|
||||
// Large screen / wide desktop
|
||||
// Note: Deprecated @screen-lg and @screen-lg-desktop as of v3.0.1
|
||||
@screen-lg: 1200px;
|
||||
@screen-lg-min: @screen-lg;
|
||||
@screen-lg-desktop: @screen-lg-min;
|
||||
|
||||
// So media queries don't overlap when required, provide a maximum
|
||||
@screen-xs-max: (@screen-sm-min - 1);
|
||||
@screen-sm-max: (@screen-md-min - 1);
|
||||
@screen-md-max: (@screen-lg-min - 1);
|
||||
|
||||
|
||||
//== Grid system
|
||||
//
|
||||
//## Define your custom responsive grid.
|
||||
|
||||
//** Number of columns in the grid.
|
||||
@grid-columns: 12;
|
||||
//** Padding between columns. Gets divided in half for the left and right.
|
||||
@grid-gutter-width: 30px;
|
||||
// Navbar collapse
|
||||
//** Point at which the navbar becomes uncollapsed.
|
||||
@grid-float-breakpoint: @screen-sm-min;
|
||||
//** Point at which the navbar begins collapsing.
|
||||
@grid-float-breakpoint-max: (@grid-float-breakpoint - 1);
|
||||
|
||||
|
||||
//== Container sizes
|
||||
//
|
||||
//## Define the maximum width of `.container` for different screen sizes.
|
||||
|
||||
// Small screen / tablet
|
||||
@container-tablet: ((720px + @grid-gutter-width));
|
||||
//** For `@screen-sm-min` and up.
|
||||
@container-sm: @container-tablet;
|
||||
|
||||
// Medium screen / desktop
|
||||
@container-desktop: ((940px + @grid-gutter-width));
|
||||
//** For `@screen-md-min` and up.
|
||||
@container-md: @container-desktop;
|
||||
|
||||
// Large screen / wide desktop
|
||||
@container-large-desktop: ((1140px + @grid-gutter-width));
|
||||
//** For `@screen-lg-min` and up.
|
||||
@container-lg: @container-large-desktop;
|
||||
|
||||
|
||||
//== Navbar
|
||||
//
|
||||
//##
|
||||
|
||||
// Basics of a navbar
|
||||
@navbar-height: 60px;
|
||||
@navbar-margin-bottom: 0;
|
||||
@navbar-border-radius: 0;
|
||||
@navbar-padding-horizontal: floor((@grid-gutter-width / 2));
|
||||
@navbar-padding-vertical: ((@navbar-height - @line-height-computed) / 2);
|
||||
@navbar-collapse-max-height: 340px;
|
||||
|
||||
@navbar-default-color: #777;
|
||||
@navbar-default-bg: #fff;
|
||||
@navbar-default-border: @gray-lighter;
|
||||
|
||||
// Navbar links
|
||||
@navbar-default-link-color: @link-color;
|
||||
@navbar-default-link-hover-color: @link-hover-color;
|
||||
@navbar-default-link-hover-bg: @link-hover-color;
|
||||
@navbar-default-link-active-color: #fff;
|
||||
@navbar-default-link-active-bg: @link-hover-color;
|
||||
@navbar-default-link-disabled-color: #ccc;
|
||||
@navbar-default-link-disabled-bg: transparent;
|
||||
|
||||
// Navbar brand label
|
||||
@navbar-default-brand-color: @navbar-default-link-color;
|
||||
@navbar-default-brand-hover-color: darken(@navbar-default-brand-color, 10%);
|
||||
@navbar-default-brand-hover-bg: transparent;
|
||||
|
||||
// Navbar toggle
|
||||
@navbar-default-toggle-hover-bg: @link-hover-color;
|
||||
@navbar-default-toggle-border-color: @link-color;
|
||||
|
||||
//== Navs
|
||||
//
|
||||
//##
|
||||
|
||||
//=== Shared nav styles
|
||||
@nav-link-padding: 10px 15px;
|
||||
@nav-link-hover-bg: @link-color;
|
||||
|
||||
@nav-disabled-link-color: @gray-light;
|
||||
@nav-disabled-link-hover-color: @gray-light;
|
||||
|
||||
@nav-open-link-hover-color: #fff;
|
||||
|
||||
//== Tabs
|
||||
@nav-tabs-border-color: #ddd;
|
||||
|
||||
@nav-tabs-link-hover-border-color: @link-color;
|
||||
|
||||
@nav-tabs-active-link-hover-bg: @body-bg;
|
||||
@nav-tabs-active-link-hover-color: @gray;
|
||||
@nav-tabs-active-link-hover-border-color: #ddd;
|
||||
|
||||
@nav-tabs-justified-link-border-color: #ddd;
|
||||
@nav-tabs-justified-active-link-border-color: @body-bg;
|
||||
|
||||
//== Pills
|
||||
@nav-pills-border-radius: @border-radius-base;
|
||||
@nav-pills-active-link-hover-bg: @component-active-bg;
|
||||
@nav-pills-active-link-hover-color: @component-active-color;
|
||||
|
||||
|
||||
//== Pagination
|
||||
//
|
||||
//##
|
||||
|
||||
@pagination-color: @link-color;
|
||||
@pagination-bg: #fff;
|
||||
@pagination-border: #ddd;
|
||||
|
||||
@pagination-hover-color: @link-hover-color;
|
||||
@pagination-hover-bg: @gray-lighter;
|
||||
@pagination-hover-border: #ddd;
|
||||
|
||||
@pagination-active-color: #fff;
|
||||
@pagination-active-bg: @brand-primary;
|
||||
@pagination-active-border: @brand-primary;
|
||||
|
||||
@pagination-disabled-color: @gray-light;
|
||||
@pagination-disabled-bg: #fff;
|
||||
@pagination-disabled-border: #ddd;
|
||||
|
||||
|
||||
//== Pager
|
||||
//
|
||||
//##
|
||||
|
||||
@pager-bg: @pagination-bg;
|
||||
@pager-border: @pagination-border;
|
||||
@pager-border-radius: 15px;
|
||||
|
||||
@pager-hover-bg: @pagination-hover-bg;
|
||||
|
||||
@pager-active-bg: @pagination-active-bg;
|
||||
@pager-active-color: @pagination-active-color;
|
||||
|
||||
@pager-disabled-color: @pagination-disabled-color;
|
||||
|
||||
|
||||
//== Jumbotron
|
||||
//
|
||||
//##
|
||||
|
||||
@jumbotron-padding: 30px;
|
||||
@jumbotron-color: inherit;
|
||||
@jumbotron-bg: @gray-lighter;
|
||||
@jumbotron-heading-color: inherit;
|
||||
@jumbotron-font-size: ceil((@font-size-base * 1.5));
|
||||
|
||||
|
||||
//== Form states and alerts
|
||||
//
|
||||
//## Define colors for form feedback states and, by default, alerts.
|
||||
|
||||
@state-success-text: darken(@brand-success, 20%);
|
||||
@state-success-bg: lighten(@brand-success, 50%);
|
||||
@state-success-border: darken(@brand-success, 5%);
|
||||
|
||||
@state-info-text: darken(@brand-info, 20%);
|
||||
@state-info-bg: lighten(@brand-info, 47%);
|
||||
@state-info-border: darken(@brand-info, 7%);
|
||||
|
||||
@state-warning-text: darken(@brand-warning, 10%);
|
||||
@state-warning-bg: lighten(@brand-warning, 45%);
|
||||
@state-warning-border: @brand-warning;
|
||||
|
||||
@state-danger-text: darken(@brand-danger, 10%);
|
||||
@state-danger-bg: lighten(@brand-danger, 50%);
|
||||
@state-danger-border: darken(@brand-danger, 5%);
|
||||
|
||||
|
||||
//== Tooltips
|
||||
//
|
||||
//##
|
||||
|
||||
//** Tooltip max width
|
||||
@tooltip-max-width: 200px;
|
||||
//** Tooltip text color
|
||||
@tooltip-color: #fff;
|
||||
//** Tooltip background color
|
||||
@tooltip-bg: #000;
|
||||
@tooltip-opacity: .9;
|
||||
|
||||
//** Tooltip arrow width
|
||||
@tooltip-arrow-width: 5px;
|
||||
//** Tooltip arrow color
|
||||
@tooltip-arrow-color: @tooltip-bg;
|
||||
|
||||
|
||||
//== Popovers
|
||||
//
|
||||
//##
|
||||
|
||||
//** Popover body background color
|
||||
@popover-bg: #fff;
|
||||
//** Popover maximum width
|
||||
@popover-max-width: 276px;
|
||||
//** Popover border color
|
||||
@popover-border-color: rgba(0,0,0,.2);
|
||||
//** Popover fallback border color
|
||||
@popover-fallback-border-color: #ccc;
|
||||
|
||||
//** Popover title background color
|
||||
@popover-title-bg: darken(@popover-bg, 3%);
|
||||
|
||||
//** Popover arrow width
|
||||
@popover-arrow-width: 10px;
|
||||
//** Popover arrow color
|
||||
@popover-arrow-color: #fff;
|
||||
|
||||
//** Popover outer arrow width
|
||||
@popover-arrow-outer-width: (@popover-arrow-width + 1);
|
||||
//** Popover outer arrow color
|
||||
@popover-arrow-outer-color: fadein(@popover-border-color, 5%);
|
||||
//** Popover outer arrow fallback color
|
||||
@popover-arrow-outer-fallback-color: darken(@popover-fallback-border-color, 20%);
|
||||
|
||||
|
||||
//== Labels
|
||||
//
|
||||
//##
|
||||
|
||||
//** Default label background color
|
||||
@label-default-bg: @gray-light;
|
||||
//** Primary label background color
|
||||
@label-primary-bg: @brand-primary;
|
||||
//** Success label background color
|
||||
@label-success-bg: @brand-success;
|
||||
//** Info label background color
|
||||
@label-info-bg: @brand-info;
|
||||
//** Warning label background color
|
||||
@label-warning-bg: @brand-warning;
|
||||
//** Danger label background color
|
||||
@label-danger-bg: @brand-danger;
|
||||
|
||||
//** Default label text color
|
||||
@label-color: #fff;
|
||||
//** Default text color of a linked label
|
||||
@label-link-hover-color: #fff;
|
||||
|
||||
|
||||
//== Modals
|
||||
//
|
||||
//##
|
||||
|
||||
//** Padding applied to the modal body
|
||||
@modal-inner-padding: 20px;
|
||||
|
||||
//** Padding applied to the modal title
|
||||
@modal-title-padding: 15px;
|
||||
//** Modal title line-height
|
||||
@modal-title-line-height: @line-height-base;
|
||||
|
||||
//** Background color of modal content area
|
||||
@modal-content-bg: #fff;
|
||||
//** Modal content border color
|
||||
@modal-content-border-color: rgba(0,0,0,.2);
|
||||
//** Modal content border color **for IE8**
|
||||
@modal-content-fallback-border-color: #999;
|
||||
|
||||
//** Modal backdrop background color
|
||||
@modal-backdrop-bg: #000;
|
||||
//** Modal backdrop opacity
|
||||
@modal-backdrop-opacity: .5;
|
||||
//** Modal header border color
|
||||
@modal-header-border-color: #e5e5e5;
|
||||
//** Modal footer border color
|
||||
@modal-footer-border-color: @modal-header-border-color;
|
||||
@modal-footer-background-color: @gray-lightest;
|
||||
|
||||
@modal-lg: 900px;
|
||||
@modal-md: 600px;
|
||||
@modal-sm: 300px;
|
||||
|
||||
|
||||
//== Alerts
|
||||
//
|
||||
//## Define alert colors, border radius, and padding.
|
||||
|
||||
@alert-padding: 15px;
|
||||
@alert-border-radius: @border-radius-base;
|
||||
@alert-link-font-weight: bold;
|
||||
|
||||
@alert-success-bg: @state-success-bg;
|
||||
@alert-success-text: @state-success-text;
|
||||
@alert-success-border: @state-success-border;
|
||||
|
||||
@alert-info-bg: @state-info-bg;
|
||||
@alert-info-text: @state-info-text;
|
||||
@alert-info-border: @state-info-border;
|
||||
|
||||
@alert-warning-bg: @state-warning-bg;
|
||||
@alert-warning-text: @state-warning-text;
|
||||
@alert-warning-border: @state-warning-border;
|
||||
|
||||
@alert-danger-bg: @state-danger-bg;
|
||||
@alert-danger-text: @state-danger-text;
|
||||
@alert-danger-border: @state-danger-border;
|
||||
|
||||
|
||||
//== Progress bars
|
||||
//
|
||||
//##
|
||||
|
||||
//** Background color of the whole progress component
|
||||
@progress-bg: white;
|
||||
@progress-border-color: @gray-lighter;
|
||||
//** Progress bar text color
|
||||
@progress-bar-color: #fff;
|
||||
|
||||
//** Default progress bar color
|
||||
@progress-bar-bg: @brand-primary;
|
||||
//** Success progress bar color
|
||||
@progress-bar-success-bg: @brand-success;
|
||||
//** Warning progress bar color
|
||||
@progress-bar-warning-bg: @brand-warning;
|
||||
//** Danger progress bar color
|
||||
@progress-bar-danger-bg: @brand-danger;
|
||||
//** Info progress bar color
|
||||
@progress-bar-info-bg: @brand-info;
|
||||
|
||||
|
||||
//== List group
|
||||
//
|
||||
//##
|
||||
|
||||
//** Background color on `.list-group-item`
|
||||
@list-group-bg: #fff;
|
||||
//** `.list-group-item` border color
|
||||
@list-group-border: #ddd;
|
||||
//** List group border radius
|
||||
@list-group-border-radius: @border-radius-base;
|
||||
|
||||
//** Background color of single list elements on hover
|
||||
@list-group-hover-bg: #f5f5f5;
|
||||
//** Text color of active list elements
|
||||
@list-group-active-color: @component-active-color;
|
||||
//** Background color of active list elements
|
||||
@list-group-active-bg: @component-active-bg;
|
||||
//** Border color of active list elements
|
||||
@list-group-active-border: @list-group-active-bg;
|
||||
@list-group-active-text-color: lighten(@list-group-active-bg, 40%);
|
||||
|
||||
@list-group-link-color: #555;
|
||||
@list-group-link-heading-color: #333;
|
||||
|
||||
|
||||
//== Panels
|
||||
//
|
||||
//##
|
||||
|
||||
@panel-bg: #fff;
|
||||
@panel-body-padding: 15px;
|
||||
@panel-border-radius: @border-radius-base;
|
||||
|
||||
//** Border color for elements within panels
|
||||
@panel-inner-border: #ddd;
|
||||
@panel-footer-bg: #f5f5f5;
|
||||
|
||||
@panel-default-text: @gray-dark;
|
||||
@panel-default-border: #ddd;
|
||||
@panel-default-heading-bg: #f5f5f5;
|
||||
|
||||
@panel-primary-text: #fff;
|
||||
@panel-primary-border: @brand-primary;
|
||||
@panel-primary-heading-bg: @brand-primary;
|
||||
|
||||
@panel-success-text: @state-success-text;
|
||||
@panel-success-border: @state-success-border;
|
||||
@panel-success-heading-bg: @state-success-bg;
|
||||
|
||||
@panel-info-text: @state-info-text;
|
||||
@panel-info-border: @state-info-border;
|
||||
@panel-info-heading-bg: @state-info-bg;
|
||||
|
||||
@panel-warning-text: @state-warning-text;
|
||||
@panel-warning-border: @state-warning-border;
|
||||
@panel-warning-heading-bg: @state-warning-bg;
|
||||
|
||||
@panel-danger-text: @state-danger-text;
|
||||
@panel-danger-border: @state-danger-border;
|
||||
@panel-danger-heading-bg: @state-danger-bg;
|
||||
|
||||
|
||||
//== Thumbnails
|
||||
//
|
||||
//##
|
||||
|
||||
//** Padding around the thumbnail image
|
||||
@thumbnail-padding: 4px;
|
||||
//** Thumbnail background color
|
||||
@thumbnail-bg: @body-bg;
|
||||
//** Thumbnail border color
|
||||
@thumbnail-border: #ddd;
|
||||
//** Thumbnail border radius
|
||||
@thumbnail-border-radius: @border-radius-base;
|
||||
|
||||
//** Custom text color for thumbnail captions
|
||||
@thumbnail-caption-color: @text-color;
|
||||
//** Padding around the thumbnail caption
|
||||
@thumbnail-caption-padding: 9px;
|
||||
|
||||
|
||||
//== Wells
|
||||
//
|
||||
//##
|
||||
|
||||
@well-bg: #f5f5f5;
|
||||
@well-border: darken(@well-bg, 7%);
|
||||
|
||||
|
||||
//== Badges
|
||||
//
|
||||
//##
|
||||
|
||||
@badge-color: #fff;
|
||||
//** Linked badge text color on hover
|
||||
@badge-link-hover-color: #fff;
|
||||
@badge-bg: @gray-light;
|
||||
|
||||
//** Badge text color in active nav link
|
||||
@badge-active-color: @link-color;
|
||||
//** Badge background color in active nav link
|
||||
@badge-active-bg: #fff;
|
||||
|
||||
@badge-font-weight: bold;
|
||||
@badge-line-height: 1;
|
||||
@badge-border-radius: 10px;
|
||||
|
||||
|
||||
//== Breadcrumbs
|
||||
//
|
||||
//##
|
||||
|
||||
@breadcrumb-padding-vertical: 8px;
|
||||
@breadcrumb-padding-horizontal: 15px;
|
||||
//** Breadcrumb background color
|
||||
@breadcrumb-bg: #f5f5f5;
|
||||
//** Breadcrumb text color
|
||||
@breadcrumb-color: #ccc;
|
||||
//** Text color of current page in the breadcrumb
|
||||
@breadcrumb-active-color: @gray-light;
|
||||
//** Textual separator for between breadcrumb elements
|
||||
@breadcrumb-separator: "/";
|
||||
|
||||
|
||||
//== Carousel
|
||||
//
|
||||
//##
|
||||
|
||||
@carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6);
|
||||
|
||||
@carousel-control-color: #fff;
|
||||
@carousel-control-width: 15%;
|
||||
@carousel-control-opacity: .5;
|
||||
@carousel-control-font-size: 20px;
|
||||
|
||||
@carousel-indicator-active-bg: #fff;
|
||||
@carousel-indicator-border-color: #fff;
|
||||
|
||||
@carousel-caption-color: #fff;
|
||||
|
||||
|
||||
//== Close
|
||||
//
|
||||
//##
|
||||
|
||||
@close-font-weight: bold;
|
||||
@close-color: #000;
|
||||
@close-text-shadow: 0 1px 0 #fff;
|
||||
|
||||
|
||||
//== Code
|
||||
//
|
||||
//##
|
||||
|
||||
@code-color: #c7254e;
|
||||
@code-bg: #f9f2f4;
|
||||
|
||||
@kbd-color: #fff;
|
||||
@kbd-bg: #333;
|
||||
|
||||
@pre-bg: #f5f5f5;
|
||||
@pre-color: @gray-dark;
|
||||
@pre-border-color: #ccc;
|
||||
@pre-scrollable-max-height: 340px;
|
||||
|
||||
|
||||
//== Type
|
||||
//
|
||||
//##
|
||||
|
||||
//** Text muted color
|
||||
@text-muted: @gray-light;
|
||||
//** Abbreviations and acronyms border color
|
||||
@abbr-border-color: @gray-light;
|
||||
//** Headings small color
|
||||
@headings-small-color: @gray-light;
|
||||
//** Blockquote small color
|
||||
@blockquote-small-color: @gray;
|
||||
//** Blockquote font size
|
||||
@blockquote-font-size: (@font-size-base * 1.125);
|
||||
//** Blockquote border color
|
||||
@blockquote-border-color: @gray-lighter;
|
||||
//** Page header border color
|
||||
@page-header-border-color: @gray-lighter;
|
||||
|
||||
|
||||
//== Miscellaneous
|
||||
//
|
||||
//##
|
||||
|
||||
//** Horizontal line color.
|
||||
@hr-border: @gray-lighter;
|
||||
|
||||
//** Horizontal offset for forms and lists.
|
||||
@component-offset-horizontal: 180px;
|
||||
|
||||
@content-margin-top: @line-height-computed;
|
||||
@content-margin-top: @line-height-computed;
|
||||
|
||||
// Custom
|
||||
|
||||
@left-menu-width: 260px;
|
||||
@left-menu-animation-duration: 0.35s;
|
||||
|
||||
@toolbar-border-color: @gray-lighter;
|
||||
@file-tree-droppable-background-color: rgb(252, 231, 199);
|
||||
|
||||
@editor-dark-background-color: #333;
|
||||
@editor-dark-toolbar-border-color: #222;
|
||||
@editor-dark-highlight-color: #FFA03A;
|
37
services/web/public/stylesheets/core/ol-variables.less
Normal file
37
services/web/public/stylesheets/core/ol-variables.less
Normal file
|
@ -0,0 +1,37 @@
|
|||
@ol-green: #4A9F48;
|
||||
@ol-dark-green: #1C5B26;
|
||||
|
||||
//== Colors
|
||||
//
|
||||
//## Gray and brand colors for use across Bootstrap.
|
||||
|
||||
|
||||
@gray-darker: #252525;
|
||||
@gray-dark: #505050;
|
||||
@gray: #7a7a7a;
|
||||
@gray-light: #a4a4a4;
|
||||
@gray-lighter: #cfcfcf;
|
||||
@gray-lightest: #f0f0f0;
|
||||
|
||||
@blue: #405ebf;
|
||||
@blueDark: #040D2D;
|
||||
@green: #46a546;
|
||||
@red: #a93529;
|
||||
@yellow: #A1A729;
|
||||
@orange: #f89406;
|
||||
@pink: #c3325f;
|
||||
@purple: #7a43b6;
|
||||
|
||||
@brand-primary: @ol-green;
|
||||
@brand-success: @green;
|
||||
@brand-info: @ol-dark-green;
|
||||
@brand-warning: @orange;
|
||||
@brand-danger: #E03A06;
|
||||
|
||||
@navbar-brand-image-url: url(/img/ol-brand/logo-horizontal.png);
|
||||
|
||||
@editor-loading-logo-padding-top: 115.44%;
|
||||
@editor-loading-logo-background-url: url(/img/ol-brand/overleaf-o-grey.svg);
|
||||
@editor-loading-logo-foreground-url: url(/img/ol-brand/overleaf-o.svg);
|
||||
|
||||
@import "./_common-variables.less";
|
|
@ -1,8 +1,3 @@
|
|||
//
|
||||
// Variables
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
//== Colors
|
||||
//
|
||||
//## Gray and brand colors for use across Bootstrap.
|
||||
|
@ -30,804 +25,10 @@
|
|||
@brand-warning: @orange;
|
||||
@brand-danger: #E03A06;
|
||||
|
||||
//== Scaffolding
|
||||
//
|
||||
// ## Settings for some of the most global styles.
|
||||
@navbar-brand-image-url: url(/img/brand/logo-horizontal.svg);
|
||||
|
||||
//** Background color for `<body>`.
|
||||
@body-bg: #fff;
|
||||
//** Global text color on `<body>`.
|
||||
@text-color: @gray-dark;
|
||||
@editor-loading-logo-padding-top: 86.2%;
|
||||
@editor-loading-logo-background-url: url(/img/brand/lion-grey.svg);
|
||||
@editor-loading-logo-foreground-url: url(/img/brand/lion.svg);
|
||||
|
||||
//** Global textual link color.
|
||||
@link-color: @brand-primary;
|
||||
//** Link hover color set via `darken()` function.
|
||||
@link-hover-color: darken(@link-color, 15%);
|
||||
|
||||
|
||||
//== Typography
|
||||
//
|
||||
//## Font, line-height, and color for body text, headings, and more.
|
||||
|
||||
@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);
|
||||
//@import url(https://fonts.googleapis.com/css?family=PT+Serif:400,600,700);
|
||||
//@import url(https://fonts.googleapis.com/css?family=PT+Serif:400,400i,700,700i);
|
||||
@import url(https://fonts.googleapis.com/css?family=Merriweather:400,400i,700,700i);
|
||||
|
||||
@font-family-sans-serif: "Open Sans", sans-serif;
|
||||
@font-family-serif: "Merriweather", serif;
|
||||
//** Default monospace fonts for `<code>`, `<kbd>`, and `<pre>`.
|
||||
@font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
@font-family-base: @font-family-sans-serif;
|
||||
|
||||
@font-size-base: 16px;
|
||||
@font-size-large: ceil((@font-size-base * 1.25)); // ~18px
|
||||
@font-size-small: ceil((@font-size-base * 0.85)); // ~12px
|
||||
|
||||
@font-size-h1: floor((@font-size-base * 2)); // ~36px
|
||||
@font-size-h2: floor((@font-size-base * 1.6)); // ~30px
|
||||
@font-size-h3: ceil((@font-size-base * 1.25)); // ~24px
|
||||
@font-size-h4: ceil((@font-size-base * 1.1)); // ~18px
|
||||
@font-size-h5: @font-size-base;
|
||||
@font-size-h6: ceil((@font-size-base * 0.85)); // ~12px
|
||||
|
||||
//** Unit-less `line-height` for use in components like buttons.
|
||||
@line-height-base: 1.5625; // 20/14
|
||||
//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
|
||||
@line-height-computed: floor((@font-size-base * @line-height-base)); // ~20px
|
||||
|
||||
//** By default, this inherits from the `<body>`.
|
||||
@headings-font-family: @font-family-serif;
|
||||
@headings-font-weight: 500;
|
||||
@headings-line-height: 1.1;
|
||||
@headings-color: @gray-dark;
|
||||
|
||||
|
||||
//-- Iconography
|
||||
//
|
||||
//## Specify custom locations of the include Glyphicons icon font. Useful for those including Bootstrap via Bower.
|
||||
|
||||
@icon-font-path: "../fonts/";
|
||||
@icon-font-name: "glyphicons-halflings-regular";
|
||||
@icon-font-svg-id: "glyphicons_halflingsregular";
|
||||
|
||||
//== Components
|
||||
//
|
||||
//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
|
||||
|
||||
@padding-base-vertical: 5px;
|
||||
@padding-base-horizontal: 16px;
|
||||
|
||||
@padding-large-vertical: 10px;
|
||||
@padding-large-horizontal: 16px;
|
||||
|
||||
@padding-small-vertical: 5px;
|
||||
@padding-small-horizontal: 10px;
|
||||
|
||||
@padding-xs-vertical: 1px;
|
||||
@padding-xs-horizontal: 5px;
|
||||
|
||||
@line-height-large: 1.33;
|
||||
@line-height-small: 1.5;
|
||||
|
||||
@border-radius-base: 3px;
|
||||
@border-radius-large: 5px;
|
||||
@border-radius-small: 2px;
|
||||
|
||||
//** Global color for active items (e.g., navs or dropdowns).
|
||||
@component-active-color: #fff;
|
||||
//** Global background color for active items (e.g., navs or dropdowns).
|
||||
@component-active-bg: @brand-primary;
|
||||
|
||||
//** Width of the `border` for generating carets that indicator dropdowns.
|
||||
@caret-width-base: 4px;
|
||||
//** Carets increase slightly in size for larger components.
|
||||
@caret-width-large: 5px;
|
||||
|
||||
|
||||
//== Tables
|
||||
//
|
||||
//## Customizes the `.table` component with basic values, each used across all table variations.
|
||||
|
||||
//** Padding for `<th>`s and `<td>`s.
|
||||
@table-cell-padding: 8px;
|
||||
//** Padding for cells in `.table-condensed`.
|
||||
@table-condensed-cell-padding: 5px;
|
||||
|
||||
//** Default background color used for all tables.
|
||||
@table-bg: transparent;
|
||||
//** Background color used for `.table-striped`.
|
||||
@table-bg-accent: #f9f9f9;
|
||||
//** Background color used for `.table-hover`.
|
||||
@table-bg-hover: #f5f5f5;
|
||||
@table-bg-active: @table-bg-hover;
|
||||
|
||||
//** Border color for table and cell borders.
|
||||
@table-border-color: #ddd;
|
||||
|
||||
|
||||
//== Buttons
|
||||
//
|
||||
//## For each of Bootstrap's buttons, define text, background and border color.
|
||||
|
||||
@btn-font-weight: 700;
|
||||
|
||||
@btn-default-color: #333;
|
||||
@btn-default-bg: #fff;
|
||||
@btn-default-border: #ccc;
|
||||
|
||||
@btn-primary-color: #fff;
|
||||
@btn-primary-bg: @brand-primary;
|
||||
@btn-primary-border: darken(@btn-primary-bg, 10%);
|
||||
|
||||
@btn-success-color: #fff;
|
||||
@btn-success-bg: @brand-success;
|
||||
@btn-success-border: darken(@btn-success-bg, 10%);
|
||||
|
||||
@btn-info-color: #fff;
|
||||
@btn-info-bg: @brand-info;
|
||||
@btn-info-border: darken(@btn-info-bg, 15%);
|
||||
|
||||
@btn-warning-color: #fff;
|
||||
@btn-warning-bg: @brand-warning;
|
||||
@btn-warning-border: darken(@btn-warning-bg, 10%);
|
||||
|
||||
@btn-danger-color: #fff;
|
||||
@btn-danger-bg: @brand-danger;
|
||||
@btn-danger-border: darken(@btn-danger-bg, 10%);
|
||||
|
||||
@btn-link-disabled-color: @gray-light;
|
||||
|
||||
|
||||
//== Forms
|
||||
//
|
||||
//##
|
||||
|
||||
//** `<input>` background color
|
||||
@input-bg: #fff;
|
||||
//** `<input disabled>` background color
|
||||
@input-bg-disabled: @gray-lighter;
|
||||
|
||||
//** Text color for `<input>`s
|
||||
@input-color: @gray;
|
||||
//** `<input>` border color
|
||||
@input-border: #ccc;
|
||||
//** `<input>` border radius
|
||||
@input-border-radius: @border-radius-base;
|
||||
//** Border color for inputs on focus
|
||||
@input-border-focus: #66afe9;
|
||||
|
||||
//** Placeholder text color
|
||||
@input-color-placeholder: @gray-light;
|
||||
|
||||
//** Default `.form-control` height
|
||||
@input-height-base: (@line-height-computed + (@padding-base-vertical * 2) + 2);
|
||||
//** Large `.form-control` height
|
||||
@input-height-large: (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2);
|
||||
//** Small `.form-control` height
|
||||
@input-height-small: (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2);
|
||||
|
||||
@legend-color: @gray-dark;
|
||||
@legend-border-color: #e5e5e5;
|
||||
|
||||
//** Background color for textual input addons
|
||||
@input-group-addon-bg: @gray-lighter;
|
||||
//** Border color for textual input addons
|
||||
@input-group-addon-border-color: @input-border;
|
||||
|
||||
|
||||
//== Dropdowns
|
||||
//
|
||||
//## Dropdown menu container and contents.
|
||||
|
||||
//** Background for the dropdown menu.
|
||||
@dropdown-bg: #fff;
|
||||
//** Dropdown menu `border-color`.
|
||||
@dropdown-border: rgba(0,0,0,.15);
|
||||
//** Dropdown menu `border-color` **for IE8**.
|
||||
@dropdown-fallback-border: #ccc;
|
||||
//** Divider color for between dropdown items.
|
||||
@dropdown-divider-bg: #e5e5e5;
|
||||
|
||||
//** Dropdown link text color.
|
||||
@dropdown-link-color: @gray-dark;
|
||||
//** Hover color for dropdown links.
|
||||
@dropdown-link-hover-color: #fff;
|
||||
//** Hover background for dropdown links.
|
||||
@dropdown-link-hover-bg: @brand-primary;
|
||||
|
||||
//** Active dropdown menu item text color.
|
||||
@dropdown-link-active-color: @component-active-color;
|
||||
//** Active dropdown menu item background color.
|
||||
@dropdown-link-active-bg: @component-active-bg;
|
||||
|
||||
//** Disabled dropdown menu item background color.
|
||||
@dropdown-link-disabled-color: @gray-light;
|
||||
|
||||
//** Text color for headers within dropdown menus.
|
||||
@dropdown-header-color: @gray-light;
|
||||
|
||||
// Note: Deprecated @dropdown-caret-color as of v3.1.0
|
||||
@dropdown-caret-color: #000;
|
||||
|
||||
|
||||
//-- Z-index master list
|
||||
//
|
||||
// Warning: Avoid customizing these values. They're used for a bird's eye view
|
||||
// of components dependent on the z-axis and are designed to all work together.
|
||||
//
|
||||
// Note: These variables are not generated into the Customizer.
|
||||
|
||||
@zindex-navbar: 1000;
|
||||
@zindex-dropdown: 1000;
|
||||
@zindex-popover: 1010;
|
||||
@zindex-tooltip: 1030;
|
||||
@zindex-navbar-fixed: 1030;
|
||||
@zindex-modal-background: 1040;
|
||||
@zindex-modal: 1050;
|
||||
|
||||
|
||||
//== Media queries breakpoints
|
||||
//
|
||||
//## Define the breakpoints at which your layout will change, adapting to different screen sizes.
|
||||
|
||||
// Extra small screen / phone
|
||||
// Note: Deprecated @screen-xs and @screen-phone as of v3.0.1
|
||||
@screen-xs: 480px;
|
||||
@screen-xs-min: @screen-xs;
|
||||
@screen-phone: @screen-xs-min;
|
||||
|
||||
// Small screen / tablet
|
||||
// Note: Deprecated @screen-sm and @screen-tablet as of v3.0.1
|
||||
@screen-sm: 768px;
|
||||
@screen-sm-min: @screen-sm;
|
||||
@screen-tablet: @screen-sm-min;
|
||||
|
||||
// Medium screen / desktop
|
||||
// Note: Deprecated @screen-md and @screen-desktop as of v3.0.1
|
||||
@screen-md: 992px;
|
||||
@screen-md-min: @screen-md;
|
||||
@screen-desktop: @screen-md-min;
|
||||
|
||||
// Large screen / wide desktop
|
||||
// Note: Deprecated @screen-lg and @screen-lg-desktop as of v3.0.1
|
||||
@screen-lg: 1200px;
|
||||
@screen-lg-min: @screen-lg;
|
||||
@screen-lg-desktop: @screen-lg-min;
|
||||
|
||||
// So media queries don't overlap when required, provide a maximum
|
||||
@screen-xs-max: (@screen-sm-min - 1);
|
||||
@screen-sm-max: (@screen-md-min - 1);
|
||||
@screen-md-max: (@screen-lg-min - 1);
|
||||
|
||||
|
||||
//== Grid system
|
||||
//
|
||||
//## Define your custom responsive grid.
|
||||
|
||||
//** Number of columns in the grid.
|
||||
@grid-columns: 12;
|
||||
//** Padding between columns. Gets divided in half for the left and right.
|
||||
@grid-gutter-width: 30px;
|
||||
// Navbar collapse
|
||||
//** Point at which the navbar becomes uncollapsed.
|
||||
@grid-float-breakpoint: @screen-sm-min;
|
||||
//** Point at which the navbar begins collapsing.
|
||||
@grid-float-breakpoint-max: (@grid-float-breakpoint - 1);
|
||||
|
||||
|
||||
//== Container sizes
|
||||
//
|
||||
//## Define the maximum width of `.container` for different screen sizes.
|
||||
|
||||
// Small screen / tablet
|
||||
@container-tablet: ((720px + @grid-gutter-width));
|
||||
//** For `@screen-sm-min` and up.
|
||||
@container-sm: @container-tablet;
|
||||
|
||||
// Medium screen / desktop
|
||||
@container-desktop: ((940px + @grid-gutter-width));
|
||||
//** For `@screen-md-min` and up.
|
||||
@container-md: @container-desktop;
|
||||
|
||||
// Large screen / wide desktop
|
||||
@container-large-desktop: ((1140px + @grid-gutter-width));
|
||||
//** For `@screen-lg-min` and up.
|
||||
@container-lg: @container-large-desktop;
|
||||
|
||||
|
||||
//== Navbar
|
||||
//
|
||||
//##
|
||||
|
||||
// Basics of a navbar
|
||||
@navbar-height: 60px;
|
||||
@navbar-margin-bottom: 0;
|
||||
@navbar-border-radius: 0;
|
||||
@navbar-padding-horizontal: floor((@grid-gutter-width / 2));
|
||||
@navbar-padding-vertical: ((@navbar-height - @line-height-computed) / 2);
|
||||
@navbar-collapse-max-height: 340px;
|
||||
|
||||
@navbar-default-color: #777;
|
||||
@navbar-default-bg: #fff;
|
||||
@navbar-default-border: @gray-lighter;
|
||||
|
||||
// Navbar links
|
||||
@navbar-default-link-color: @link-color;
|
||||
@navbar-default-link-hover-color: @link-hover-color;
|
||||
@navbar-default-link-hover-bg: @link-hover-color;
|
||||
@navbar-default-link-active-color: #fff;
|
||||
@navbar-default-link-active-bg: @link-hover-color;
|
||||
@navbar-default-link-disabled-color: #ccc;
|
||||
@navbar-default-link-disabled-bg: transparent;
|
||||
|
||||
// Navbar brand label
|
||||
@navbar-default-brand-color: @navbar-default-link-color;
|
||||
@navbar-default-brand-hover-color: darken(@navbar-default-brand-color, 10%);
|
||||
@navbar-default-brand-hover-bg: transparent;
|
||||
|
||||
// Navbar toggle
|
||||
@navbar-default-toggle-hover-bg: @link-hover-color;
|
||||
@navbar-default-toggle-border-color: @link-color;
|
||||
|
||||
//== Navs
|
||||
//
|
||||
//##
|
||||
|
||||
//=== Shared nav styles
|
||||
@nav-link-padding: 10px 15px;
|
||||
@nav-link-hover-bg: @link-color;
|
||||
|
||||
@nav-disabled-link-color: @gray-light;
|
||||
@nav-disabled-link-hover-color: @gray-light;
|
||||
|
||||
@nav-open-link-hover-color: #fff;
|
||||
|
||||
//== Tabs
|
||||
@nav-tabs-border-color: #ddd;
|
||||
|
||||
@nav-tabs-link-hover-border-color: @link-color;
|
||||
|
||||
@nav-tabs-active-link-hover-bg: @body-bg;
|
||||
@nav-tabs-active-link-hover-color: @gray;
|
||||
@nav-tabs-active-link-hover-border-color: #ddd;
|
||||
|
||||
@nav-tabs-justified-link-border-color: #ddd;
|
||||
@nav-tabs-justified-active-link-border-color: @body-bg;
|
||||
|
||||
//== Pills
|
||||
@nav-pills-border-radius: @border-radius-base;
|
||||
@nav-pills-active-link-hover-bg: @component-active-bg;
|
||||
@nav-pills-active-link-hover-color: @component-active-color;
|
||||
|
||||
|
||||
//== Pagination
|
||||
//
|
||||
//##
|
||||
|
||||
@pagination-color: @link-color;
|
||||
@pagination-bg: #fff;
|
||||
@pagination-border: #ddd;
|
||||
|
||||
@pagination-hover-color: @link-hover-color;
|
||||
@pagination-hover-bg: @gray-lighter;
|
||||
@pagination-hover-border: #ddd;
|
||||
|
||||
@pagination-active-color: #fff;
|
||||
@pagination-active-bg: @brand-primary;
|
||||
@pagination-active-border: @brand-primary;
|
||||
|
||||
@pagination-disabled-color: @gray-light;
|
||||
@pagination-disabled-bg: #fff;
|
||||
@pagination-disabled-border: #ddd;
|
||||
|
||||
|
||||
//== Pager
|
||||
//
|
||||
//##
|
||||
|
||||
@pager-bg: @pagination-bg;
|
||||
@pager-border: @pagination-border;
|
||||
@pager-border-radius: 15px;
|
||||
|
||||
@pager-hover-bg: @pagination-hover-bg;
|
||||
|
||||
@pager-active-bg: @pagination-active-bg;
|
||||
@pager-active-color: @pagination-active-color;
|
||||
|
||||
@pager-disabled-color: @pagination-disabled-color;
|
||||
|
||||
|
||||
//== Jumbotron
|
||||
//
|
||||
//##
|
||||
|
||||
@jumbotron-padding: 30px;
|
||||
@jumbotron-color: inherit;
|
||||
@jumbotron-bg: @gray-lighter;
|
||||
@jumbotron-heading-color: inherit;
|
||||
@jumbotron-font-size: ceil((@font-size-base * 1.5));
|
||||
|
||||
|
||||
//== Form states and alerts
|
||||
//
|
||||
//## Define colors for form feedback states and, by default, alerts.
|
||||
|
||||
@state-success-text: darken(@brand-success, 20%);
|
||||
@state-success-bg: lighten(@brand-success, 50%);
|
||||
@state-success-border: darken(@brand-success, 5%);
|
||||
|
||||
@state-info-text: darken(@brand-info, 20%);
|
||||
@state-info-bg: lighten(@brand-info, 47%);
|
||||
@state-info-border: darken(@brand-info, 7%);
|
||||
|
||||
@state-warning-text: darken(@brand-warning, 10%);
|
||||
@state-warning-bg: lighten(@brand-warning, 45%);
|
||||
@state-warning-border: @brand-warning;
|
||||
|
||||
@state-danger-text: darken(@brand-danger, 10%);
|
||||
@state-danger-bg: lighten(@brand-danger, 50%);
|
||||
@state-danger-border: darken(@brand-danger, 5%);
|
||||
|
||||
|
||||
//== Tooltips
|
||||
//
|
||||
//##
|
||||
|
||||
//** Tooltip max width
|
||||
@tooltip-max-width: 200px;
|
||||
//** Tooltip text color
|
||||
@tooltip-color: #fff;
|
||||
//** Tooltip background color
|
||||
@tooltip-bg: #000;
|
||||
@tooltip-opacity: .9;
|
||||
|
||||
//** Tooltip arrow width
|
||||
@tooltip-arrow-width: 5px;
|
||||
//** Tooltip arrow color
|
||||
@tooltip-arrow-color: @tooltip-bg;
|
||||
|
||||
|
||||
//== Popovers
|
||||
//
|
||||
//##
|
||||
|
||||
//** Popover body background color
|
||||
@popover-bg: #fff;
|
||||
//** Popover maximum width
|
||||
@popover-max-width: 276px;
|
||||
//** Popover border color
|
||||
@popover-border-color: rgba(0,0,0,.2);
|
||||
//** Popover fallback border color
|
||||
@popover-fallback-border-color: #ccc;
|
||||
|
||||
//** Popover title background color
|
||||
@popover-title-bg: darken(@popover-bg, 3%);
|
||||
|
||||
//** Popover arrow width
|
||||
@popover-arrow-width: 10px;
|
||||
//** Popover arrow color
|
||||
@popover-arrow-color: #fff;
|
||||
|
||||
//** Popover outer arrow width
|
||||
@popover-arrow-outer-width: (@popover-arrow-width + 1);
|
||||
//** Popover outer arrow color
|
||||
@popover-arrow-outer-color: fadein(@popover-border-color, 5%);
|
||||
//** Popover outer arrow fallback color
|
||||
@popover-arrow-outer-fallback-color: darken(@popover-fallback-border-color, 20%);
|
||||
|
||||
|
||||
//== Labels
|
||||
//
|
||||
//##
|
||||
|
||||
//** Default label background color
|
||||
@label-default-bg: @gray-light;
|
||||
//** Primary label background color
|
||||
@label-primary-bg: @brand-primary;
|
||||
//** Success label background color
|
||||
@label-success-bg: @brand-success;
|
||||
//** Info label background color
|
||||
@label-info-bg: @brand-info;
|
||||
//** Warning label background color
|
||||
@label-warning-bg: @brand-warning;
|
||||
//** Danger label background color
|
||||
@label-danger-bg: @brand-danger;
|
||||
|
||||
//** Default label text color
|
||||
@label-color: #fff;
|
||||
//** Default text color of a linked label
|
||||
@label-link-hover-color: #fff;
|
||||
|
||||
|
||||
//== Modals
|
||||
//
|
||||
//##
|
||||
|
||||
//** Padding applied to the modal body
|
||||
@modal-inner-padding: 20px;
|
||||
|
||||
//** Padding applied to the modal title
|
||||
@modal-title-padding: 15px;
|
||||
//** Modal title line-height
|
||||
@modal-title-line-height: @line-height-base;
|
||||
|
||||
//** Background color of modal content area
|
||||
@modal-content-bg: #fff;
|
||||
//** Modal content border color
|
||||
@modal-content-border-color: rgba(0,0,0,.2);
|
||||
//** Modal content border color **for IE8**
|
||||
@modal-content-fallback-border-color: #999;
|
||||
|
||||
//** Modal backdrop background color
|
||||
@modal-backdrop-bg: #000;
|
||||
//** Modal backdrop opacity
|
||||
@modal-backdrop-opacity: .5;
|
||||
//** Modal header border color
|
||||
@modal-header-border-color: #e5e5e5;
|
||||
//** Modal footer border color
|
||||
@modal-footer-border-color: @modal-header-border-color;
|
||||
@modal-footer-background-color: @gray-lightest;
|
||||
|
||||
@modal-lg: 900px;
|
||||
@modal-md: 600px;
|
||||
@modal-sm: 300px;
|
||||
|
||||
|
||||
//== Alerts
|
||||
//
|
||||
//## Define alert colors, border radius, and padding.
|
||||
|
||||
@alert-padding: 15px;
|
||||
@alert-border-radius: @border-radius-base;
|
||||
@alert-link-font-weight: bold;
|
||||
|
||||
@alert-success-bg: @state-success-bg;
|
||||
@alert-success-text: @state-success-text;
|
||||
@alert-success-border: @state-success-border;
|
||||
|
||||
@alert-info-bg: @state-info-bg;
|
||||
@alert-info-text: @state-info-text;
|
||||
@alert-info-border: @state-info-border;
|
||||
|
||||
@alert-warning-bg: @state-warning-bg;
|
||||
@alert-warning-text: @state-warning-text;
|
||||
@alert-warning-border: @state-warning-border;
|
||||
|
||||
@alert-danger-bg: @state-danger-bg;
|
||||
@alert-danger-text: @state-danger-text;
|
||||
@alert-danger-border: @state-danger-border;
|
||||
|
||||
|
||||
//== Progress bars
|
||||
//
|
||||
//##
|
||||
|
||||
//** Background color of the whole progress component
|
||||
@progress-bg: white;
|
||||
@progress-border-color: @gray-lighter;
|
||||
//** Progress bar text color
|
||||
@progress-bar-color: #fff;
|
||||
|
||||
//** Default progress bar color
|
||||
@progress-bar-bg: @brand-primary;
|
||||
//** Success progress bar color
|
||||
@progress-bar-success-bg: @brand-success;
|
||||
//** Warning progress bar color
|
||||
@progress-bar-warning-bg: @brand-warning;
|
||||
//** Danger progress bar color
|
||||
@progress-bar-danger-bg: @brand-danger;
|
||||
//** Info progress bar color
|
||||
@progress-bar-info-bg: @brand-info;
|
||||
|
||||
|
||||
//== List group
|
||||
//
|
||||
//##
|
||||
|
||||
//** Background color on `.list-group-item`
|
||||
@list-group-bg: #fff;
|
||||
//** `.list-group-item` border color
|
||||
@list-group-border: #ddd;
|
||||
//** List group border radius
|
||||
@list-group-border-radius: @border-radius-base;
|
||||
|
||||
//** Background color of single list elements on hover
|
||||
@list-group-hover-bg: #f5f5f5;
|
||||
//** Text color of active list elements
|
||||
@list-group-active-color: @component-active-color;
|
||||
//** Background color of active list elements
|
||||
@list-group-active-bg: @component-active-bg;
|
||||
//** Border color of active list elements
|
||||
@list-group-active-border: @list-group-active-bg;
|
||||
@list-group-active-text-color: lighten(@list-group-active-bg, 40%);
|
||||
|
||||
@list-group-link-color: #555;
|
||||
@list-group-link-heading-color: #333;
|
||||
|
||||
|
||||
//== Panels
|
||||
//
|
||||
//##
|
||||
|
||||
@panel-bg: #fff;
|
||||
@panel-body-padding: 15px;
|
||||
@panel-border-radius: @border-radius-base;
|
||||
|
||||
//** Border color for elements within panels
|
||||
@panel-inner-border: #ddd;
|
||||
@panel-footer-bg: #f5f5f5;
|
||||
|
||||
@panel-default-text: @gray-dark;
|
||||
@panel-default-border: #ddd;
|
||||
@panel-default-heading-bg: #f5f5f5;
|
||||
|
||||
@panel-primary-text: #fff;
|
||||
@panel-primary-border: @brand-primary;
|
||||
@panel-primary-heading-bg: @brand-primary;
|
||||
|
||||
@panel-success-text: @state-success-text;
|
||||
@panel-success-border: @state-success-border;
|
||||
@panel-success-heading-bg: @state-success-bg;
|
||||
|
||||
@panel-info-text: @state-info-text;
|
||||
@panel-info-border: @state-info-border;
|
||||
@panel-info-heading-bg: @state-info-bg;
|
||||
|
||||
@panel-warning-text: @state-warning-text;
|
||||
@panel-warning-border: @state-warning-border;
|
||||
@panel-warning-heading-bg: @state-warning-bg;
|
||||
|
||||
@panel-danger-text: @state-danger-text;
|
||||
@panel-danger-border: @state-danger-border;
|
||||
@panel-danger-heading-bg: @state-danger-bg;
|
||||
|
||||
|
||||
//== Thumbnails
|
||||
//
|
||||
//##
|
||||
|
||||
//** Padding around the thumbnail image
|
||||
@thumbnail-padding: 4px;
|
||||
//** Thumbnail background color
|
||||
@thumbnail-bg: @body-bg;
|
||||
//** Thumbnail border color
|
||||
@thumbnail-border: #ddd;
|
||||
//** Thumbnail border radius
|
||||
@thumbnail-border-radius: @border-radius-base;
|
||||
|
||||
//** Custom text color for thumbnail captions
|
||||
@thumbnail-caption-color: @text-color;
|
||||
//** Padding around the thumbnail caption
|
||||
@thumbnail-caption-padding: 9px;
|
||||
|
||||
|
||||
//== Wells
|
||||
//
|
||||
//##
|
||||
|
||||
@well-bg: #f5f5f5;
|
||||
@well-border: darken(@well-bg, 7%);
|
||||
|
||||
|
||||
//== Badges
|
||||
//
|
||||
//##
|
||||
|
||||
@badge-color: #fff;
|
||||
//** Linked badge text color on hover
|
||||
@badge-link-hover-color: #fff;
|
||||
@badge-bg: @gray-light;
|
||||
|
||||
//** Badge text color in active nav link
|
||||
@badge-active-color: @link-color;
|
||||
//** Badge background color in active nav link
|
||||
@badge-active-bg: #fff;
|
||||
|
||||
@badge-font-weight: bold;
|
||||
@badge-line-height: 1;
|
||||
@badge-border-radius: 10px;
|
||||
|
||||
|
||||
//== Breadcrumbs
|
||||
//
|
||||
//##
|
||||
|
||||
@breadcrumb-padding-vertical: 8px;
|
||||
@breadcrumb-padding-horizontal: 15px;
|
||||
//** Breadcrumb background color
|
||||
@breadcrumb-bg: #f5f5f5;
|
||||
//** Breadcrumb text color
|
||||
@breadcrumb-color: #ccc;
|
||||
//** Text color of current page in the breadcrumb
|
||||
@breadcrumb-active-color: @gray-light;
|
||||
//** Textual separator for between breadcrumb elements
|
||||
@breadcrumb-separator: "/";
|
||||
|
||||
|
||||
//== Carousel
|
||||
//
|
||||
//##
|
||||
|
||||
@carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6);
|
||||
|
||||
@carousel-control-color: #fff;
|
||||
@carousel-control-width: 15%;
|
||||
@carousel-control-opacity: .5;
|
||||
@carousel-control-font-size: 20px;
|
||||
|
||||
@carousel-indicator-active-bg: #fff;
|
||||
@carousel-indicator-border-color: #fff;
|
||||
|
||||
@carousel-caption-color: #fff;
|
||||
|
||||
|
||||
//== Close
|
||||
//
|
||||
//##
|
||||
|
||||
@close-font-weight: bold;
|
||||
@close-color: #000;
|
||||
@close-text-shadow: 0 1px 0 #fff;
|
||||
|
||||
|
||||
//== Code
|
||||
//
|
||||
//##
|
||||
|
||||
@code-color: #c7254e;
|
||||
@code-bg: #f9f2f4;
|
||||
|
||||
@kbd-color: #fff;
|
||||
@kbd-bg: #333;
|
||||
|
||||
@pre-bg: #f5f5f5;
|
||||
@pre-color: @gray-dark;
|
||||
@pre-border-color: #ccc;
|
||||
@pre-scrollable-max-height: 340px;
|
||||
|
||||
|
||||
//== Type
|
||||
//
|
||||
//##
|
||||
|
||||
//** Text muted color
|
||||
@text-muted: @gray-light;
|
||||
//** Abbreviations and acronyms border color
|
||||
@abbr-border-color: @gray-light;
|
||||
//** Headings small color
|
||||
@headings-small-color: @gray-light;
|
||||
//** Blockquote small color
|
||||
@blockquote-small-color: @gray;
|
||||
//** Blockquote font size
|
||||
@blockquote-font-size: (@font-size-base * 1.125);
|
||||
//** Blockquote border color
|
||||
@blockquote-border-color: @gray-lighter;
|
||||
//** Page header border color
|
||||
@page-header-border-color: @gray-lighter;
|
||||
|
||||
|
||||
//== Miscellaneous
|
||||
//
|
||||
//##
|
||||
|
||||
//** Horizontal line color.
|
||||
@hr-border: @gray-lighter;
|
||||
|
||||
//** Horizontal offset for forms and lists.
|
||||
@component-offset-horizontal: 180px;
|
||||
|
||||
@content-margin-top: @line-height-computed;
|
||||
@content-margin-top: @line-height-computed;
|
||||
|
||||
// Custom
|
||||
|
||||
@left-menu-width: 260px;
|
||||
@left-menu-animation-duration: 0.35s;
|
||||
|
||||
@toolbar-border-color: @gray-lighter;
|
||||
@file-tree-droppable-background-color: rgb(252, 231, 199);
|
||||
|
||||
@editor-dark-background-color: #333;
|
||||
@editor-dark-toolbar-border-color: #222;
|
||||
@editor-dark-highlight-color: #FFA03A;
|
||||
@import "_common-variables.less";
|
||||
|
|
3
services/web/public/stylesheets/ol-style.less
Normal file
3
services/web/public/stylesheets/ol-style.less
Normal file
|
@ -0,0 +1,3 @@
|
|||
// Core variables and mixins
|
||||
@import "core/ol-variables.less";
|
||||
@import "_style_includes.less";
|
|
@ -1,86 +1,3 @@
|
|||
// Core variables and mixins
|
||||
@import "core/variables.less";
|
||||
@import url(https://netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css);
|
||||
|
||||
@import "core/mixins.less";
|
||||
|
||||
// Reset
|
||||
@import "core/normalize.less";
|
||||
@import "core/print.less";
|
||||
|
||||
// Core CSS
|
||||
@import "core/scaffolding.less";
|
||||
@import "core/type.less";
|
||||
@import "core/grid.less";
|
||||
|
||||
// Components
|
||||
@import "components/tables.less";
|
||||
@import "components/forms.less";
|
||||
@import "components/buttons.less";
|
||||
@import "components/card.less";
|
||||
//@import "components/code.less";
|
||||
@import "components/component-animations.less";
|
||||
@import "components/glyphicons.less";
|
||||
@import "components/dropdowns.less";
|
||||
@import "components/button-groups.less";
|
||||
@import "components/input-groups.less";
|
||||
@import "components/navs.less";
|
||||
@import "components/navbar.less";
|
||||
@import "components/footer.less";
|
||||
//@import "components/breadcrumbs.less";
|
||||
//@import "components/pagination.less";
|
||||
@import "components/pager.less";
|
||||
@import "components/labels.less";
|
||||
//@import "components/badges.less";
|
||||
//@import "components/jumbotron.less";
|
||||
@import "components/thumbnails.less";
|
||||
@import "components/alerts.less";
|
||||
@import "components/progress-bars.less";
|
||||
// @import "components/media.less";
|
||||
// @import "components/list-group.less";
|
||||
// @import "components/panels.less";
|
||||
// @import "components/wells.less";
|
||||
@import "components/close.less";
|
||||
@import "components/fineupload.less";
|
||||
@import "components/hover.less";
|
||||
|
||||
// Components w/ JavaScript
|
||||
@import "components/modals.less";
|
||||
@import "components/tooltip.less";
|
||||
@import "components/popovers.less";
|
||||
@import "components/carousel.less";
|
||||
|
||||
// ngTagsInput
|
||||
@import "components/tags-input.less";
|
||||
|
||||
// Utility classes
|
||||
@import "core/utilities.less";
|
||||
@import "core/responsive-utilities.less";
|
||||
|
||||
// ShareLaTeX app classes
|
||||
@import "app/base.less";
|
||||
@import "app/account-settings.less";
|
||||
@import "app/beta-program.less";
|
||||
@import "app/about-page.less";
|
||||
@import "app/project-list.less";
|
||||
@import "app/editor.less";
|
||||
@import "app/homepage.less";
|
||||
@import "app/plans.less";
|
||||
@import "app/recurly.less";
|
||||
@import "app/bonus.less";
|
||||
@import "app/register.less";
|
||||
@import "app/blog.less";
|
||||
@import "app/features.less";
|
||||
@import "app/templates.less";
|
||||
@import "app/wiki.less";
|
||||
@import "app/translations.less";
|
||||
@import "app/contact-us.less";
|
||||
@import "app/subscription.less";
|
||||
@import "app/sprites.less";
|
||||
@import "app/invite.less";
|
||||
@import "app/review-features-page.less";
|
||||
@import "app/error-pages.less";
|
||||
|
||||
@import "../js/libs/pdfListView/TextLayer.css";
|
||||
@import "../js/libs/pdfListView/AnnotationsLayer.css";
|
||||
@import "../js/libs/pdfListView/HighlightsLayer.css";
|
||||
@import "_style_includes.less";
|
|
@ -116,6 +116,24 @@ describe "AuthenticationManager", ->
|
|||
expect(err).to.exist
|
||||
done()
|
||||
|
||||
it "should not start the bcrypt process", (done)->
|
||||
@AuthenticationManager.setUserPassword @user_id, @password, (err)=>
|
||||
@bcrypt.genSalt.called.should.equal false
|
||||
@bcrypt.hash.called.should.equal false
|
||||
done()
|
||||
|
||||
describe "too short", ->
|
||||
beforeEach ->
|
||||
@settings.passwordStrengthOptions =
|
||||
length:
|
||||
max:10
|
||||
min:6
|
||||
@password = "dsd"
|
||||
|
||||
it "should return and error", (done)->
|
||||
@AuthenticationManager.setUserPassword @user_id, @password, (err)->
|
||||
expect(err).to.exist
|
||||
done()
|
||||
|
||||
it "should not start the bcrypt process", (done)->
|
||||
@AuthenticationManager.setUserPassword @user_id, @password, (err)=>
|
||||
|
|
|
@ -37,6 +37,7 @@ describe "SubscriptionUpdater", ->
|
|||
constructor: (opts)->
|
||||
subscription.admin_id = opts.admin_id
|
||||
return subscription
|
||||
@remove: sinon.stub().yields()
|
||||
@SubscriptionModel.update = @updateStub
|
||||
@SubscriptionModel.findAndModify = @findAndModifyStub
|
||||
|
||||
|
@ -230,3 +231,35 @@ describe "SubscriptionUpdater", ->
|
|||
@ReferalAllocator.assignBonus.calledWith(@adminuser_id).should.equal true
|
||||
done()
|
||||
|
||||
describe "deleteSubscription", ->
|
||||
beforeEach (done) ->
|
||||
@subscription_id = ObjectId().toString()
|
||||
@subscription = {
|
||||
"mock": "subscription",
|
||||
admin_id: ObjectId(),
|
||||
member_ids: [ ObjectId(), ObjectId(), ObjectId() ]
|
||||
}
|
||||
@SubscriptionLocator.getSubscription = sinon.stub().yields(null, @subscription)
|
||||
@SubscriptionUpdater._setUsersMinimumFeatures = sinon.stub().yields()
|
||||
@SubscriptionUpdater.deleteSubscription @subscription_id, done
|
||||
|
||||
it "should look up the subscription", ->
|
||||
@SubscriptionLocator.getSubscription
|
||||
.calledWith(@subscription_id)
|
||||
.should.equal true
|
||||
|
||||
it "should remove the subscription", ->
|
||||
@SubscriptionModel.remove
|
||||
.calledWith({_id: ObjectId(@subscription_id)})
|
||||
.should.equal true
|
||||
|
||||
it "should downgrade the admin_id", ->
|
||||
@SubscriptionUpdater._setUsersMinimumFeatures
|
||||
.calledWith(@subscription.admin_id)
|
||||
.should.equal true
|
||||
|
||||
it "should downgrade all of the members", ->
|
||||
for user_id in @subscription.member_ids
|
||||
@SubscriptionUpdater._setUsersMinimumFeatures
|
||||
.calledWith(user_id)
|
||||
.should.equal true
|
||||
|
|
|
@ -94,7 +94,7 @@ describe "Opening", ->
|
|||
|
||||
expect(error, "smoke test: error returned in getting project list").to.not.exist
|
||||
expect(!!stderr.match("200 OK"), "smoke test: response code is not 200 getting project list").to.equal true
|
||||
expect(!!stdout.match("<title>Your Projects - ShareLaTeX, Online LaTeX Editor</title>"), "smoke test: body does not have correct title").to.equal true
|
||||
expect(!!stdout.match("<title>Your Projects - .*, Online LaTeX Editor</title>"), "smoke test: body does not have correct title").to.equal true
|
||||
expect(!!stdout.match("ProjectPageController"), "smoke test: body does not have correct angular controller").to.equal true
|
||||
done()
|
||||
|
||||
|
|
Loading…
Reference in a new issue