How to build an Input field with restrictions - html

Hello everyone I am a junior web dev. and I've been stuck on this task for weeks now :[]. I need an input field that will show default text coming from the backend. Restrictions I need for this input field are: The input line should break after reaching 30 characters and some parts of default text should be noneditable.
//Here's the closest solution that I came up
import React, { useEffect } from "react";
import styles from "./TextEditor.module.css";
const TextEditor = (props) => {
var limit = 80000; //height limit
let defaultvalueArr = props.data.variables.map((vari, index) => {
return vari.texts.map((p, index) => {
return p;
});
});
useEffect(() => {
const box = document.getElementById(props.title);
var charlimit = 30; // char limit per line
var space;
box.onkeyup = function () {
var lines = box.value.split("\n");
console.log(box.value, "box.value");
for (var i = 0; i < lines.length; i++) {
if (lines[i].length <= charlimit) continue;
var j = 0;
space = charlimit;
while (j++ <= charlimit) {
if (lines[i].charAt(j) === " ") space = j;
}
lines[i + 1] = lines[i].substring(space + 1) + (lines[i + 1] || "");
lines[i] = lines[i].substring(0, space);
}
box.value = lines.slice(0, 500).join("\n");
};
box.oninput = function() {
box.style.height = "";
box.style.height = Math.min(box.scrollHeight, limit) + "px";
};
box.value = defaultvalueArr.join("\n");
}, []);
return (
<div className={styles.TextEditor}>
<textarea
id={props.title}
className={styles.TextArea}
// rows={defaultvalueArr[0].length}
></textarea>
</div>
);
};
export default TextEditor;
output
the problem with this solution is that when the number of characters reaches 30 the cursor jumps at the end of text

Related

The asignCards() function is supposed to give random cards from the cards array to each player

This asignCards() function is supposed to give random cards from the cards array to each player. The cards are not supposed to be repeated but I get repeated cards.
// For each player in the array of players, each player receives two cards.
const asignCards = () => {
players.forEach((player) => {
let useCards = [];
let inUse = cards;
for (let i = 0; i < 1; i++) {
let random = Math.floor(Math.random() * 52);
let random2 = Math.floor(Math.random() * 52);
if (
useCards.indexOf(JSON.stringify(inUse[random])) === -1 ||
useCards.indexOf(JSON.stringify(inUse[random2])) === -1 ||
useCards.indexOf(JSON.stringify(inUse[random])) !==
useCards.indexOf(JSON.stringify(inUse[random2]))
) {
player.card1 = inUse[random];
player.card2 = inUse[random2];
useCards.push(JSON.stringify(player.card1));
useCards.push(JSON.stringify(player.card2));
}else {
i--;
}
}
console.log(player);
});
};
asignCards();
You should declare your useCards array outside of the loop, otherwise everytime it will be empty at each iteration. Put it that way, you can not have duplicate for a player, but the same card can be assigned to more players.
const asignCards = () => {
let useCards = [];
players.forEach((player) => {
let inUse = cards;
for (let i = 0; i < 1; i++) {
let random = Math.floor(Math.random() * 52);
let random2 = Math.floor(Math.random() * 52);
if (
useCards.indexOf(JSON.stringify(inUse[random])) === -1 ||
useCards.indexOf(JSON.stringify(inUse[random2])) === -1 ||
useCards.indexOf(JSON.stringify(inUse[random])) !==
useCards.indexOf(JSON.stringify(inUse[random2]))
) {
player.card1 = inUse[random];
player.card2 = inUse[random2];
useCards.push(JSON.stringify(player.card1));
useCards.push(JSON.stringify(player.card2));
}else {
i--;
}
}
console.log(player);
});
};
asignCards();
Bonus: you may want to rename your function to assignCards (from assign) with double s.
Update:
Here's a different approach. Every time you want to play you shuffle the deck first, then pick your amount of cards from the top of the deck with Array.pop(), so you're 100% sure there will be no duplicates.
let players = ["A", "B", "C", "D"];
let cards = [...Array(52).keys()];
const cardsPerPlayer = 2;
const asignCards = () => {
// Shuffle deck
var n = cards.length;
while (n--) {
var i = Math.floor(n * Math.random());
var tmp = cards[i];
cards[i] = cards[n];
cards[n] = tmp;
}
players.forEach((player, index) => {
let myCards = [];
for (c = 0; c < cardsPerPlayer; c++) {
myCards.push(cards.pop());
}
console.log(`${player} has the following cards: ${myCards}`);
});
};
asignCards();

ReactJS handle tab character in textarea

How can I handle tab key pressed events in ReactJS so I'm able to indent text inside a textarea?
onChange event does not get fired when tab is pressed on the textarea, so I guess there might be a higher level handler I can use to detect this event.
onKeyDown={(e: React.KeyboardEvent) => {
if (e.key === 'Tab' && !e.shiftKey) {
e.preventDefault();
const value = this.textareaRef.current!.value;
const selectionStart = this.textareaRef.current!.selectionStart;
const selectionEnd = this.textareaRef.current!.selectionEnd;
this.textareaRef.current!.value =
value.substring(0, selectionStart) + ' ' + value.substring(selectionEnd);
this.textareaRef.current!.selectionStart = selectionEnd + 2 - (selectionEnd - selectionStart);
this.textareaRef.current!.selectionEnd = selectionEnd + 2 - (selectionEnd - selectionStart);
}
if (e.key === 'Tab' && e.shiftKey) {
e.preventDefault();
const value = this.textareaRef.current!.value;
const selectionStart = this.textareaRef.current!.selectionStart;
const selectionEnd = this.textareaRef.current!.selectionEnd;
const beforeStart = value
.substring(0, selectionStart)
.split('')
.reverse()
.join('');
const indexOfTab = beforeStart.indexOf(' ');
const indexOfNewline = beforeStart.indexOf('\n');
if (indexOfTab !== -1 && indexOfTab < indexOfNewline) {
this.textareaRef.current!.value =
beforeStart
.substring(indexOfTab + 2)
.split('')
.reverse()
.join('') +
beforeStart
.substring(0, indexOfTab)
.split('')
.reverse()
.join('') +
value.substring(selectionEnd);
this.textareaRef.current!.selectionStart = selectionStart - 2;
this.textareaRef.current!.selectionEnd = selectionEnd - 2;
}
}
}}
you can try onKeyDown and get the keycode for tab.
add: function(event){
console.log(event.keyCode); //press TAB and get the keyCode
},
render: function(){
return(
<div>
<input type="text" id="one" onKeyDown={this.add} />
</div>
);
}
Just in case someone wants a slightly updated and (in my opinion, enhanced) React Hooks version of vipe's solution in TypeScript:
Implementation Usage Example:
<EnhancedTextArea
ref={txtCodeInput} {/* reference used whenever required as seen below */}
className='code-input'
tabSize={2}
onTextChange={handleCodeChange} {/* Function accepting callback of type (string) -> void, called every time code is changed */}
/>
Getting Text:
const txtCodeInput = useRef<EnhancedTextAreaRefs>(null);
...
const codeContent = txtCodeInput.current?.getCodeContent();
EnhancedTextArea.tsx:
import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
type EnhancedTextAreaProps = {
onTextChange?: (text: string) => void,
className?: string,
spellCheck?: boolean,
tabSize?: number,
};
export type EnhancedTextAreaRefs = {
getCodeContent: () => string;
}
const EnhancedTextArea = forwardRef<EnhancedTextAreaRefs, EnhancedTextAreaProps>(({
onTextChange = undefined,
className = undefined,
tabSize = 4,
spellCheck = false,
}: EnhancedTextAreaProps, ref) => {
const [text, setText] = useState('');
const [stateSelectionStart, setStateSelectionStart] = useState(0);
const [stateSelectionEnd, setStateSelectionEnd] = useState(0);
const txtInput = useRef<HTMLTextAreaElement>(null);
useImperativeHandle(ref, () => ({
getCodeContent: () => text,
}));
useEffect(() => {
const textArea = txtInput.current;
if (!textArea) {
return;
}
if (stateSelectionStart >= 0) {
textArea.selectionStart = stateSelectionStart;
}
if (stateSelectionEnd >= 0) {
textArea.selectionEnd = stateSelectionEnd;
}
}, [text, stateSelectionStart, stateSelectionEnd]);
async function handleCodeChange(e: React.ChangeEvent<HTMLTextAreaElement>): Promise<void> {
const text = e.target.value;
setText(text);
if (onTextChange) {
onTextChange(text);
}
}
async function handleKeyDown(e: React.KeyboardEvent<HTMLTextAreaElement>): Promise<void> {
const textArea = e.target as HTMLTextAreaElement;
const tabString = ' '.repeat(tabSize);
const value = textArea.value;
const selectionStart = textArea.selectionStart;
const selectionEnd = textArea.selectionEnd;
if (e.key === 'Tab' && !e.shiftKey) {
e.preventDefault();
if (selectionStart !== selectionEnd) {
const slices1 = getNewLineSlices(value, selectionStart, selectionEnd);
const newValue1 = addTabs(value, slices1, tabString);
setText(newValue1);
setStateSelectionStart(selectionStart + tabSize);
setStateSelectionEnd(selectionEnd + (newValue1.length - value.length));
} else {
const newValue2 = value.substring(0, selectionStart) + tabString + value.substring(selectionEnd);
setText(newValue2);
setStateSelectionStart(selectionEnd + tabSize - (selectionEnd - selectionStart));
setStateSelectionEnd(selectionEnd + tabSize - (selectionEnd - selectionStart));
}
} else if (e.key === 'Tab' && e.shiftKey) {
e.preventDefault();
const slices2 = getNewLineSlices(value, selectionStart, selectionEnd);
const newValue3 = removeTabs(value, slices2, tabSize);
const diff = value.length - newValue3.length;
setText(newValue3);
setStateSelectionStart(Math.max(0, selectionStart - (diff ? tabSize : 0)));
setStateSelectionEnd(Math.max(0, selectionEnd - diff));
} else {
setStateSelectionStart(-1);
setStateSelectionEnd(-1);
}
}
function getNewLineSlices(value: string, selectionStart: number, selectionEnd: number): Array<string | null> {
const newLineLocations = getAllIndices(value, '\n');
const left = findRange(newLineLocations, selectionStart);
const split = value.split('\n');
const arr = [];
let count = 0;
for (let i = 0; i < split.length; i++) {
const line = split[i];
if (count > left && count <= selectionEnd) {
arr.push(line);
} else {
arr.push(null);
}
count += line.length + 1;
}
return arr;
}
function addTabs(value: string, arr: Array<string | null>, joiner: string): string {
const split = value.split('\n');
let ret = '';
for (let i = 0; i < split.length; i++) {
const val = split[i];
const newLineVal = arr[i];
if (newLineVal === val) {
ret += joiner;
}
ret += val;
if (i !== split.length - 1) {
ret += '\n';
}
}
return ret;
}
function removeTabs(value: string, arr: Array<string | null>, tabSize: number): string {
const split = value.split('\n');
let ret = '';
for (let i = 0; i < split.length; i++) {
const val = split[i];
const newLineVal = arr[i];
if (!val.startsWith(' ') || newLineVal !== val) {
ret += val;
if (i !== split.length - 1) {
ret += '\n';
}
continue;
}
let count = 1;
while (val[count] === ' ' && count < tabSize) {
count++;
}
ret += val.substring(count);
if (i !== split.length - 1) {
ret += '\n';
}
}
return ret;
}
function getAllIndices(arr: string, val: string): Array<number> {
const indices = [];
let i = -1;
while ((i = arr.indexOf(val, i + 1)) !== -1){
indices.push(i);
}
return indices;
}
function findRange(arr: Array<number>, min: number): number {
for (let i = 0; i < arr.length; i++) {
if (arr[i] >= min) {
return i === 0 ? -1 : arr[i - 1];
}
}
return arr[arr.length - 1];
}
return(
<textarea
ref={txtInput}
value={text}
onKeyDown={handleKeyDown}
onChange={handleCodeChange}
className={className}
spellCheck={spellCheck} />
);
});
EnhancedTextArea.displayName = 'EnhancedTextArea';
export default EnhancedTextArea;
My solution using useRef() for functional component :
const codeAreaRef = useRef();
const [code, setCode] = useState('');
//--------------
<textarea
name='code'
value={code}
ref={codeAreaRef}
onChange={(e) => {
setCode(e.target.value);
}}
onKeyDown={(e) => {
if (e.key == 'Tab') {
e.preventDefault();
const { selectionStart, selectionEnd } = e.target;
const newText =
code.substring(0, selectionStart) +
' ' + // Edit this for type tab you want
// Here it's 2 spaces per tab
code.substring(selectionEnd, code.length);
codeAreaRef.current.focus();
codeAreaRef.current.value = newText;
codeAreaRef.current.setSelectionRange(
selectionStart + 2,
selectionStart + 2
);
setCode(newText);
}
}}
/>

