Skip to content

Instantly share code, notes, and snippets.

@cacaodev
Created March 31, 2010 12:02
Show Gist options
  • Save cacaodev/350234 to your computer and use it in GitHub Desktop.
Save cacaodev/350234 to your computer and use it in GitHub Desktop.
@import "CPObject.j"
@import "CPString.j"
@import "CPDate.j"
@import "CPDictionary.j"
@import "CPBundle.j"
@import "CPException.j"
@import "CPKeyedArchiver.j"
@import "CPKeyedUnarchiver.j"
@import <AppKit/CPCookie.j>
var _standardUserDefaults = nil,
_startLoadingDate,
dbDidLoad,
systemDB;
@implementation CPUserDefaults : CPObject
{
var _bundleName;
var _localDefaults;
var delegate @accessors(readwrite);
}
- (id)init
{
if (self = [super init])
{
_localDefaults = [[CPDictionary alloc] init];
_bundleName = [[[CPBundle mainBundle] infoDictionary] objectForKey:"CPBundleName"];
_startLoadingDate = [CPDate date];
}
return self;
}
- (id)_localDefaults
{
return _localDefaults;
}
- (id)_bundleName
{
return _bundleName;
}
+ (id)standardUserDefaults
{
if(_standardUserDefaults == nil)
_standardUserDefaults = [[[CPUserDefaults classForUserDefaults] alloc] init];
return _standardUserDefaults;
}
+ (Class)classForUserDefaults
{
if CPBrowserIsEngine(CPWebKitBrowserEngine)
return [_CPDatabaseDefaults class];
return [_CPCookieDefaults class];
}
- (void)notifyDelegate
{
if (delegate && [delegate respondsToSelector:@selector(userDefaultsDidLoad)])
[delegate userDefaultsDidLoad];
}
@end
// HTML5 Database support
@implementation _CPDatabaseDefaults : CPUserDefaults
{
}
- (id)init
{
self = [super init];
if (self)
{
dbDidLoad = false;
if (initDB(_bundleName))
loadLocalDefaultsFromSQL(_bundleName, _localDefaults, self);
}
return nil;
}
- (void)objectForKey:(CPString)key
{
return [_localDefaults objectForKey:key];
}
- (void)setObject:(id)value forKey:(CPString)key
{
[_localDefaults setObject:value forKey:key];
var sqlvalue = [value SQLValue],
sqltype = [value className];
//[CPException raise:CPInternalInconsistencyException reason:"A default's value can be only property list objects"];
insertValueForKey(key, sqlvalue, sqltype, _bundleName);
}
- (void)removeObjectForKey:(CPString)key
{
[_localDefaults removeObjectForKey:key];
deleteValueForkey(key,_bundleName);
}
@end
// No HTML5 Database support - Using cookies
@implementation _CPCookieDefaults : CPUserDefaults
{
var _cookie;
}
- (id)init
{
self = [super init];
if (self)
{
_cookie = [[CPCookie alloc] initWithName:_bundleName];
var allKeys = [[_cookie value] componentsSeparatedByString:";"],
count = [allKeys count],
i = 0;
for (;i < count; i++)
{
var key = [allKeys objectAtIndex:i];
var cookie = [[CPCookie alloc] initWithName:_bundleName+"_"+key];
var content = [cookie value];
if (content)
{
var contentArray = [content componentsSeparatedByString:@"@@@"],
type = [contentArray objectAtIndex:0],
cookievalue = [contentArray objectAtIndex:1],
value = [CPClassFromString(type) objectWithCookieValue:cookievalue];
if (value != "")
[_localDefaults setObject:value forKey:key];
}
}
[self notifyDelegate];
}
return self;
}
- (void)objectForKey:(CPString)key
{
return [_localDefaults objectForKey:key];
}
- (void)setObject:(id)value forKey:(CPString)key
{
[_localDefaults setObject:value forKey:key];
var cookievalue = [value cookieValue],
cookietype = [value className],
content = cookietype+ "@@@" + cookievalue; // too hacky ?
var cookie = [[CPCookie alloc] initWithName:_bundleName+"_"+key];
[cookie setValue:content expires:[[CPDate alloc] initWithTimeIntervalSinceNow:3600*24*365] domain:(window.location.href.hostname)];
[self _update];
}
- (void)removeObjectForKey:(CPString)key
{
[_localDefaults removeObjectForKey:key];
[self _update];
}
- (void)_update
{
var allKeys = [[_localDefaults allKeys] componentsJoinedByString:";"];
[_cookie setValue:allKeys expires:[[CPDate alloc] initWithTimeIntervalSinceNow:3600*24*365] domain:(window.location.href.hostname)];
}
@end
// Primitive Data Type Categories
@implementation CPNumber (UserDefaults)
- (int)SQLValue
{
return [self intValue];
}
+ (id)objectWithSQLValue:(int)value
{
return [CPNumber numberWithInt:value];
}
- (id)cookieValue
{
return [self stringValue];
}
+ (id)objectWithCookieValue:(id)value
{
return [CPNumber numberWithInt:parseInt(value)];
}
@end
@implementation CPString (UserDefaults)
- (id)SQLValue
{
return self;
}
+ (id)objectWithSQLValue:(id)value
{
return [CPString stringWithString:value];
}
- (id)cookieValue
{
return self;
}
+ (id)objectWithCookieValue:(id)value
{
return [CPString stringWithString:value];
}
@end
@implementation CPData (CPUserDefaults)
- (id)SQLValue
{
return [self string];
}
+ (id)objectWithSQLValue:(id)value
{
return [CPData dataWithString:value];
}
- (id)cookieValue
{
return [self string];
}
+ (id)objectWithCookieValue:(id)value
{
return [CPData dataWithString:value];
}
@end
@implementation CPDate (CPUserDefaults)
- (id)SQLValue
{
[self descriptionWithCalendarFormat:@"%x"];
}
+ (id)objectWithSQLValue:(id)value
{
return [CPDate dateWithString:value calendarFormat:@"%x"];
}
- (id)cookieValue
{
[self descriptionWithCalendarFormat:@"%x"];
}
+ (id)objectWithCookieValue:(id)value
{
return [CPDate dateWithString:value calendarFormat:@"%x"];
}
@end
// HTML5 Database functions
var initDB = function initDB(name)
{
try
{
if (!window.openDatabase)
alert("not supported");
else
{
var shortName = "Cappuccino",
version = "1.0",
displayName = "Cappuccino Defaults Database",
maxSize = 1000000, // in bytes
myDB = openDatabase(shortName, version, displayName, maxSize);
createTable(myDB,name);
systemDB = myDB;
return true;
}
}
catch(e)
{
// Error handling code goes here.
if (e == "INVALID_STATE_ERR")
// Version number mismatch.
CPLogConsole("Invalid database version.");
else
CPLogConsole("Unknown error "+e+".");
}
return false;
}
function createTable(db,name)
{
db.transaction
(
function (transaction)
{
transaction.executeSql('CREATE TABLE IF NOT EXISTS '+name+'(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, key TEXT NOT NULL UNIQUE, Type TEXT NOT NULL, CPString TEXT, CPNumber INTEGER, CPData BLOB, CPDate DATE, Boolean BOOL);', [], nullDataHandler, killTransaction);
}
);
}
function loadLocalDefaultsFromSQL(tablename, dictionary, defaultsInstance)
{
myDB = systemDB;
myDB.transaction
(
function (transaction)
{
var loadLocalDict = function loadLocalDict(transaction, results)
{
var rows = results.rows;
count = rows.length;
i = 0;
for (;i < count; i++)
{
var row = rows.item(i),
key = row['key'],
type = row['Type'],
sqlvalue = row[type];
var aclass = CPClassFromString(type),
value = [aclass objectWithSQLValue:sqlvalue];
[dictionary setObject:value forKey:key];
}
CPLogConsole("Database loading time = "+([CPDate date] - _startLoadingDate));
[defaultsInstance notifyDelegate];
dbDidLoad = true;
}
transaction.executeSql('SELECT * FROM ' + tablename + ' ;',[], loadLocalDict, errorHandler);
}
);
}
function insertValueForKey(key,value,column,table)
{
var myDB = systemDB;
// TODO = Detect if type change and delete old value
myDB.transaction(
function (transaction) {
var func = function func(transaction, results)
{
if(results.rows.length == 0)
transaction.executeSql('INSERT INTO ' + table + ' (key,Type,'+column+') VALUES (?,?,?);', [key,column,value], nullDataHandler, errorHandler);
else
transaction.executeSql('UPDATE ' + table + ' SET ' + column + '=? , Type=? WHERE key=?;', [value,column,key], nullDataHandler, errorHandler);
};
transaction.executeSql('SELECT * FROM ' + table + ' WHERE key=?;', [key], func, errorHandler);
}
);
}
function deleteValueForkey(key,table)
{
var myDB = systemDB;
myDB.transaction(
function (transaction) {
transaction.executeSql('DELETE * FROM ' + table + ' WHERE key=?;', [key], nullDataHandler, errorHandler);
}
);
}
/*! When passed as the error handler, this causes a transaction to fail with a warning message. */
function errorHandler(transaction, error)
{
// Error is a human-readable string.
alert('Error : '+error.message+' (Code ' + error.code + ')');
return true;
}
/*! This is used as a data handler for a request that should return no data. */
function nullDataHandler(transaction, results)
{
}
/*! When passed as the error handler, this silently causes a transaction to fail. */
function killTransaction(transaction, error)
{
return true; // fatal transaction error
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment