diff --git a/helm-chart/templates/_helpers.tpl b/helm-chart/templates/_helpers.tpl index b750bb641a..611d11d2f4 100644 --- a/helm-chart/templates/_helpers.tpl +++ b/helm-chart/templates/_helpers.tpl @@ -1,25 +1,3 @@ -{{/* -Create the name of the local-settings secret -*/}} -{{/*{{- define "localSettings.env" }} -{{- range $key, $val := $.Values.localSettings }} - - name: {{ $key }} - valueFrom: - configMapKeyRef: - name: local-settings-{{ $.Values.deployEnv }} - key: {{ $key }} -{{- end }} -{{- end }} -*/}} - -{{- define "secrets.localSettings" -}} -{{- if .Values.secrets.localSettings.ref }} -{{- .Values.secrets.localSettings.ref }} -{{- else -}} -local-settings-secrets-{{ .Values.deployEnv }} -{{- end }} -{{- end }} - {{- define "secrets.googleClient" }} {{- if .Values.web.secrets.googleClient.ref -}} {{- .Values.web.secrets.googleClient.ref }} diff --git a/helm-chart/templates/cronjob/daily-email-notifications.yaml b/helm-chart/templates/cronjob/daily-email-notifications.yaml index 11b5350d29..440c5552d0 100644 --- a/helm-chart/templates/cronjob/daily-email-notifications.yaml +++ b/helm-chart/templates/cronjob/daily-email-notifications.yaml @@ -32,7 +32,11 @@ spec: value: "varnish-{{ .Values.deployEnv }}-{{ .Release.Revision }}" envFrom: - secretRef: - name: {{ template "secrets.localSettings" . }} + name: {{ .Values.secrets.localSettings.ref }} + optional: true + - secretRef: + name: local-settings-secrets-{{ .Values.deployEnv }} + optional: true - configMapRef: name: local-settings-{{ .Values.deployEnv }} volumeMounts: diff --git a/helm-chart/templates/cronjob/index-from-queue.yaml b/helm-chart/templates/cronjob/index-from-queue.yaml index 2898465534..c80f4e3bc1 100644 --- a/helm-chart/templates/cronjob/index-from-queue.yaml +++ b/helm-chart/templates/cronjob/index-from-queue.yaml @@ -44,7 +44,11 @@ spec: value: "varnish-{{ .Values.deployEnv }}-{{ .Release.Revision }}" envFrom: - secretRef: - name: {{ template "secrets.localSettings" . }} + name: {{ .Values.secrets.localSettings.ref }} + optional: true + - secretRef: + name: local-settings-secrets-{{ .Values.deployEnv }} + optional: true - configMapRef: name: local-settings-{{ .Values.deployEnv }} volumeMounts: diff --git a/helm-chart/templates/cronjob/metrics.yaml b/helm-chart/templates/cronjob/metrics.yaml index fd0c40872b..f6bc3c9bd4 100644 --- a/helm-chart/templates/cronjob/metrics.yaml +++ b/helm-chart/templates/cronjob/metrics.yaml @@ -31,7 +31,11 @@ spec: value: "varnish-{{ .Values.deployEnv }}-{{ .Release.Revision }}" envFrom: - secretRef: - name: {{ template "secrets.localSettings" . }} + name: {{ .Values.secrets.localSettings.ref }} + optional: true + - secretRef: + name: local-settings-secrets-{{ .Values.deployEnv }} + optional: true - configMapRef: name: local-settings-{{ .Values.deployEnv }} volumeMounts: diff --git a/helm-chart/templates/cronjob/nation-builder-sync.yaml b/helm-chart/templates/cronjob/nation-builder-sync.yaml index 350546ed65..87765af846 100644 --- a/helm-chart/templates/cronjob/nation-builder-sync.yaml +++ b/helm-chart/templates/cronjob/nation-builder-sync.yaml @@ -32,7 +32,11 @@ spec: value: "varnish-{{ .Values.deployEnv }}-{{ .Release.Revision }}" envFrom: - secretRef: - name: {{ template "secrets.localSettings" . }} + name: {{ .Values.secrets.localSettings.ref }} + optional: true + - secretRef: + name: local-settings-secrets-{{ .Values.deployEnv }} + optional: true - configMapRef: name: local-settings-{{ .Values.deployEnv }} volumeMounts: diff --git a/helm-chart/templates/cronjob/regenerate-long-cached-data.yaml b/helm-chart/templates/cronjob/regenerate-long-cached-data.yaml index 53973348b1..a6cf02124e 100644 --- a/helm-chart/templates/cronjob/regenerate-long-cached-data.yaml +++ b/helm-chart/templates/cronjob/regenerate-long-cached-data.yaml @@ -32,7 +32,11 @@ spec: value: "varnish-{{ .Values.deployEnv }}-{{ .Release.Revision }}" envFrom: - secretRef: - name: {{ template "secrets.localSettings" . }} + name: {{ .Values.secrets.localSettings.ref }} + optional: true + - secretRef: + name: local-settings-secrets-{{ .Values.deployEnv }} + optional: true - configMapRef: name: local-settings-{{ .Values.deployEnv }} volumeMounts: diff --git a/helm-chart/templates/cronjob/reindex-elasticsearch.yaml b/helm-chart/templates/cronjob/reindex-elasticsearch.yaml index 84ae9323bf..a5d6b39a10 100644 --- a/helm-chart/templates/cronjob/reindex-elasticsearch.yaml +++ b/helm-chart/templates/cronjob/reindex-elasticsearch.yaml @@ -53,7 +53,11 @@ spec: key: slack-webhook envFrom: - secretRef: - name: {{ template "secrets.localSettings" . }} + name: {{ .Values.secrets.localSettings.ref }} + optional: true + - secretRef: + name: local-settings-secrets-{{ .Values.deployEnv }} + optional: true - configMapRef: name: local-settings-{{ .Values.deployEnv }} volumeMounts: diff --git a/helm-chart/templates/cronjob/sitemaps.yaml b/helm-chart/templates/cronjob/sitemaps.yaml index 6fc853bd6c..78b21db85f 100644 --- a/helm-chart/templates/cronjob/sitemaps.yaml +++ b/helm-chart/templates/cronjob/sitemaps.yaml @@ -38,7 +38,11 @@ spec: value: "varnish-{{ .Values.deployEnv }}-{{ .Release.Revision }}" envFrom: - secretRef: - name: {{ template "secrets.localSettings" . }} + name: {{ .Values.secrets.localSettings.ref }} + optional: true + - secretRef: + name: local-settings-secrets-{{ .Values.deployEnv }} + optional: true - configMapRef: name: local-settings-{{ .Values.deployEnv }} volumeMounts: diff --git a/helm-chart/templates/cronjob/topics-indexing.yaml b/helm-chart/templates/cronjob/topics-indexing.yaml index 684f3583ce..a5dd7660b9 100644 --- a/helm-chart/templates/cronjob/topics-indexing.yaml +++ b/helm-chart/templates/cronjob/topics-indexing.yaml @@ -28,7 +28,11 @@ spec: value: "varnish-{{ .Values.deployEnv }}-{{ .Release.Revision }}" envFrom: - secretRef: - name: {{ template "secrets.localSettings" . }} + name: {{ .Values.secrets.localSettings.ref }} + optional: true + - secretRef: + name: local-settings-secrets-{{ .Values.deployEnv }} + optional: true - configMapRef: name: local-settings-{{ .Values.deployEnv }} volumeMounts: diff --git a/helm-chart/templates/cronjob/weekly-email-notifications.yaml b/helm-chart/templates/cronjob/weekly-email-notifications.yaml index 0dd3163532..de20c63e86 100644 --- a/helm-chart/templates/cronjob/weekly-email-notifications.yaml +++ b/helm-chart/templates/cronjob/weekly-email-notifications.yaml @@ -32,7 +32,11 @@ spec: value: "varnish-{{ .Values.deployEnv }}-{{ .Release.Revision }}" envFrom: - secretRef: - name: {{ template "secrets.localSettings" . }} + name: {{ .Values.secrets.localSettings.ref }} + optional: true + - secretRef: + name: local-settings-secrets-{{ .Values.deployEnv }} + optional: true - configMapRef: name: local-settings-{{ .Values.deployEnv }} volumeMounts: diff --git a/helm-chart/templates/rollout/monitor.yaml b/helm-chart/templates/rollout/monitor.yaml index 518f85a3ee..8a9d96cfd2 100644 --- a/helm-chart/templates/rollout/monitor.yaml +++ b/helm-chart/templates/rollout/monitor.yaml @@ -67,7 +67,11 @@ spec: value: "varnish-{{ .Values.deployEnv }}-{{ .Release.Revision }}" envFrom: - secretRef: - name: {{ template "secrets.localSettings" . }} + name: {{ .Values.secrets.localSettings.ref }} + optional: true + - secretRef: + name: local-settings-secrets-{{ .Values.deployEnv }} + optional: true - configMapRef: name: local-settings-{{ .Values.deployEnv }} resources: {{ toYaml .Values.monitor.resources | nindent 10 }} diff --git a/helm-chart/templates/rollout/web.yaml b/helm-chart/templates/rollout/web.yaml index ee4b2d517d..1631c84323 100644 --- a/helm-chart/templates/rollout/web.yaml +++ b/helm-chart/templates/rollout/web.yaml @@ -85,7 +85,11 @@ spec: value: "{{ .Release.Revision }}" envFrom: - secretRef: - name: {{ template "secrets.localSettings" . }} + name: {{ .Values.secrets.localSettings.ref }} + optional: true + - secretRef: + name: local-settings-secrets-{{ .Values.deployEnv }} + optional: true - configMapRef: name: local-settings-{{ .Values.deployEnv }} ports: diff --git a/helm-chart/templates/secret/local-settings.yaml b/helm-chart/templates/secret/local-settings.yaml index 6fddbb0e7d..0c33fe652c 100644 --- a/helm-chart/templates/secret/local-settings.yaml +++ b/helm-chart/templates/secret/local-settings.yaml @@ -1,4 +1,4 @@ -{{- if .Values.secrets.localSettings.DATABASES_HOST }} +{{- if .Values.secrets.localSettings.data }} apiVersion: v1 kind: Secret metadata: @@ -8,44 +8,7 @@ metadata: {{- include "sefaria.labels" . | nindent 4 }} type: Opaque data: - {{- with .Values.secrets.localSettings }} - DATABASES_HOST: {{ .DATABASES_HOST | b64enc }} - DATABASES_PASS: {{ .DATABASES_PASS | b64enc }} - DATABASES_USER: {{ .DATABASES_USER | b64enc }} - DATABASES_PORT: {{ .DATABASES_PORT | b64enc }} - GOOGLE_OAUTH2_CLIENT_ID: {{ .GOOGLE_OAUTH2_CLIENT_ID | b64enc }} - GOOGLE_OAUTH2_CLIENT_SECRET: {{ .GOOGLE_OAUTH2_CLIENT_SECRET | b64enc }} - SECRET_KEY: {{ .SECRET_KEY | b64enc }} - MANDRILL_API_KEY: {{ .MANDRILL_API_KEY | b64enc }} - SEFARIA_DB: {{ .SEFARIA_DB | b64enc }} - SEFARIA_DB_USER: {{ .SEFARIA_DB_USER | b64enc }} - SEFARIA_DB_PASSWORD: {{ .SEFARIA_DB_PASSWORD | b64enc }} - SEARCH_ADMIN_USER: {{ .SEARCH_ADMIN_USER | b64enc }} - SEARCH_ADMIN_PW: {{ .SEARCH_ADMIN_PW | b64enc }} - SEARCH_ADMIN_K8S: {{ .SEARCH_ADMIN_K8S | b64enc }} - TURN_SECRET: {{ .TURN_SECRET | b64enc }} - TURN_USER: {{ .TURN_USER | b64enc }} - SEFARIA_BOT_API_KEY: {{ .SEFARIA_BOT_API_KEY | b64enc }} - CLOUDFLARE_ZONE: {{ .CLOUDFLARE_ZONE | b64enc }} - CLOUDFLARE_EMAIL: {{ .CLOUDFLARE_EMAIL | b64enc }} - CLOUDFLARE_TOKEN: {{ .CLOUDFLARE_TOKEN | b64enc }} - GOOGLE_TAG_MANAGER_CODE: {{ .GOOGLE_TAG_MANAGER_CODE | b64enc }} - GOOGLE_ANALYTICS_CODE: {{ .GOOGLE_ANALYTICS_CODE | b64enc }} - GOOGLE_MAPS_API_KEY: {{ .GOOGLE_MAPS_API_KEY | b64enc }} - MIXPANEL_CODE: {{ .MIXPANEL_CODE | b64enc }} - HOTJAR_ID: {{ .HOTJAR_ID | b64enc }} - AWS_ACCESS_KEY: {{ .AWS_ACCESS_KEY | b64enc }} - AWS_SECRET_KEY: {{ .AWS_SECRET_KEY | b64enc }} - S3_BUCKET: {{ .S3_BUCKET | b64enc }} - NATIONBUILDER_TOKEN: {{ .NATIONBUILDER_TOKEN | b64enc }} - NATIONBUILDER_CLIENT_ID: {{ .NATIONBUILDER_CLIENT_ID | b64enc }} - NATIONBUILDER_CLIENT_SECRET: {{ .NATIONBUILDER_CLIENT_SECRET | b64enc }} - MAILCHIMP_API_KEY: {{ .MAILCHIMP_API_KEY | b64enc }} - MAILCHIMP_ANNOUNCE_ID: {{ .MAILCHIMP_ANNOUNCE_ID | b64enc }} - MAILCHIMP_WEBHOOK_KEY: {{ .MAILCHIMP_WEBHOOK_KEY | b64enc }} - RECAPTCHA_PUBLIC_KEY: {{ .RECAPTCHA_PUBLIC_KEY | b64enc }} - RECAPTCHA_PRIVATE_KEY: {{ .RECAPTCHA_PRIVATE_KEY | b64enc }} - SIMPLE_JWT_SIGNING_KEY: {{ .SIMPLE_JWT_SIGNING_KEY | b64enc }} - MOBILE_APP_KEY: {{ .MOBILE_APP_KEY | b64enc }} + {{- range $k, $v := .Values.secrets.localSettings.data }} + {{ $k }}: {{ $v | b64enc }} {{- end }} {{- end }} diff --git a/helm-chart/values.yaml b/helm-chart/values.yaml index c1f78f6d82..76acd72d17 100644 --- a/helm-chart/values.yaml +++ b/helm-chart/values.yaml @@ -203,50 +203,50 @@ monitor: secrets: localSettings: - # If you're using a refenrece to an existing secret then the data: section - # should be commented out and vice-versa. + # A reference to a secret containing the local settings ref: - # Add the value of these variables in the data: field for local use only. - # Also, it is understood that if you're adding one value you're adding - # all variable values - DATABASES_HOST: - DATABASES_PASS: - DATABASES_USER: - DATABASES_PORT: - GOOGLE_OAUTH2_CLIENT_ID: - GOOGLE_OAUTH2_CLIENT_SECRET: - SECRET_KEY: - MANDRILL_API_KEY: - SEFARIA_DB: - SEFARIA_DB_USER: - SEFARIA_DB_PASSWORD: - SEARCH_ADMIN_USER: - SEARCH_ADMIN_PW: - SEARCH_ADMIN_K8S: - TURN_SECRET: - TURN_USER: - SEFARIA_BOT_API_KEY: - CLOUDFLARE_ZONE: - CLOUDFLARE_EMAIL: - CLOUDFLARE_TOKEN: - GOOGLE_TAG_MANAGER_CODE: - GOOGLE_ANALYTICS_CODE: - GOOGLE_MAPS_API_KEY: - MIXPANEL_CODE: - HOTJAR_ID: - AWS_ACCESS_KEY: - AWS_SECRET_KEY: - S3_BUCKET: - NATIONBUILDER_TOKEN: - NATIONBUILDER_CLIENT_ID: - NATIONBUILDER_CLIENT_SECRET: - MAILCHIMP_API_KEY: - MAILCHIMP_ANNOUNCE_ID: - MAILCHIMP_WEBHOOK_KEY: - RECAPTCHA_PUBLIC_KEY: - RECAPTCHA_PRIVATE_KEY: - SIMPLE_JWT_SIGNING_KEY: - MOBILE_APP_KEY: + # The data keys can be used to override values in the local settings secret. + # This is intended for local development use and CI, do not use this in + # production. + #data: + # DATABASES_HOST: + # DATABASES_PASS: + # DATABASES_USER: + # DATABASES_PORT: + # GOOGLE_OAUTH2_CLIENT_ID: + # GOOGLE_OAUTH2_CLIENT_SECRET: + # SECRET_KEY: + # MANDRILL_API_KEY: + # SEFARIA_DB: + # SEFARIA_DB_USER: + # SEFARIA_DB_PASSWORD: + # SEARCH_ADMIN_USER: + # SEARCH_ADMIN_PW: + # SEARCH_ADMIN_K8S: + # TURN_SECRET: + # TURN_USER: + # SEFARIA_BOT_API_KEY: + # CLOUDFLARE_ZONE: + # CLOUDFLARE_EMAIL: + # CLOUDFLARE_TOKEN: + # GOOGLE_TAG_MANAGER_CODE: + # GOOGLE_ANALYTICS_CODE: + # GOOGLE_MAPS_API_KEY: + # MIXPANEL_CODE: + # HOTJAR_ID: + # AWS_ACCESS_KEY: + # AWS_SECRET_KEY: + # S3_BUCKET: + # NATIONBUILDER_TOKEN: + # NATIONBUILDER_CLIENT_ID: + # NATIONBUILDER_CLIENT_SECRET: + # MAILCHIMP_API_KEY: + # MAILCHIMP_ANNOUNCE_ID: + # MAILCHIMP_WEBHOOK_KEY: + # RECAPTCHA_PUBLIC_KEY: + # RECAPTCHA_PRIVATE_KEY: + # SIMPLE_JWT_SIGNING_KEY: + # MOBILE_APP_KEY: backupManager: # If you're using a reference to an existing secret then the data: section # should be commented out and vice-versa. @@ -256,7 +256,7 @@ secrets: # serviceAccount: # This is used in mongobackup & reindex-elasticsearch cron jobs. slackWebhook: - # If you're using a refenrece to an existing secret then the data: section + # If you're using a reference to an existing secret then the data: section # should be commented out and vice-versa. ref: # data: diff --git a/static/css/s2.css b/static/css/s2.css index 092efc53c1..eb242197e3 100644 --- a/static/css/s2.css +++ b/static/css/s2.css @@ -3105,6 +3105,7 @@ a.navBlockTitle:hover { } .textTableOfContents .torahNavParshiot .tocLevel .schema-node-toc { flex: 1 1 50%; + text-decoration: none; } .textTableOfContents > .tocLevel > .schema-node-toc { margin: 10px 0; diff --git a/static/js/BookPage.jsx b/static/js/BookPage.jsx index 3aa6d4f5fd..f7e22bd061 100644 --- a/static/js/BookPage.jsx +++ b/static/js/BookPage.jsx @@ -335,13 +335,28 @@ class TextTableOfContents extends Component { } componentDidMount() { this.loadData(); + this.scrollToCurrent(); } loadData(){ // Ensures data this text is in cache, rerenders after data load if needed - Sefaria.getIndexDetails(this.props.title).then(data => this.setState({ - indexDetails: data, - tab: this.getDefaultActiveTab(data) - })); + Sefaria.getIndexDetails(this.props.title).then((data) => { + this.setState({ + indexDetails: data, + tab: this.getDefaultActiveTab(data) + }); + this.scrollToCurrent(); + }); + } + annotateTorahAltDisplayProperties(altStructSchema){ + for (const node of altStructSchema.nodes) { + node["displayFixedTitleSubSections"] = true; + } + } + scrollToCurrent(){ + const curr = document.querySelector(".current"); + if(curr){ + Sefaria.util.scrollIntoViewIfNeeded(curr, {block: "center"}); + } } getDefaultActiveTab(indexDetails){ return ("default_struct" in indexDetails && indexDetails.default_struct in indexDetails?.alts) ? indexDetails.default_struct : "schema"; @@ -371,6 +386,11 @@ class TextTableOfContents extends Component { const defaultStruct = this.getDefaultActiveTab(this.state.indexDetails); const excludedStructs = this.state.indexDetails?.exclude_structs || []; const alts = this.state.indexDetails?.alts || {}; + if(isTorah){ + //add a dummy prop (maybe later add to actual db) to indicate the special display case for this alt struct. + // Showing both linked title and subsections + this.annotateTorahAltDisplayProperties(alts["Parasha"]) + } let structTabOptions = []; if(!excludedStructs.includes("schema")){ structTabOptions.push({ @@ -536,7 +556,7 @@ class SchemaNode extends Component { super(props); this.state = { // Collapse nodes below top level, and those that aren't default or makred includedSections - collapsed: "nodes" in props.schema ? props.schema.nodes.map(node => !(props.topLevel || props.disableSubCollapse || node.default || node.includeSections)) : [] + collapsed: "nodes" in props.schema && !(props.topLevel || props.disableSubCollapse) ? props.schema.nodes.map(node => !(node.default || node.includeSections)) : [] }; } toggleCollapse(i) { @@ -573,16 +593,18 @@ class SchemaNode extends Component { ); } - } else { + } else { //we do have subcontent let content = this.props.schema.nodes.map(function(node, i) { - const includeSections = node?.includeSections ?? true; //either undefined or explicitly true let path; - if ("nodes" in node || ("refs" in node && node.refs.length && includeSections)) { - // SchemaNode with children (nodes) or ArrayMapNode with depth (refs) + if (node.nodeType == "ArrayMapNode") { + //ArrayMapNode content + path = this.props.refPath + ", " + node.title; + return ; + } else if ("nodes" in node) { + // SchemaNode with children (nodes) path = this.props.refPath + ", " + node.title; - const keyPressFunc = this.props.disableSubCollapse ? null : (e) => { this.toggleCollapse(i) } return ( -
+
{this.toggleCollapse(i)}} onKeyPress={(e) => {e.charCode == 13 ? this.toggleCollapse(i):null}} @@ -602,9 +624,6 @@ class SchemaNode extends Component {
: null }
); - } else if (node.nodeType == "ArrayMapNode") { - // ArrayMapNode with only wholeRef - return ; } else if (node.nodeType == "DictionaryNode") { return ); }.bind(this)); + + let path = this.props.refPath + ", " + schema.title; + let ref = "wholeRef" in schema ? Sefaria.splitSpanningRefNaive(schema.wholeRef)[0] : null; + + return schema.displayFixedTitleSubSections ? ( + + +
{sectionLinks}
+
+ ) : ( +
+ {this.toggleCollapse()}} + onKeyPress={(e) => {e.charCode == 13 ? this.toggleCollapse():null}} + role="heading" + aria-level="3" + aria-hidden="true" tabIndex={0}> + + + {!this.state.collapsed ? +
{sectionLinks}
+ : null } +
+ ); - return (
{sectionLinks}
); - - } else { + } else { //just a single link for an alt struct section let currentPlace = this.props?.currentlyVisibleSectionRef && - (this.props.schema.wholeRef == this.props?.currentlyVisibleRef || (Sefaria.refContains(this.props.schema.wholeRef, this.props?.currentlyVisibleRef))); + (schema.wholeRef == this.props?.currentlyVisibleRef || (Sefaria.refContains(schema.wholeRef, this.props?.currentlyVisibleRef))); const linkClasses = classNames({"schema-node-toc": 1, "linked":1, "current": currentPlace}); return ( - + - + ); } diff --git a/static/js/sefaria/sefaria.js b/static/js/sefaria/sefaria.js index 24c38b0ff9..3a08032140 100644 --- a/static/js/sefaria/sefaria.js +++ b/static/js/sefaria/sefaria.js @@ -278,6 +278,10 @@ Sefaria = extend(Sefaria, { const oref = this.getRefFromCache(ref); return oref ? oref.sectionRef : null; }, + splitSpanningRefNaive: function(ref){ + if (ref.indexOf("-") == -1) { return ref; } + return ref.split("-"); + }, splitRangingRef: function(ref) { // Returns an array of segment level refs which correspond to the ranging `ref` // e.g. "Genesis 1:1-2" -> ["Genesis 1:1", "Genesis 1:2"] diff --git a/static/js/sefaria/util.js b/static/js/sefaria/util.js index 3ebec9dbf5..0d3bb61f10 100644 --- a/static/js/sefaria/util.js +++ b/static/js/sefaria/util.js @@ -9,6 +9,28 @@ import {HDate, months} from '@hebcal/core'; var INBROWSER = (typeof document !== 'undefined'); class Util { + + /** + * Method to scroll into view port, if it's outside the viewport + * From: https://medium.com/@makk.bit/scroll-into-view-if-needed-10a96e0bdb61 + * @param {Object} target - DOM Element + * @returns {undefined} + * See also: https://www.javascripttutorial.net/dom/css/check-if-an-element-is-visible-in-the-viewport/ + * + */ + static scrollIntoViewIfNeeded(target, scrollIntoViewOptions) { + // Target is outside the viewport from the bottom + if (target.getBoundingClientRect().bottom > window.innerHeight) { + // The bottom of the target will be aligned to the bottom of the visible area of the scrollable ancestor. + target.scrollIntoView(scrollIntoViewOptions); + } + + // Target is outside the view from the top + if (target.getBoundingClientRect().top < 0) { + // The top of the target will be aligned to the top of the visible area of the scrollable ancestor + target.scrollIntoView(scrollIntoViewOptions); + } + } static selectElementContents(el) { //source: https://stackoverflow.com/questions/4183401/can-you-set-and-or-change-the-user-s-text-selection-in-javascript if (window.getSelection && document.createRange) {