Call truncate function inside .each loop when populating select options?

I'm trying to dynamically populate a select and call a truncate function in the loop... like below. I want to send the option text down to the function, truncate it if it's longer than 20 chars and send it back before it gets added to the option and appended to the select.
$(function() {
for (var i = 0; i < response.option.length; i++) {
var truncatedText = truncate();
var text = response.option[i].name;
truncate(text);
$("select").append("<option>" + truncatedText.text + "</option>");
}
});
function truncate(text) {
var textLength = text.length;
if (textLength > 20) {
text = text.substr(0, 20) + '...';
}
return text;
}
After jsfiddling for a while I landed on a working solution. Is there a more elegant way to do this?
https://jsfiddle.net/kirkbross/pcb0a3Lg/9/
var namesList = ['johnathan', 'tim', 'greggory', 'ashton', 'elizabeth'];
$(function() {
for (var i = 0; i < 5; i++) {
var name = namesList[i];
$('#names').append('<option>' + name + '</option>');
}
var selected_option = $('#names').find('option:selected').val();
var truncated = truncate(selected_option);
$('option:selected').text(truncated.new);
$('#names').change(function(){
var selected_option = $(this).find('option:selected').val();
var truncated = truncate(selected_option);
$('option:selected').text(truncated.new);
});
});
function truncate(selected_option) {
var nameLength = selected_option.length
if (nameLength > 4) {
selected_option = selected_option.substr(0, 4) + '...';
}
return {new: selected_option}
}

Funky IE JSON conversions

When running our AngularJS app in IE11 everything looks great in the debugger, but when our app encodes the data as JSON to save to our database, we get bad results.
Our app obtains a record from our database, then some manipulation is done and then the data is saved back to the server from another model.
Here is the data I got back from the server in the setAttendanceGetSInfo() function below:
{"data":{"Start":"2014-10-16T19:36:00Z","End":"2014-10-16T19:37:00Z"},
This is the code used to "convert the data" to 3 properties in our model:
var setAttendanceGetSInfo = function (CourseId, PID) {
return setAttendanceInfo(CourseId, PID)
.then(function (result) {
return $q.all([
$http.get("../api/Axtra/getSInfo/" + model.event.Id),
$http.get("../api/Axtra/GetStartAndEndDateTime/" + aRow.Rid)
]);
}).then(function (result) {
var r = result.data;
var e = Date.fromISO(r.Start);
var f = Date.fromISO(r.End);
angular.extend(model.event, {
examDate: new Date(e).toLocaleDateString(),
examStartTime: (new Date(e)).toLocaleTimeString(),
examEndTime: (new Date(f)).toLocaleTimeString()
});
return result.sInfo;
});
};
fromISO is defined as:
(function(){
var D= new Date('2011-06-02T09:34:29+02:00');
if(!D || +D!== 1307000069000){
Date.fromISO= function(s){
var day, tz,
rx=/^(\d{4}\-\d\d\-\d\d([tT ][\d:\.]*)?)([zZ]|([+\-])(\d\d):(\d\d))?$/,
p= rx.exec(s) || [];
if(p[1]){
day= p[1].split(/\D/);
for(var i= 0, L= day.length; i<L; i++){
day[i]= parseInt(day[i], 10) || 0;
};
day[1]-= 1;
day= new Date(Date.UTC.apply(Date, day));
if(!day.getDate()) return NaN;
if(p[5]){
tz= (parseInt(p[5], 10)*60);
if(p[6]) tz+= parseInt(p[6], 10);
if(p[4]== '+') tz*= -1;
if(tz) day.setUTCMinutes(day.getUTCMinutes()+ tz);
}
return day;
}
return NaN;
}
}
else{
Date.fromISO= function(s){
return new Date(s);
}
}
})()
Take a look at the screenshot of the event model data:
But, if I eval the event model using JSON.stringify(model.event), I get this:
{\"examDate\":\"?10?/?16?/?2014\",\"examStartTime\":\"?2?:?44?:?00? ?PM\",\"examEndTime\":\"?2?:?44?:?00? ?PM\"}
And this is the JSON encoded data that actually got stored on the DB:
"examDate":"¿10¿/¿16¿/¿2014","examStartTime":"¿2¿:¿36¿:¿00¿ ¿PM","examEndTime":"¿2¿:¿37¿:¿00¿ ¿PM"
What is wrong here and how can I fix this? It works exactly as designed in Chrome and Firefox. I have not yet tested on Safari or earlier versions of IE.
The toJSON for the date class isn't defined perfectly the same for all browsers.
(You can see a related question here: Discrepancy in JSON.stringify of date values in different browsers
I would suspect that you have a custom toJSON added to the Date prototype since your date string doesn't match the standard and that is likely where your issue is. Alternatively, you can use the Date toJSON recommended in the above post to solve your issues.
First, I modified the fromISO prototype to this:
(function () {
var D = new Date('2011-06-02T09:34:29+02:00');
if (!D || +D !== 1307000069000) {
Date.fromISO = function (s) {
var D, M = [], hm, min = 0, d2,
Rx = /([\d:]+)(\.\d+)?(Z|(([+\-])(\d\d):(\d\d))?)?$/;
D = s.substring(0, 10).split('-');
if (s.length > 11) {
M = s.substring(11).match(Rx) || [];
if (M[1]) D = D.concat(M[1].split(':'));
if (M[2]) D.push(Math.round(M[2] * 1000));// msec
}
for (var i = 0, L = D.length; i < L; i++) {
D[i] = parseInt(D[i], 10);
}
D[1] -= 1;
while (D.length < 6) D.push(0);
if (M[4]) {
min = parseInt(M[6]) * 60 + parseInt(M[7], 10);// timezone not UTC
if (M[5] == '+') min *= -1;
}
try {
d2 = Date.fromUTCArray(D);
if (min) d2.setUTCMinutes(d2.getUTCMinutes() + min);
}
catch (er) {
// bad input
}
return d2;
}
}
else {
Date.fromISO = function (s) {
return new Date(s);
}
}
Date.fromUTCArray = function (A) {
var D = new Date;
while (A.length < 7) A.push(0);
var T = A.splice(3, A.length);
D.setUTCFullYear.apply(D, A);
D.setUTCHours.apply(D, T);
return D;
}
Date.toJSON = function (key) {
return isFinite(this.valueOf()) ?
this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z' : null;
};
})()
Then I added moment.js and formatted the dates when they get stored:
var SaveAffRow = function () {
// make sure dates on coursedate and event are correct.
var cd = model.a.courseDate;
var ed = model.event.examDate;
var est = model.event.examStartTime;
var eet = model.event.examEndTime;
model.a.courseDate = moment(cd).format("MM/DD/YYYY");
model.event.examDate = moment(ed).format("MM/DD/YYYY");
model.event.examStartTime = moment(est).format("MM/DD/YYYY hh:mm A");
model.event.examEndTime = moment(eet).format("MM/DD/YYYY hh:mm A");
affRow.DocumentsJson = angular.toJson({a: model.a, event: model.event});
var aff = {};
if (affRow.Id != 0)
aff = affRow.$update({ Id: affRow.Id });
else
aff = affRow.$save({ Id: affRow.Id });
return aff;
};
and when they get read (just in case they are messed up already):
var setAttendanceGetSInfo = function (CourseId, PID) {
return setAttendanceInfo(CourseId, PID)
.then(function (result) {
return $q.all([
$http.get("../api/Axtra/getSInfo/" + model.event.Id),
$http.get("../api/Axtra/GetStartAndEndDateTime/" + aRow.Rid)
]);
}).then(function (result) {
var r = result.data;
var e = Date.fromISO(r.Start);
var f = Date.fromISO(r.End);
angular.extend(model.event, {
examDate: moment(e).format("MM/DD/YYYY"),
examStartTime: moment(e).format("MM/DD/YYYY hh:mm A"),
examEndTime: moment(f).format("MM/DD/YYYY hh:mm A")
});
return result.sInfo;
});
};

Can I increase QUOTA_BYTES_PER_ITEM in Chrome?

Is there any way to increase the chrome.storage.sync.QUOTA_BYTES_PER_ITEM ?
For me, the default 4096 Bytes is a little bit short.
I tried to execute
chrome.storage.sync.QUOTA_BYTES_PER_ITEM = 8192;
However, it seems that the actual limit doesn't change.
How can I do this?
No, QUOTA_BYTES_PER_ITEM is there for reference only; it is not a settable value. You could use the value of QUOTA_BYTES_PER_ITEM to split an item up into multiple items, though:
function syncStore(key, objectToStore, callback) {
var jsonstr = JSON.stringify(objectToStore);
var i = 0;
var storageObj = {};
// split jsonstr into chunks and store them in an object indexed by `key_i`
while(jsonstr.length > 0) {
var index = key + "_" + i++;
// since the key uses up some per-item quota, see how much is left for the value
// also trim off 2 for quotes added by storage-time `stringify`
var valueLength = chrome.storage.sync.QUOTA_BYTES_PER_ITEM - index.length - 2;
// trim down segment so it will be small enough even when run through `JSON.stringify` again at storage time
var segment = jsonstr.substr(0, valueLength);
while(JSON.stringify(segment).length > valueLength)
segment = jsonstr.substr(0, --valueLength);
storageObj[index] = segment;
jsonstr = jsonstr.substr(valueLength);
}
// store all the chunks
chrome.storage.sync.set(storageObj, callback);
}
Then write an analogous fetch function that fetches by key and glues the object back together.
just modify answer of #apsilliers
function syncStore(key, objectToStore) {
var jsonstr = JSON.stringify(objectToStore);
var i = 0;
var storageObj = {};
// split jsonstr into chunks and store them in an object indexed by `key_i`
while(jsonstr.length > 0) {
var index = key + "_" + i++;
// since the key uses up some per-item quota, see how much is left for the value
// also trim off 2 for quotes added by storage-time `stringify`
const maxLength = chrome.storage.sync.QUOTA_BYTES_PER_ITEM - index.length - 2;
var valueLength = jsonstr.length;
if(valueLength > maxLength){
valueLength = maxLength;
}
// trim down segment so it will be small enough even when run through `JSON.stringify` again at storage time
//max try is QUOTA_BYTES_PER_ITEM to avoid infinite loop
var segment = jsonstr.substr(0, valueLength);
for(let i = 0; i < chrome.storage.sync.QUOTA_BYTES_PER_ITEM; i++){
const jsonLength = JSON.stringify(segment).length;
if(jsonLength > maxLength){
segment = jsonstr.substr(0, --valueLength);
}else {
break;
}
}
storageObj[index] = segment;
jsonstr = jsonstr.substr(valueLength);
}
also function to read each partition and merge again
function syncGet(key, callback) {
chrome.storage.sync.get(key, (data) => {
console.log(data[key]);
console.log(typeof data[key]);
if(data != undefined && data != "undefined" && data != {} && data[key] != undefined && data[key] != "undefined"){
const keyArr = new Array();
for(let i = 0; i <= data[key].count; i++) {
keyArr.push(`${data[key].prefix}${i}`)
}
chrome.storage.sync.get(keyArr, (items) => {
console.log(data)
const keys = Object.keys( items );
const length = keys.length;
let results = "";
if(length > 0){
const sepPos = keys[0].lastIndexOf("_");
const prefix = keys[0].substring(0, sepPos);
for(let x = 0; x < length; x ++){
results += items[`${prefix }_${x}`];
}
callback(JSON.parse(results));
return;
}
callback(undefined);
});
} else {
callback(undefined);
}
});
}
it tested and it works for my case
this is a better version of #uncle bob's functions, working with manifest v3 (you can use it just like how you can use the normal sync.set or sync.get function)
NOTE: it only works with JSONs (arrays and objects) since a string shouldn't be that long
let browserServices;
if (typeof browser === "undefined") {
browserServices = chrome;
} else {
browserServices = browser;
}
function syncSet(obj = {}) {
return new Promise((resolve, reject) => {
var storageObj = {};
for (let u = 0; u < Object.keys(obj).length; u++) {
const key = Object.keys(obj)[u];
const objectToStore = obj[key]
var jsonstr = JSON.stringify(objectToStore);
var i = 0;
// split jsonstr into chunks and store them in an object indexed by `key_i`
while (jsonstr.length > 0) {
var index = key + "USEDTOSEPERATE" + i++;
// since the key uses up some per-item quota, see how much is left for the value
// also trim off 2 for quotes added by storage-time `stringify`
const maxLength = browserServices.storage.sync.QUOTA_BYTES_PER_ITEM - index.length - 2;
var valueLength = jsonstr.length;
if (valueLength > maxLength) {
valueLength = maxLength;
}
// trim down segment so it will be small enough even when run through `JSON.stringify` again at storage time
//max try is QUOTA_BYTES_PER_ITEM to avoid infinite loop
var segment = jsonstr.substring(0, valueLength);
var jsonLength = JSON.stringify(segment).length;
segment = jsonstr.substring(0, valueLength = (valueLength - (jsonLength - maxLength) - 1));
for (let i = 0; i < browserServices.storage.sync.QUOTA_BYTES_PER_ITEM; i++) {
jsonLength = JSON.stringify(segment).length;
if (jsonLength > maxLength) {
segment = jsonstr.substring(0, --valueLength);
} else {
break;
}
}
storageObj[index] = segment;
jsonstr = jsonstr.substring(valueLength, Infinity);
}
}
chrome.storage.sync.set(storageObj).then(() => {
resolve()
})
})
}
function syncGet(uniqueKeys = []) {
return new Promise((resolve, reject) => {
browserServices.storage.sync.get(null).then((data) => {
const keyArr = Object.keys(data).filter(e => uniqueKeys.filter(j => e.indexOf(j) == 0).length > 0)
browserServices.storage.sync.get(keyArr).then((items) => {
var results = {};
for (let i = 0; i < uniqueKeys.length; i++) {
const uniqueKey = uniqueKeys[i];
const keysFiltered = keyArr.filter(e => e.split("USEDTOSEPERATE")[0] == uniqueKey)
if (keysFiltered.length > 0) {
results[uniqueKey] = ""
for (let x = 0; x < keysFiltered.length; x++) {
results[uniqueKey] += items[`${keysFiltered[x]}`];
}
results[uniqueKey] = JSON.parse(results[uniqueKey])
}
}
resolve(results)
});
});
})
}
example of usage:
syncSet({
"keyTest": ["a lot of text"],
"keyTest1": ["a lot of text"]
}
)
syncGet(["keyTest","keyTest1"]).then(results=>console.log(results))
// {keyTest:["a lot of text"],keyTest1:["a lot of text"]}