diff --git a/src/App.js b/src/App.js index c8dc13d6..9591c97e 100644 --- a/src/App.js +++ b/src/App.js @@ -41,7 +41,7 @@ import StyleKit from '@Style/StyleKit'; if (__DEV__ === false) { const bugsnag = new Client(); - // Disable console.log for non-dev builds + /** Disable console.log for non-dev builds */ console.log = () => {}; } @@ -80,9 +80,9 @@ const AppDrawerStack = createDrawerNavigator( openRightDrawer: () => DrawerActions.openDrawer({ key: stateKey }), closeRightDrawer: () => DrawerActions.closeDrawer({ key: stateKey }), lockRightDrawer: lock => { - // this is the key part + /** This is the key part */ SideMenuManager.get().setLockedForRightSideMenu(lock); - // We have to return something + /** We have to return something. */ return NavigationActions.setParams({ params: { dummy: true }, key: route.key, @@ -159,9 +159,9 @@ const DrawerStack = createDrawerNavigator( openLeftDrawer: () => DrawerActions.openDrawer({ key: stateKey }), closeLeftDrawer: () => DrawerActions.closeDrawer({ key: stateKey }), lockLeftDrawer: lock => { - // this is the key part + /** This is the key part. */ SideMenuManager.get().setLockedForLeftSideMenu(lock); - // We have to return something + /** We have to return something. */ return NavigationActions.setParams({ params: { dummy: true }, key: route.key, diff --git a/src/lib/utils.js b/src/lib/utils.js index fe987936..9267a507 100644 --- a/src/lib/utils.js +++ b/src/lib/utils.js @@ -17,9 +17,9 @@ export function isMatchCaseInsensitive(a, b) { * Returns a Date object from a JSON stringifed date */ export function dateFromJsonString(str) { - if(str) { + if (str) { return new Date(JSON.parse(str)); } return str; -} \ No newline at end of file +} diff --git a/src/screens/Authentication/Authenticate.js b/src/screens/Authentication/Authenticate.js index cee69e26..370b871a 100644 --- a/src/screens/Authentication/Authenticate.js +++ b/src/screens/Authentication/Authenticate.js @@ -87,6 +87,10 @@ export default class Authenticate extends Abstract { } submitPressed() { + if (this.activeSource && this.activeSource.isLocked()) { + return; + } + /** * If we just pressed submit on the only pending source left, disable * submit button @@ -187,7 +191,7 @@ export default class Authenticate extends Abstract { return; } - // Disable submit while we're processing. Will be re-enabled below. + /** Disable submit while we're processing. Will be re-enabled below. */ this.setState({ submitDisabled: true }); const result = await source.authenticate(); @@ -197,6 +201,7 @@ export default class Authenticate extends Abstract { this.forceUpdate(); } else if (source.isLocked()) { this.onSourceLocked(source); + return; } else { if (result.error && result.error.message) { Alert.alert('Unsuccessful', result.error.message); @@ -229,14 +234,14 @@ export default class Authenticate extends Abstract { /** * @private * When a source returns in a locked status we create a timeout for the lock - * period. + * period. This will auto reprompt the user for auth after the period is up. */ onSourceLocked(source) { this.setState({ sourceLocked: true, submitDisabled: true }); setTimeout(() => { - source.setWaitingForInput(); this.setState({ sourceLocked: false, submitDisabled: false }); + this.beginAuthenticationForSource(source); this.forceUpdate(); }, source.lockTimeout); } diff --git a/src/screens/Authentication/Sources/AuthenticationSourceBiometric.js b/src/screens/Authentication/Sources/AuthenticationSourceBiometric.js index 1847c372..a69d6d00 100644 --- a/src/screens/Authentication/Sources/AuthenticationSourceBiometric.js +++ b/src/screens/Authentication/Sources/AuthenticationSourceBiometric.js @@ -12,6 +12,7 @@ export default class AuthenticationSourceBiometric extends AuthenticationSource super.initializeForInterface(); KeysManager.getDeviceBiometricsAvailability((available, type, noun) => { + this.isReady = true; this.isAvailable = available; this.biometricsType = type; this.biometricsNoun = noun; @@ -32,7 +33,7 @@ export default class AuthenticationSourceBiometric extends AuthenticationSource } get title() { - return this.biometricsType; + return this.biometricsType || 'Biometrics'; } get isFingerprint() { @@ -78,7 +79,7 @@ export default class AuthenticationSourceBiometric extends AuthenticationSource } async authenticate() { - if (!this.isAvailable) { + if (this.isReady && !this.isAvailable) { this.didFail(); return { success: false, @@ -92,6 +93,7 @@ export default class AuthenticationSourceBiometric extends AuthenticationSource if (Platform.OS === 'android') { return FingerprintScanner.authenticate({ + deviceCredentialAllowed: true, description: 'Biometrics are required to access your notes.', }) .then(() => { @@ -102,6 +104,7 @@ export default class AuthenticationSourceBiometric extends AuthenticationSource if (error.name === 'DeviceLocked') { this.setLocked(); + FingerprintScanner.release(); return this._fail( 'Authentication failed. Wait 30 seconds to try again.' ); diff --git a/src/screens/Settings/Settings.js b/src/screens/Settings/Settings.js index 75336fb0..1e6e82ff 100644 --- a/src/screens/Settings/Settings.js +++ b/src/screens/Settings/Settings.js @@ -1,8 +1,5 @@ -import React, { Component } from 'react'; -import { - ScrollView, - Alert -} from 'react-native'; +import React from 'react'; +import { ScrollView, Alert } from 'react-native'; import { SafeAreaView } from 'react-navigation'; import SectionHeader from '@Components/SectionHeader'; import SectionedAccessoryTableCell from '@Components/SectionedAccessoryTableCell'; @@ -16,10 +13,7 @@ import SF from '@SFJS/sfjs'; import Storage from '@SFJS/storageManager'; import Sync from '@SFJS/syncManager'; import Abstract from '@Screens/Abstract'; -import { - SCREEN_INPUT_MODAL, - SCREEN_MANAGE_PRIVILEGES -} from '@Screens/screens'; +import { SCREEN_INPUT_MODAL, SCREEN_MANAGE_PRIVILEGES } from '@Screens/screens'; import AuthSection from '@Screens/Settings/Sections/AuthSection'; import CompanySection from '@Screens/Settings/Sections/CompanySection'; import EncryptionSection from '@Screens/Settings/Sections/EncryptionSection'; @@ -29,16 +23,21 @@ import { ICON_CHECKMARK } from '@Style/icons'; import StyleKit from '@Style/StyleKit'; export default class Settings extends Abstract { - static navigationOptions = ({ navigation, navigationOptions }) => { const templateOptions = { - title: "Settings", + title: 'Settings', leftButton: { - title: ApplicationState.isIOS ? "Done" : null, - iconName: ApplicationState.isIOS ? null : StyleKit.nameForIcon(ICON_CHECKMARK), - } - } - return Abstract.getDefaultNavigationOptions({navigation, navigationOptions, templateOptions}); + title: ApplicationState.isIOS ? 'Done' : null, + iconName: ApplicationState.isIOS + ? null + : StyleKit.nameForIcon(ICON_CHECKMARK), + }, + }; + return Abstract.getDefaultNavigationOptions({ + navigation, + navigationOptions, + templateOptions, + }); }; constructor(props) { @@ -46,31 +45,33 @@ export default class Settings extends Abstract { props.navigation.setParams({ leftButton: { - title: ApplicationState.isIOS ? "Done" : null, - iconName: ApplicationState.isIOS ? null : StyleKit.nameForIcon(ICON_CHECKMARK), + title: ApplicationState.isIOS ? 'Done' : null, + iconName: ApplicationState.isIOS + ? null + : StyleKit.nameForIcon(ICON_CHECKMARK), onPress: () => { this.dismiss(); - } - } - }) + }, + }, + }); - if(__DEV__) { + if (__DEV__) { props.navigation.setParams({ rightButton: { - title: "Destroy Data", + title: 'Destroy Data', onPress: () => { Storage.get().clear(); Auth.get().signout(); KeysManager.get().clearOfflineKeysAndData(true); - } - } - }) + }, + }, + }); } this.sortOptions = [ - {key: "created_at", label: "Date Added"}, - {key: "client_updated_at", label: "Date Modified"}, - {key: "title", label: "Title"}, + { key: 'created_at', label: 'Date Added' }, + { key: 'client_updated_at', label: 'Date Modified' }, + { key: 'title', label: 'Title' }, ]; this.options = ApplicationState.getOptions(); @@ -84,10 +85,14 @@ export default class Settings extends Abstract { } loadSecurityStatus() { - var hasPasscode = KeysManager.get().hasOfflinePasscode(); - var hasBiometrics = KeysManager.get().hasBiometrics(); - var encryptedStorage = KeysManager.get().isStorageEncryptionEnabled(); - this.mergeState({hasPasscode: hasPasscode, hasBiometrics: hasBiometrics, storageEncryption: encryptedStorage}) + const hasPasscode = KeysManager.get().hasOfflinePasscode(); + const hasBiometrics = KeysManager.get().hasBiometrics(); + const encryptedStorage = KeysManager.get().isStorageEncryptionEnabled(); + this.mergeState({ + hasPasscode: hasPasscode, + hasBiometrics: hasBiometrics, + storageEncryption: encryptedStorage, + }); } componentWillUnmount() { @@ -102,215 +107,278 @@ export default class Settings extends Abstract { } resaveOfflineData(callback, updateAfter = false) { - Sync.get().resaveOfflineData().then(() => { - if(updateAfter) { - this.forceUpdate(); - } - callback && callback(); - }); + Sync.get() + .resaveOfflineData() + .then(() => { + if (updateAfter) { + this.forceUpdate(); + } + callback && callback(); + }); } onSignOutPress = () => { AlertManager.get().confirm({ - title: "Sign Out?", - text: "Signing out will remove all data from this device, including notes and tags. Make sure your data is synced before proceeding.", - confirmButtonText: "Sign Out", + title: 'Sign Out?', + text: 'Signing out will remove all data from this device, including notes and tags. Make sure your data is synced before proceeding.', + confirmButtonText: 'Sign Out', onConfirm: () => { - Auth.get().signout().then(() => { - this.forceUpdate(); - }) - } - }) - } + Auth.get() + .signout() + .then(() => { + this.forceUpdate(); + }); + }, + }); + }; onStorageEncryptionEnable = () => { AlertManager.get().confirm({ - title: "Enable Storage Encryption?", - text: "Storage encryption improves your security by encrypting your data on your device. It may increase app start-up time.", - confirmButtonText: "Enable", + title: 'Enable Storage Encryption?', + text: 'Storage encryption improves your security by encrypting your data on your device. It may increase app start-up time.', + confirmButtonText: 'Enable', onConfirm: () => { - this.mergeState({storageEncryptionLoading: true}); + this.mergeState({ storageEncryptionLoading: true }); KeysManager.get().enableStorageEncryption(); this.resaveOfflineData(() => { - this.mergeState({storageEncryption: true, storageEncryptionLoading: false}); + this.mergeState({ + storageEncryption: true, + storageEncryptionLoading: false, + }); }); - } - }) - } + }, + }); + }; onStorageEncryptionDisable = () => { AlertManager.get().confirm({ - title: "Disable Storage Encryption?", - text: "Storage encryption improves your security by encrypting your data on your device. Disabling it can improve app start-up speed.", - confirmButtonText: "Disable", + title: 'Disable Storage Encryption?', + text: 'Storage encryption improves your security by encrypting your data on your device. Disabling it can improve app start-up speed.', + confirmButtonText: 'Disable', onConfirm: () => { - this.mergeState({storageEncryptionLoading: true}); + this.mergeState({ storageEncryptionLoading: true }); KeysManager.get().disableStorageEncryption(); this.resaveOfflineData(() => { - this.mergeState({storageEncryption: false, storageEncryptionLoading: false}); + this.mergeState({ + storageEncryption: false, + storageEncryptionLoading: false, + }); }); - } - }) - } + }, + }); + }; onPasscodeEnable = () => { this.props.navigation.navigate(SCREEN_INPUT_MODAL, { - title: "Setup Passcode", - placeholder: "Enter a passcode", - confirmPlaceholder: "Confirm passcode", + title: 'Setup Passcode', + placeholder: 'Enter a passcode', + confirmPlaceholder: 'Confirm passcode', secureTextEntry: true, requireConfirm: true, showKeyboardChooser: true, onSubmit: async (value, keyboardType) => { - Storage.get().setItem("passcodeKeyboardType", keyboardType); + Storage.get().setItem('passcodeKeyboardType', keyboardType); let identifier = await SF.get().crypto.generateUUID(); - SF.get().crypto.generateInitialKeysAndAuthParamsForUser(identifier, value).then((results) => { - let keys = results.keys; - let authParams = results.authParams; + SF.get() + .crypto.generateInitialKeysAndAuthParamsForUser(identifier, value) + .then(results => { + let keys = results.keys; + let authParams = results.authParams; - // make sure it has valid items - if(_.keys(keys).length > 0) { - KeysManager.get().setOfflineAuthParams(authParams); - KeysManager.get().persistOfflineKeys(keys); - var encryptionSource = KeysManager.get().encryptionSource(); - if(encryptionSource == "offline") { - this.resaveOfflineData(null, true); + // make sure it has valid items + if (_.keys(keys).length > 0) { + KeysManager.get().setOfflineAuthParams(authParams); + KeysManager.get().persistOfflineKeys(keys); + const encryptionSource = KeysManager.get().encryptionSource(); + if (encryptionSource === 'offline') { + this.resaveOfflineData(null, true); + } + } else { + Alert.alert( + 'Passcode Error', + 'There was an error setting up your passcode. Please try again.' + ); } - } else { - this.mergeState({setupButtonText: SAVE_BUTTON_DEFAULT_TEXT, setupButtonEnabled: true}); - Alert.alert("Passcode Error", "There was an error setting up your passcode. Please try again."); - } - }); - } + }); + }, }); - } + }; onPasscodeDisable = () => { - this.handlePrivilegedAction(true, SFPrivilegesManager.ActionManagePasscode, () => { - var encryptionSource = KeysManager.get().encryptionSource(); - var message; - if(encryptionSource == "account") { - message = "Are you sure you want to disable your local passcode? This will not affect your encryption status, as your data is currently being encrypted through your sync account keys."; - } else if(encryptionSource == "offline") { - message = "Are you sure you want to disable your local passcode? This will disable encryption on your data."; - } - - AlertManager.get().confirm({ - title: "Disable Passcode", - text: message, - confirmButtonText: "Disable Passcode", - onConfirm: async () => { - var result = await KeysManager.get().clearOfflineKeysAndData(); - if(encryptionSource == "offline") { - // remove encryption from all items - this.resaveOfflineData(null, true); - } - - this.mergeState({hasPasscode: false}); - this.forceUpdate(); + this.handlePrivilegedAction( + true, + SFPrivilegesManager.ActionManagePasscode, + () => { + const encryptionSource = KeysManager.get().encryptionSource(); + let message; + if (encryptionSource === 'account') { + message = 'Are you sure you want to disable your local passcode? This will not affect your encryption status, as your data is currently being encrypted through your sync account keys.'; + } else if (encryptionSource === 'offline') { + message = 'Are you sure you want to disable your local passcode? This will disable encryption on your data.'; } - }) - }) - } + + AlertManager.get().confirm({ + title: 'Disable Passcode', + text: message, + confirmButtonText: 'Disable Passcode', + onConfirm: async () => { + await KeysManager.get().clearOfflineKeysAndData(); + + if (encryptionSource === 'offline') { + // remove encryption from all items + this.resaveOfflineData(null, true); + } + + this.mergeState({ hasPasscode: false }); + this.forceUpdate(); + }, + }); + } + ); + }; onFingerprintEnable = () => { KeysManager.get().enableBiometrics(); this.loadSecurityStatus(); - } + }; onFingerprintDisable = () => { - this.handlePrivilegedAction(true, SFPrivilegesManager.ActionManagePasscode, () => { - KeysManager.get().disableBiometrics(); - this.loadSecurityStatus(); - }); - } + this.handlePrivilegedAction( + true, + SFPrivilegesManager.ActionManagePasscode, + () => { + KeysManager.get().disableBiometrics(); + this.loadSecurityStatus(); + } + ); + }; - onSortChange = (key) => { + onSortChange = key => { this.options.setSortBy(key); this.forceUpdate(); - } + }; - onOptionSelect = (option) => { - this.options.setDisplayOptionKeyValue(option, !this.options.getDisplayOptionValue(option)); + onOptionSelect = option => { + this.options.setDisplayOptionKeyValue( + option, + !this.options.getDisplayOptionValue(option) + ); this.forceUpdate(); - } + }; toggleSortReverse = () => { this.options.setSortReverse(!this.options.sortReverse); this.forceUpdate(); - } + }; openManagePrivs = () => { - this.handlePrivilegedAction(true, SFPrivilegesManager.ActionManagePrivileges, () => { - this.props.navigation.navigate(SCREEN_MANAGE_PRIVILEGES) - }) - } + this.handlePrivilegedAction( + true, + SFPrivilegesManager.ActionManagePrivileges, + () => { + this.props.navigation.navigate(SCREEN_MANAGE_PRIVILEGES); + } + ); + }; render() { - if(this.state.lockContent) { - return (); + if (this.state.lockContent) { + return ; } - let signedIn = !Auth.get().offline(); + const signedIn = !Auth.get().offline(); return ( - - - - {!signedIn && !this.state.confirmRegistration && + + + {!signedIn && !this.state.confirmRegistration && ( {this.dismiss()}} + title={'Account'} + onAuthSuccess={() => { + this.dismiss(); + }} /> - } + )} - + {this.sortOptions.map((option, i) => { return ( {this.onSortChange(option.key)}} + onPress={() => { + this.onSortChange(option.key); + }} text={option.label} key={option.key} first={i == 0} last={i == this.sortOptions.length - 1} - selected={() => {return option.key == this.options.sortBy}} + selected={() => { + return option.key === this.options.sortBy; + }} /> - ) + ); })} - + {this.onOptionSelect('hidePreviews')}} - text={"Hide note previews"} + onPress={() => { + this.onOptionSelect('hidePreviews'); + }} + text={'Hide note previews'} first={true} - selected={() => {return this.options.hidePreviews}} + selected={() => { + return this.options.hidePreviews; + }} /> {this.onOptionSelect('hideTags')}} - text={"Hide note tags"} - selected={() => {return this.options.hideTags}} + onPress={() => { + this.onOptionSelect('hideTags'); + }} + text={'Hide note tags'} + selected={() => { + return this.options.hideTags; + }} /> {this.onOptionSelect('hideDates')}} - text={"Hide note dates"} + onPress={() => { + this.onOptionSelect('hideDates'); + }} + text={'Hide note dates'} last={true} - selected={() => {return this.options.hideDates}} + selected={() => { + return this.options.hideDates; + }} /> - - - - + + ); diff --git a/src/screens/SideMenu/SideMenuManager.js b/src/screens/SideMenu/SideMenuManager.js index a71ed227..872cd9fc 100644 --- a/src/screens/SideMenu/SideMenuManager.js +++ b/src/screens/SideMenu/SideMenuManager.js @@ -1,19 +1,13 @@ -import React, { Component, Fragment } from 'react'; -import { ScrollView, View, Text, FlatList } from 'react-native'; - -import ApplicationState from "@Lib/ApplicationState" - -/* - Because SideMenus (SideMenu and NoteSideMenu) are rendering by React Navigation as drawer components - on app startup, we can't give them params at will. We need a way for components like the Compose - screen to talk to the NoteSideMenu and give it the current note context. The only way seems to be - some shared singleton object, which is this. - - This object will handle state for both side menus. -*/ - +/** + * Because SideMenus (SideMenu and NoteSideMenu) are rendering by React + * Navigation as drawer components on app startup, we can't give them params at + * will. We need a way for components like the Compose screen to talk to the + * NoteSideMenu and give it the current note context. The only way seems to be + * some shared singleton object, which is this. + * + * This object will handle state for both side menus. + */ export default class SideMenuManager { - static instance = null; static get() { if (this.instance == null) { @@ -24,35 +18,41 @@ export default class SideMenuManager { } setLeftSideMenuReference(ref) { - // The ref handler of the main component sometimes passes null, then passes the correct reference - if(!this.leftSideMenu) { + /** + * The ref handler of the main component sometimes passes null, then passes + * the correct reference + */ + if (!this.leftSideMenu) { this.leftSideMenu = ref; } } setRightSideMenuReference(ref) { - // The ref handler of the main component sometimes passes null, then passes the correct reference - if(!this.rightSideMenu) { + /** + * The ref handler of the main component sometimes passes null, then passes + * the correct reference + */ + if (!this.rightSideMenu) { this.rightSideMenu = ref; } } - /* - @param handler.onEditorSelect - @param handler.onTagSelect - @param handler.getSelectedTags - */ + /** + * @param handler.onEditorSelect + * @param handler.onTagSelect + * @param handler.getSelectedTags + */ setHandlerForLeftSideMenu(handler) { this.leftSideMenuHandler = handler; return handler; } - /* - @param handler.onTagSelect - @param handler.getSelectedTags - @param handler.getCurrentNote - */ + /** + * @param handler.onTagSelect + * @param handler.getSelectedTags + * @param handler.getCurrentNote + */ setHandlerForRightSideMenu(handler) { this.rightSideMenuHandler = handler; @@ -72,7 +72,7 @@ export default class SideMenuManager { removeHandlerForRightSideMenu(handler) { // In tablet switching mode, a new Compose window may be created before the first one unmounts. // If an old instance asks us to remove handler, we want to make sure it's not the new one - if(handler == this.rightSideMenuHandler) { + if (handler === this.rightSideMenuHandler) { this.rightSideMenuHandler = null; } } @@ -92,5 +92,4 @@ export default class SideMenuManager { isRightSideMenuLocked() { return this.rightSideMenuLocked; } - } diff --git a/src/style/Util/CSSParser.js b/src/style/Util/CSSParser.js index 2c277971..3b37c915 100644 --- a/src/style/Util/CSSParser.js +++ b/src/style/Util/CSSParser.js @@ -1,31 +1,31 @@ -const GenericVarPrefix = "--"; -const StylekitPrefix = "--sn-stylekit"; -const StylekitPrefixToBurn = "--sn-"; +const PREFIX_GENERIC = '--'; +const PREFIX_STANDARD_NOTES = '--sn-stylekit'; +const PREFIX_STANDARD_NOTES_BURN = '--sn-'; export default class CSSParser { - /* - @param css: CSS file contents in string format - */ + /** + * @param css: CSS file contents in string format + */ static cssToObject(css) { - let object = {}; - let lines = css.split("\n"); + const object = {}; + const lines = css.split('\n'); - for(var line of lines) { + for (let line of lines) { line = line.trim(); - if(line.startsWith(GenericVarPrefix)) { - // Remove initial "--" - if(line.startsWith(StylekitPrefix)) { - line = line.slice(StylekitPrefixToBurn.length, line.length); + if (line.startsWith(PREFIX_GENERIC)) { + // Remove initial '--' + if (line.startsWith(PREFIX_STANDARD_NOTES)) { + line = line.slice(PREFIX_STANDARD_NOTES_BURN.length, line.length); } else { // Not all vars start with --sn-stylekit. e.g --background-color - line = line.slice(GenericVarPrefix.length, line.length); + line = line.slice(PREFIX_GENERIC.length, line.length); } - let parts = line.split(":"); + const parts = line.split(':'); let key = parts[0].trim(); - let value = parts[1].trim();; + let value = parts[1].trim(); key = this.hyphenatedStringToCamelCase(key); - value = value.replace(";", "").trim(); + value = value.replace(';', '').trim(); object[key] = value; } @@ -37,18 +37,21 @@ export default class CSSParser { } static resolveVariablesThatReferenceOtherVariables(object, round = 0) { - for(const key of Object.keys(object)) { - let value = object[key]; - let stripValue = "var("; - if(typeof value == "string" && value.startsWith(stripValue)) { - let from = stripValue.length; - let to = value.indexOf(")"); + for (const key of Object.keys(object)) { + const value = object[key]; + const stripValue = 'var('; + if (typeof value === 'string' && value.startsWith(stripValue)) { + const from = stripValue.length; + const to = value.indexOf(')'); let varName = value.slice(from, to); - if(varName.startsWith(StylekitPrefix)) { - varName = varName.slice(StylekitPrefixToBurn.length, varName.length); + if (varName.startsWith(PREFIX_STANDARD_NOTES)) { + varName = varName.slice( + PREFIX_STANDARD_NOTES_BURN.length, + varName.length + ); } else { // Not all vars start with --sn-stylekit. e.g --background-color - varName = varName.slice(GenericVarPrefix.length, varName.length); + varName = varName.slice(PREFIX_GENERIC.length, varName.length); } varName = this.hyphenatedStringToCamelCase(varName); object[key] = object[varName]; @@ -59,17 +62,17 @@ export default class CSSParser { // the left hand counterparts. In the first round, variables on rhs mentioned before // its value has been gotten to in the loop will be missed. The second round takes care of this // and makes sure that all values will resolve. - if(round == 0) { + if (round === 0) { this.resolveVariablesThatReferenceOtherVariables(object, ++round); } } static hyphenatedStringToCamelCase(string) { - let comps = string.split("-"); - let result = ""; - for(var i = 0; i < comps.length; i++) { + const comps = string.split('-'); + let result = ''; + for (let i = 0; i < comps.length; i++) { let part = comps[i]; - if(i == 0) { + if (i === 0) { result += part; } else { result += this.capitalizeFirstLetter(part);