8

So, I'm writing an extension to allow people to fine and save colors from images found on the web. It's going well but now I'm trying to conceptualize how I'll actually store them, and list stored items.

As far as I can tell, chrome.storage.sync() only allows for objects. Which means I'd have to do something like this:

{colors: [{colorName: 'white', colorHex: '#ffffff'}, {colorName: 'black', colorHex: '#000000'}]}

Which seems wildly inefficient, since every time I want to add or subtract a color from the favorite list, I will need to get the entire array, change the one item I want, and then store the array back. Not to mention scanning an array for a color to see if it exists or not could be very intensive on a large array.

Ultimately, I'd like to be able to do something along the lines of

 colors['#fff'].name = white;

However, that doesn't seem possible.

I'd love to hear some other ideas as to what the best way to accomplish this might be.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Chris Sobolewski
  • 12,819
  • 12
  • 63
  • 96

2 Answers2

8

The beauty of Javascript is that everything is loosely considered an object. Functions, arrays, and even variables can be accessed as objects.

You could create an array like this,

var colors {}
colors["#FFF"] = "white";
colors["#000"] = "black";

Or perhaps use an array of empty functions,

function color(name, hex /* ... other properties */ ) { }

var colors {
    color1: color("white", "#FFF");
    color2: color("black", "#000");
}

Then these colors can be accessed by

color1.name

or

color1.hex

Although, because you should use a specific 'key' value for each object in storage, perhaps that is a better way to go.

For instance,

function save_color() {
    var white = "#FFF";
                             //key    value   callback
    chrome.storage.sync.set({"white": white}, function() {
        console.log("The value stored was: " + white);
    });
}

Or, for multiple colors

function save_colors() {
    var white = "#FFF";
    var black = "#000";

    chrome.storage.sync.set([{"white": white}, {"black": black}], function() {
        console.log("The values stored are: " + white + " and " + black);
    });
}

I think that may work, i haven't tried storing multiple objects using one api call before, but you should get the point. A good way to implement this may be to have an empty array that gets added to every time the user finds a color they would like to add, then periodically the extension can push the data to sync.

Once you have done a ton of testing and your sync storage is cluttered, keep track of the keys you used during development and remember to run a batch data removal. It would look something like this:

function clear_data() {
    var keys = { "white", "black" };
    chrome.storage.sync.remove(keys, function() {
        for(var i = 0; i < keys.length; i++)
            console.log("Removed Data for Key: " + key[i]);
    });
}

By the way, to retrieve the value stored in sync,

function load_color() {
    var color = "white";
                           //key   callback
    chrome.storage.sync.get(color, function(val) {
        console.log("The value returned was: " + val);
    });
}
David Freitag
  • 2,252
  • 2
  • 16
  • 18
  • 1
    Also, if you decide to add the option to use local storage instead of sync, or some combination of both, the syntax for chrome.storage.local is the same as chrome.storage.sync. It's usually a good practice to allow users to decide what kind of storage they want (some people dislike the cloud). – David Freitag Jun 20 '13 at 14:11
2

I was unsure about this as well, so I made a small example.

manifest.json:

{
    "manifest_version": 2,

    "name": "Test",
    "description": "Test.",
    "version": "1.0",

    "permissions": [
        "storage"
    ],
    "content_scripts": [
        {
            "matches": ["https://www.google.com/*"],
            "js": ["content-script.js"]
        }
    ]
}

content-script.js:

console.log("content script loaded")

function modifyObject() {
    chrome.storage.sync.get(null, function(storageData3) {
        storageData3.object.property2 = false;

        chrome.storage.sync.set(storageData3, function() {
            chrome.storage.sync.get(null, function(storageData4) {
                console.log("after setting *only* object: " + JSON.stringify(storageData4));
            });
        });
    });
}

// Dumb attempt at setting only property2 of "object"; will add a new top level object "property2".
function attemptToModifyProperty2() {
    var toSave = { "property2": false };
    chrome.storage.sync.set(toSave, function() {
        chrome.storage.sync.get(null, function(storageData2) {
            console.log("after attemping to set *only* property2: " + JSON.stringify(storageData2));

            modifyObject();
        });
    });
}

function addArray() {
    var toSave = { "array": [1, 2, 3] };
    chrome.storage.sync.set(toSave, function() {
        chrome.storage.sync.get(null, function(storageData1) {
            console.log("after setting *only* array: " + JSON.stringify(storageData1));

            attemptToModifyProperty2();
        });
    });
}

function addObject() {
    var toSave = { "object": { "property1": true, "property2": true } };
    chrome.storage.sync.set(toSave, function() {
        chrome.storage.sync.get(null, function(storageData) {
            console.log("after setting *only* object: " + JSON.stringify(storageData));

            addArray();
        });
    });
}

chrome.storage.sync.clear();

addObject();

If you go to google.com (and log in, or change the matches in manifest.json to http), and then open the console, you'll see this output:

content script loaded
content-script.js:42 after setting *only* object: {"object":{"property1":true,"property2":true}}
content-script.js:31 after setting *only* array: {"array":[1,2,3],"object":{"property1":true,"property2":true}}
content-script.js:20 after attemping to set *only* property2: {"array":[1,2,3],"object":{"property1":true,"property2":true},"property2":false}
content-script.js:9 after setting *only* object: {"array":[1,2,3],"object":{"property1":true,"property2":false},"property2":false}

My conclusions from this were that it's only possible to set top-level objects. Even if you want to change only one property that is nested deeply within a top-level object, you will have to pass the entire object to set().

Mitch
  • 23,716
  • 9
  • 83
  • 122