├── utils.gs ├── images ├── fig.png ├── figur.png ├── Panel_small.png └── crossref_icon_128.png ├── lof-loader.html ├── changelog.md ├── privacy.md ├── LICENSE ├── terms-of-service.md ├── loader-style.html ├── error.gs ├── lof.html ├── sidebar.gs ├── test.gs ├── main.gs ├── manual_tests.md ├── lof-config.html ├── settings.test.gs ├── README.md ├── text.test.gs ├── sidebar-style.html ├── text.gs ├── settings.gs ├── sidebar-html.html ├── lof-code.gs └── sidebar-js.html /utils.gs: -------------------------------------------------------------------------------- 1 | // repair copied links 2 | -------------------------------------------------------------------------------- /images/fig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidrthorn/cross_reference/HEAD/images/fig.png -------------------------------------------------------------------------------- /images/figur.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidrthorn/cross_reference/HEAD/images/figur.png -------------------------------------------------------------------------------- /images/Panel_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidrthorn/cross_reference/HEAD/images/Panel_small.png -------------------------------------------------------------------------------- /images/crossref_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidrthorn/cross_reference/HEAD/images/crossref_icon_128.png -------------------------------------------------------------------------------- /lof-loader.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v2 4 | 5 | - capitalisation is left entirely to the user. When replacement text is lowercase, capitalisation is 6 | based on the text being replaced. 7 | - list of figures show the entire paragraph that contains the label 8 | - support for suffixes -------------------------------------------------------------------------------- /privacy.md: -------------------------------------------------------------------------------- 1 | # Cross Reference for Google Docs 2 | 3 | ## Privacy Policy 4 | Cross Reference makes changes to Google Docs documents and stores user settings in the users Google Docs property stores (document stores and user stores). Cross Reference neither exports nor collects any user data. 5 | 6 | The list of figures feature in Cross Reference uses the PDF.js library to process documents as PDFs. However, this library is run within the add-on instance in the document and does not export the PDF to any external servers. 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 David Rowthorn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /terms-of-service.md: -------------------------------------------------------------------------------- 1 | # Terms of Service for Cross Reference 2 | 3 | By installing and using the Cross Reference Google Docs add-on, you agree to these terms. 4 | 5 | ### Disclaimer of Warranties 6 | This plugin is provided "as is" and "as available," without any warranties of any kind, 7 | either express or implied, including but not limited to warranties of merchantability, 8 | fitness for a particular purpose, or non-infringement. We do not warrant that the plugin 9 | will be uninterrupted, error-free, or free of harmful components. 10 | 11 | ### Limitation of Liability 12 | In no event shall we be liable for any direct, indirect, incidental, special, consequential, 13 | or exemplary damages, including but not limited to, damages for loss of profits, goodwill, data, 14 | or other intangible losses, resulting from the use or inability to use the plugin, even if 15 | we have been advised of the possibility of such damages. 16 | 17 | ### Your Responsibility 18 | You acknowledge and agree that your use of the plugin is solely at your own risk. 19 | You are responsible for ensuring that your documents are backed up and for 20 | taking appropriate precautions to prevent any loss of data or disruption to your work. 21 | -------------------------------------------------------------------------------- /loader-style.html: -------------------------------------------------------------------------------- 1 | 76 | -------------------------------------------------------------------------------- /error.gs: -------------------------------------------------------------------------------- 1 | function handleErr(err) { 2 | addFlag(err.text, err.CRUrl) 3 | DocumentApp.getUi().alert(err.message) 4 | } 5 | 6 | 7 | function addFlag(text, CRUrl) { 8 | if (Number.isNaN(CRUrl.start) || Number.isNaN(CRUrl.end)) return 9 | 10 | const doc = DocumentApp.getActiveDocument() 11 | const position = doc.newPosition(text, CRUrl.start) 12 | 13 | text.setForegroundColor(CRUrl.start, CRUrl.end, '#FF0000') 14 | doc.setCursor(position) 15 | } 16 | 17 | 18 | function CRError(containingText, CRUrl, error) { 19 | const url = CRUrl.url 20 | const errorBoilerplate = "\n\nClick 'OK' to see the problem highlighted. Updating the document when this " + 21 | '\nhas been fixed will automatically remove this highlighting.' 22 | 23 | const messages = { 24 | duplicate: 'There are at least 2 labels with the code ' + url + '.' + 25 | "\n\nLabel codes must be 5 letters and label names (e.g. '" + 26 | url.substr(7) + "') must be unique." + 27 | errorBoilerplate, 28 | missref: 'There is a reference with nothing to refer to.' + 29 | '\nIt might contain a typo or the corresponding label might be missing.' + 30 | errorBoilerplate, 31 | unrecognised: 'A label or reference code was not recognised.' + 32 | '\n\nIt might be a typo or it might be a custom label you' + 33 | '\nhave not yet added in the configuration sidebar.' + 34 | errorBoilerplate, 35 | } 36 | 37 | return { 38 | text: containingText, 39 | CRUrl: CRUrl, 40 | error: error, 41 | message: messages[error], 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lof.html: -------------------------------------------------------------------------------- 1 | != include('loader-style'); ?> 2 | 3 | 5 | 6 |
4 |
5 | Cross Reference is an open source cross referencing add-on for Google Docs that automatically numbers elements such as figures and updates and formats in-text references to them. [Install](https://chrome.google.com/webstore/detail/cross-reference/hknkaiempgninehdhkgekoeoilkapgob?hl=en-GB).
6 |
7 | ## Short instructions
8 |
9 | Create a label for a figure by writing the word 'figure' in the caption and then adding a hyperlink
10 | to that word with the url `#figur_YOURCHOSENNAME`. Refer to this figure in your text by writing 'figure' again and
11 | adding a hyperlink with `#fig_YOURCHOSENNAME`. Update the document via 'Add-ons->Cross Reference->Update document'.
12 |
13 | ## More detailed instructions
14 |
15 | Cross Reference is built around two concepts: labels and references.
16 |
17 | A label is always unique and is associated with a single figure (or table etc.); this is usually represented by text such as _figure 1_ in the figure caption.
18 |
19 | A reference is usually in the text, and refers to a labelled figure (or table etc.). There can be many references to a single labelled entity.
20 |
21 | To refer to, say, a figure you must create a label for it, usually in the caption. Cross Reference uses
22 | hyperlinks to create labels. These have the following syntax: `#figur_population`. `#figur` is the built-in prefix used by Cross Reference; `population` in this case is your chosen name for the figure. Notice that the prefix uses `figur` rather than `figure` with an `e` -- all prefixes in Cross Reference are composed of a hash and a five-letter code, and separated from the name with an underscore.
23 |
24 | To create this label, you simply need to create some dummy text and add the link. You don't need to add any numbering, as
25 | this is taken care of by Cross Reference.
26 | So in the caption you could add the word 'figure', highlight that word, goto 'Tools->Insert link' and add `#figur_population` as
27 | the URL. (The dummy text will replaced by the add-on, so don't highlight text that you want to keep, such as other parts
28 | of the caption.)
29 |
30 | You now have a label that can be referred to. Creating references is similar to creating labels. First, create some dummy
31 | text, such as the word 'figure' -- so you might write something like "for an example of this phenomenon, see figure". Again,
32 | you don't need any numbering, as the add-on will take care of this. Now you need to apply another hyperlink, in this case to the word 'figure'. Reference syntax, however, is slightly
33 | different; in our case, it will be `#fig_population`. This must be identical to the label hyperlink, except that the code is now `fig` not `figur` (reference codes always use the first three letters of the code used for labels.)
34 |
35 | If you now goto the 'Add-ons->Cross Reference->Update document', your text should be replaced with a numbered label and
36 | a reference with the same number.
37 |
38 | Because Cross Reference replaces your dummy text, you can use a short word or even one letter, and you can also use the `Ctrl+K` shortcut to add the link. These two additions to the process should make things quicker.
39 |
40 | ## Not working?
41 |
42 | Aside from bugs, the biggest source of user error is using label syntax for references or vice-versa. Labels and references
43 | use a different code. If your references have nothing to refer to, make sure you created a label under you figure, not a
44 | reference.
45 |
46 | # This didn't work / I don't get it / how do I change the style of labels or references?
47 |
48 | The wiki covers the use of Cross Reference in more depth, including customising labels and references. Please feel free to raise issues or ask questions!
49 |
50 | [WIKI](https://github.com/davidrthorn/cross_reference/wiki/Cross-Reference-for-Google-Docs)
51 |
--------------------------------------------------------------------------------
/text.test.gs:
--------------------------------------------------------------------------------
1 | function testAllText() {
2 | testSuite('text.js', [
3 | testGetStyle,
4 | testCodeFromCRUrl,
5 | testCapitalizeIfAppropriate,
6 | testIsCapitalized,
7 | testCapitalize,
8 | testLabelNumberHandler,
9 | testRefNumberHandler,
10 | ])
11 | }
12 |
13 | function testCodeFromCRUrl() {
14 | It('ref url returns 3-string code',
15 | codeFromUrl('#fig_hello'),
16 | 'fig'
17 | )
18 | It('lab url returns 5 string code',
19 | codeFromUrl('#figur_hello'),
20 | 'figur'
21 | )
22 | It('returns null for other url',
23 | codeFromUrl('https://google.com'),
24 | null
25 | )
26 | It('returns null for malformed CRUrl',
27 | codeFromUrl('#figu_hello'),
28 | null
29 | )
30 | }
31 |
32 | function testCapitalizeIfAppropriate() {
33 | It('returns capitalized when reference text is already capitalized',
34 | capitalizeIfAppropriate('aa bb Figure', 5, 'Figure'),
35 | 'Figure'
36 | )
37 | It('returns capitalized when text to replace is already capitalized',
38 | capitalizeIfAppropriate('aa bb Figur', 5, 'figure'),
39 | 'Figure'
40 | )
41 | It('returns uncapitalized when text to replace is not capitalized',
42 | capitalizeIfAppropriate('aa bb figur', 5, 'figure'),
43 | 'Figure'
44 | )
45 | }
46 |
47 | function testCapitalize() {
48 | It('returns empty for empty',
49 | capitalize(''),
50 | ''
51 | )
52 | It('capitalizes non-empty string',
53 | capitalize('hello'),
54 | 'Hello'
55 | )
56 | It('does not modify already capitalized',
57 | capitalize('Hello'),
58 | 'Hello'
59 | )
60 | }
61 |
62 | function testIsCapitalized() {
63 | It('returns false for empty string',
64 | isCapitalized(''),
65 | false
66 | )
67 | It('returns true if is capitalized',
68 | isCapitalized('Hello'),
69 | true
70 | )
71 | It('returns false if not capitalize',
72 | isCapitalized('hello'),
73 | false
74 | )
75 | }
76 |
77 | function testLabelNumberHandler() {
78 | const recordedNumbers = {
79 | '#fig_first': 1,
80 | }
81 | const countByLabelType = {
82 | 'figur': 1,
83 | 'table': 2,
84 | }
85 | let sut = handleLabNumber(recordedNumbers)(countByLabelType)
86 |
87 | let got = sut('#figur_somename')
88 |
89 | It('increments the existing entry in the label map',
90 | countByLabelType['figur'],
91 | 2
92 | )
93 | It('records the url in the recorded numbers',
94 | recordedNumbers,
95 | {
96 | '#fig_first': 1,
97 | '#fig_somename': 2,
98 | }
99 | )
100 | It('returns the correct number',
101 | got,
102 | 2
103 | )
104 |
105 | got = sut('#figur_first')
106 | It('returns duplicate error',
107 | sut('#figur_first').message,
108 | 'duplicate'
109 | )
110 | }
111 |
112 |
113 | function testRefNumberHandler() {
114 | let recordedNumbers = {
115 | '#fig_first': 1,
116 | '#fig_second': 2,
117 | }
118 | let sut = handleRefNumber(recordedNumbers)
119 |
120 | let got = sut('#fig_second')
121 | It('returns correct number for url',
122 | got,
123 | 2
124 | )
125 | It('returns error for missing url',
126 | sut('#fig_missing').message,
127 | 'missref'
128 | )
129 | }
130 |
131 |
132 | function testGetStyle() {
133 | let settings = getDefaultSettings()
134 | let props = getProps('lab')(settings)
135 |
136 | let got = getStyle(props.figur)
137 | It('returns correct style object for default',
138 | got,
139 | { BOLD: false, ITALIC: false, UNDERLINE: false, FOREGROUND_COLOR: null }
140 | )
141 | }
142 |
143 |
144 | function testGetCRUrls() {
145 | let mockText = {
146 | getText: () => 'lorem ipsum',
147 | getTextAttributeIndices: () => [6, 10],
148 | getLinkUrl: idx => [6, 9].includes(idx) ? '#figur_test' : null
149 | }
150 | const isCR = isCRUrl(5)
151 |
152 | It('returns start and url',
153 | getCRUrls(isCR)(mockText),
154 | [{ start: 6, end: 9, url: '#figur_test'}]
155 | )
156 |
157 | mockText.getLinkUrl = idx => [6, 9].includes(idx) ? '#figur_test' : 'https://google.com'
158 | It("doesn't get broken by surrounding links",
159 | getCRUrls(isCR)(mockText),
160 | [{ start: 6, end: 9, url: '#figur_test'}]
161 | )
162 |
163 | }
164 |
165 |
166 | function testUpdateText() {
167 | const results = []
168 | let got = updateText([1, 2, 3])(cr => {results.push(cr)})
169 |
170 | It('calls mock handler for each cr in reverse order',
171 | results,
172 | [3, 2, 1]
173 | )
174 | It('returns undefined if no error',
175 | got,
176 | undefined
177 | )
178 |
179 | got = updateText([1, 2, 3])(cr => new Error('hello'))
180 | It('returns error if there is one',
181 | typeof got.message,
182 | 'string'
183 | )
184 | }
185 |
--------------------------------------------------------------------------------
/sidebar-style.html:
--------------------------------------------------------------------------------
1 |
234 |
--------------------------------------------------------------------------------
/text.gs:
--------------------------------------------------------------------------------
1 | const isCRUrl = codeLength => url => (new RegExp('^#[^_]{' + codeLength + '}_')).test(url)
2 |
3 | const handleRefNumber = numberForRefUrl => url => numberForRefUrl[url] || new Error('missref')
4 |
5 | const isCapitalized = str => str !== '' && str.charAt(0) === str.charAt(0).toUpperCase()
6 |
7 | const capitalize = str => str.charAt(0).toUpperCase() + str.slice(1)
8 |
9 |
10 | function codeFromUrl(url) {
11 | if (!url) return
12 | const match = url.match(/^#([^_]{3}|[^_]{5})_/)
13 | return match ? match[1] : null
14 | }
15 |
16 |
17 | /*
18 | @var numForRefUrl : {} -- map of `ref url` to `number for the corresponding label`
19 | @var countByLabelType : {} -- map of `label code` to `count of code in document`
20 | @var url : string -- the current url
21 | @return int | error -- the number for the current label
22 | */
23 | const handleLabNumber = numberForRefUrl => countByLabelType => url => {
24 | const code = codeFromUrl(url)
25 | const refEquivalent = '#' + code.substr(0, 3) + '_' + url.substr(7)
26 |
27 | const num = countByLabelType[code] + 1 || 1
28 |
29 | if (refEquivalent in numberForRefUrl) return new Error('duplicate')
30 |
31 | numberForRefUrl[refEquivalent] = num
32 | countByLabelType[code] = num
33 |
34 | return num
35 | }
36 |
37 |
38 | /*
39 | @var CRUrls : [{start, end, url}]
40 | @var handleCR : {start, end, url} -> ?error
41 | */
42 | const updateText = CRUrls => handleCR => {
43 | let i = CRUrls.length
44 | while (i--) { // iterate backwards because we're changing the underlying text length
45 | const error = handleCR(CRUrls[i])
46 | if (error) {
47 | return error
48 | }
49 | }
50 | }
51 |
52 | /*
53 | @var paragraphs : [paragraphs]
54 | @var getCRs : text -> [{start, end, url}]
55 | @var handleText : text -> [{start, end, url}] -> ?error
56 | @return ?error
57 | */
58 | const updateParagraphs = paragraphs => getCRs => handleText => {
59 | for (let i = 0, len = paragraphs.length; i < len; i++) {
60 | const text = paragraphs[i].editAsText()
61 |
62 | const CRUrls = getCRs(text)
63 | if (!CRUrls.length) continue
64 |
65 | const handleCR = handleText(text)
66 | const error = updateText(CRUrls)(handleCR)
67 | if (error) {
68 | return error
69 | }
70 | }
71 | }
72 |
73 |
74 | const replaceText = (text, CRUrl, replacementText, style) => {
75 | const { start, end, url } = CRUrl
76 | const replacementEnd = start + replacementText.length - 1
77 | const size = text.getFontSize(start)
78 |
79 | text.deleteText(start, end)
80 | .insertText(start, replacementText)
81 | .setLinkUrl(start, replacementEnd, url)
82 | .setAttributes(start, replacementEnd, style)
83 | .setFontSize(start, replacementEnd, size)
84 | }
85 |
86 |
87 | const getStyle = prop => ({
88 | 'BOLD': prop.isBold,
89 | 'ITALIC': prop.isItalic,
90 | 'UNDERLINE': prop.isUnderlined,
91 | 'FOREGROUND_COLOR': prop.color,
92 | })
93 |
94 | /*
95 | @var props : {} -- map of `code` to `properties`
96 | @var handleNumbering : {start, end, url} -> int
97 | @var text : Text
98 | @var CRUrl : {start, end, url}
99 | @return ?error
100 | */
101 | const handleCRUrl = props => handleNumbering => text => CRUrl => {
102 | const foundCode = codeFromUrl(CRUrl.url)
103 |
104 | const prop = props[foundCode]
105 | if (!prop) {
106 | return new CRError(text, CRUrl, 'unrecognised')
107 | }
108 |
109 | let replacementText = capitalizeIfAppropriate(text.getText(), CRUrl.start, prop.text)
110 | const num = handleNumbering(CRUrl.url)
111 | if (num instanceof Error) {
112 | return new CRError(text, CRUrl, num.message)
113 | }
114 |
115 | replacementText += num + prop.suffix
116 | replacementText = replacementText.replace(' ', '\xA0')
117 |
118 | const style = getStyle(prop)
119 |
120 | replaceText(text, CRUrl, replacementText, style)
121 | }
122 |
123 |
124 | /*
125 | @var props : {} -- map of `code` to `properties`
126 | @var handleNumbering : {start, end, url} -> int
127 | @var text : Text
128 | @var CRUrl : {start, end, url}
129 | @return ?error
130 | */
131 | const handleFootnoteLabCRUrl = props => handleNumbering => text => CRUrl => {
132 | const foundCode = codeFromUrl(CRUrl.url)
133 | if (foundCode !== 'fnote') return
134 |
135 | const num = handleNumbering(CRUrl.url)
136 | if (num instanceof Error) {
137 | return new CRError(text, CRUrl, num.message)
138 | }
139 |
140 | text.setAttributes(CRUrl.start, CRUrl.end, {'UNDERLINE': null, 'FOREGROUND_COLOR': '#000000'})
141 | }
142 |
143 |
144 | /*
145 | @var isCRUrl : string -> bool
146 | @var text : Text
147 | @return [{start, end, url}]
148 | */
149 | const getCRUrls = isCRUrl => text => {
150 | const textLength = text.getText().length
151 | const idxs = text.getTextAttributeIndices()
152 | idxs.push(textLength) // the final index is the end of the text
153 |
154 | const CRUrls = []
155 |
156 | for (let i = 0, len = idxs.length; i < len; i++) {
157 | const idx = idxs[i]
158 |
159 | const urlHere = idx !== textLength ? text.getLinkUrl(idx) : null
160 | const urlToTheLeft = i > 0 ? text.getLinkUrl(idx - 1) : null
161 |
162 | const isStart = !isCRUrl(urlToTheLeft) && isCRUrl(urlHere)
163 | const isEnd = isCRUrl(urlToTheLeft) && !isCRUrl(urlHere)
164 |
165 | if (isStart) {
166 | CRUrls.push({ start: idx, url: urlHere })
167 | }
168 | if (isEnd) {
169 | CRUrls[CRUrls.length - 1].end = idx - 1
170 | }
171 | }
172 | return CRUrls
173 | }
174 |
175 |
176 | const capitalizeIfAppropriate = (text, start, replacementText) =>
177 | isCapitalized(replacementText) || !isCapitalized(text.substr(start, start + 1))
178 | ? replacementText
179 | : capitalize(replacementText)
180 |
--------------------------------------------------------------------------------
/settings.gs:
--------------------------------------------------------------------------------
1 | // isCrossProp returns true if a string is a key from the gDocs property store
2 | const isCrossProp = propKey => propKey.substr(0, 6) === 'cross_'
3 |
4 | // getPropKey returns a key to be used in the gDocs property stores
5 | const getPropKey = labCode => 'cross_' + labCode.substr(0, 3)
6 |
7 | const refCodeFrom = labCode => labCode.substr(0, 3)
8 |
9 | const encodeSetting = s => JSON.stringify(s)
10 |
11 | const decodeSetting = s => JSON.parse(s)
12 |
13 | const isDefault = code => ['equ', 'fig', 'fno', 'tab'].includes(code.substr(0, 3))
14 |
15 |
16 | // encodeSettings returns an object where key is the key to be used
17 | // in the gDocs prop stores and value is the settings encoded as a string
18 | function encodeSettings(settings) {
19 | const result = {}
20 | for (const key in settings) {
21 | const setting = settings[key]
22 | result[getPropKey(setting.lab.code)] = encodeSetting(setting)
23 | }
24 | return result
25 | }
26 |
27 |
28 | // getSettings retrieves a combination of default, user-level and document-level
29 | // settings (in that order of priority)
30 | function getSettings() {
31 | let settings = getDefaultSettings()
32 | settings = patchSettings(settings, PropertiesService.getUserProperties())
33 | settings = patchSettings(settings, PropertiesService.getDocumentProperties())
34 |
35 | return settings
36 | }
37 |
38 |
39 | // patchSettings overwrites settings with those from a given gDocs property store
40 | function patchSettings(settings, propStore) {
41 | const props = propStore.getProperties()
42 |
43 | for (const key in props) {
44 | if (!isCrossProp(key)) continue
45 |
46 | const encoded = props[key]
47 |
48 | let s = null
49 | if (isLegacy(encoded)) {
50 | s = decodeLegacy(encoded)
51 | propStore.setProperty(key, encodeSetting(s)) // update legacy props
52 | } else {
53 | s = decodeSetting(encoded)
54 | }
55 |
56 | settings[s.lab.code] = s
57 | }
58 | return settings
59 | }
60 |
61 |
62 | // getProps returns the sub-settings for a particular type of cross reference
63 | // (e.g. label or reference) with codes as keys (e.g. {fig: {...}}, or {figur: {...}})
64 | const getProps = type => settings => {
65 | const props = {}
66 | for (const key in settings) {
67 | const setting = settings[key]
68 | props[setting[type].code] = setting[type]
69 | }
70 | return props
71 | }
72 |
73 |
74 | function clearPropStore(store) {
75 | for (const key in store) {
76 | if (isCrossProp(key)) {
77 | store.deleteProperty(key)
78 | }
79 | }
80 | }
81 |
82 |
83 | function updateDocProps() {
84 | const encoded = encodeSettings(getSettings())
85 | PropertiesService.getDocumentProperties().setProperties(encoded)
86 | }
87 |
88 |
89 | function getDefaultSettings() {
90 | return {
91 | equat: {
92 | name: 'Equation',
93 | lab: {
94 | code: 'equat',
95 | text: 'equation ',
96 | isBold: false,
97 | isItalic: false,
98 | isUnderlined: false,
99 | color: null,
100 | suffix: '',
101 | },
102 | ref: {
103 | code: 'equ',
104 | text: 'equation ',
105 | isBold: false,
106 | isItalic: false,
107 | isUnderlined: false,
108 | color: null,
109 | suffix: '',
110 | }
111 | },
112 | figur: {
113 | name: 'Figure',
114 | lab: {
115 | code: 'figur',
116 | text: 'figure ',
117 | isBold: false,
118 | isItalic: false,
119 | isUnderlined: false,
120 | color: null,
121 | suffix: '',
122 | },
123 | ref: {
124 | code: 'fig',
125 | text: 'figure ',
126 | isBold: false,
127 | isItalic: false,
128 | isUnderlined: false,
129 | color: null,
130 | suffix: '',
131 | }
132 | },
133 | fnote: {
134 | name: 'Footnote',
135 | lab: {
136 | code: 'fnote',
137 | text: '',
138 | isBold: false,
139 | isItalic: false,
140 | isUnderlined: false,
141 | color: null,
142 | suffix: '',
143 | },
144 | ref: {
145 | code: 'fno',
146 | text: 'fn. ',
147 | isBold: false,
148 | isItalic: false,
149 | isUnderlined: false,
150 | color: null,
151 | suffix: '',
152 | },
153 | },
154 | table: {
155 | name: 'Table',
156 | lab: {
157 | code: 'table',
158 | text: 'table ',
159 | isBold: false,
160 | isItalic: false,
161 | isUnderlined: false,
162 | color: null,
163 | suffix: '',
164 | },
165 | ref: {
166 | code: 'tab',
167 | text: 'table ',
168 | isBold: false,
169 | isItalic: false,
170 | isUnderlined: false,
171 | color: null,
172 | suffix: '',
173 | }
174 | }
175 | }
176 | }
177 |
178 | /* Required for legacy. Hardcoded as hell */
179 | const isLegacy = encoded => encoded.charAt(0) !== '{'
180 |
181 | const decodeLegacy = encoded => {
182 | const asArr = encoded.split('_')
183 | const bool = str => str === 'true'
184 | const realNull = str => str === 'null' ? null : str
185 |
186 | return {
187 | name: asArr[1],
188 | lab: {
189 | code: asArr[0],
190 | text: asArr[2],
191 | isBold: bool(asArr[3]),
192 | isItalic: bool(asArr[4]),
193 | isUnderlined: bool(asArr[5]),
194 | color: realNull(asArr[10]),
195 | suffix: '',
196 | },
197 | ref: {
198 | code: refCodeFrom(asArr[0]),
199 | text: asArr[6],
200 | isBold: bool(asArr[7]),
201 | isItalic: bool(asArr[8]),
202 | isUnderlined: bool(asArr[9]),
203 | color: realNull(asArr[11]),
204 | suffix: '',
205 | }
206 | }
207 | }
208 |
209 | /*
210 | Legacy properties example:
211 |
212 | */
213 |
214 | /** For debugging purposes */
215 |
216 | function setLegacyDocProps() {
217 | clearProps()
218 | PropertiesService.getDocumentProperties().setProperties({
219 | cross_tab: 'table_Table_table _null_null_null_table _null_null_null_null_null',
220 | cross_tes: 'testi_Testing_testing _null_null_null_testing _null_null_null_null_null',
221 | cross_fig: 'figur_Figure_figFIGFIG _null_null_null_figure _null_null_null_null_null',
222 | cross_equ: 'equat_Equation_equation _null_null_null_equation _null_null_null_null_null',
223 | cross_fno: 'fnote_Footnote__null_null_null_fn. _null_null_null_null_null'
224 | })
225 | }
226 |
227 | function clearProps() {
228 | PropertiesService.getDocumentProperties().deleteAllProperties()
229 | PropertiesService.getUserProperties().deleteAllProperties()
230 | }
231 |
232 | function viewProps() {
233 | Logger.log(PropertiesService.getDocumentProperties().getProperties())
234 | Logger.log(PropertiesService.getUserProperties().getProperties())
235 | }
236 |
--------------------------------------------------------------------------------
/sidebar-html.html:
--------------------------------------------------------------------------------
1 |