Created
March 31, 2010 12:02
-
-
Save cacaodev/350234 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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