├── test ├── titanium │ ├── .gitignore │ ├── Resources │ │ ├── lib │ │ ├── test │ │ │ ├── browser │ │ │ └── tests_to_run.js │ │ ├── app.js │ │ ├── runner.js │ │ └── qunit │ │ │ └── titanium_adaptor.js │ ├── manifest │ └── tiapp.xml ├── browser │ ├── tasks.html │ ├── tasks.client.js │ ├── test.migrations.html │ ├── test.sync.html │ ├── test.search.html │ ├── test.mixin.html │ ├── test.persistence.html │ ├── test.jquery-persistence.html │ ├── test.uki-persistence.html │ ├── test.search.js │ ├── uki │ │ └── uki-persistence.js │ ├── util.js │ ├── qunit │ │ └── qunit.css │ ├── test.mixin.js │ ├── test.sync.js │ └── test.migrations.js └── node │ ├── test.store.config.js │ ├── test.memory.store.js │ ├── test.sqlite.store.js │ ├── test.error.handling.js │ ├── test.sqlite3.store.js │ ├── test.sync.server.js │ ├── partial.sync.schema.sql │ └── node-blog.js ├── .gitignore ├── index.js ├── demo └── jquerymobile │ ├── assets │ ├── version.png │ ├── ipad-palm.png │ ├── jqm-sitebg.png │ └── jquery-logo.png │ ├── README.md │ ├── order │ └── form-fake-response.html │ ├── docs │ ├── text.html │ └── text_and_images.html │ └── index.html ├── lib ├── index.js ├── persistence.sync.server.php.sql ├── persistence.store.config.js ├── persistence.pool.js ├── persistence.jquery.js ├── persistence.store.sqlite.js ├── persistence.store.sqlite3.js ├── persistence.store.react-native.js ├── persistence.sync.server.php ├── persistence.store.mysql.js ├── persistence.store.titanium.js ├── persistence.store.websql.js ├── persistence.store.cordovasql.js ├── persistence.sync.server.js ├── persistence.store.memory.js ├── persistence.search.js ├── persistence.migrations.js └── persistence.jquery.mobile.js ├── package.json ├── AUTHORS ├── docs ├── jquery.md ├── search.md ├── DEVELOPMENT.md ├── jquery.mobile.md ├── migrations.md └── sync.md ├── CHANGES └── bower.json /test/titanium/.gitignore: -------------------------------------------------------------------------------- 1 | tmp 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | test/titanium/build 2 | -------------------------------------------------------------------------------- /test/titanium/Resources/lib: -------------------------------------------------------------------------------- 1 | ../../../lib -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/'); 2 | -------------------------------------------------------------------------------- /test/titanium/Resources/test/browser: -------------------------------------------------------------------------------- 1 | ../../../browser -------------------------------------------------------------------------------- /demo/jquerymobile/assets/version.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coresmart/persistencejs/HEAD/demo/jquerymobile/assets/version.png -------------------------------------------------------------------------------- /demo/jquerymobile/assets/ipad-palm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coresmart/persistencejs/HEAD/demo/jquerymobile/assets/ipad-palm.png -------------------------------------------------------------------------------- /demo/jquerymobile/assets/jqm-sitebg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coresmart/persistencejs/HEAD/demo/jquerymobile/assets/jqm-sitebg.png -------------------------------------------------------------------------------- /test/titanium/Resources/app.js: -------------------------------------------------------------------------------- 1 | var win = Titanium.UI.createWindow({ 2 | url:'runner.js', 3 | title: 'Unit Test' 4 | }); 5 | win.open(); -------------------------------------------------------------------------------- /demo/jquerymobile/assets/jquery-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coresmart/persistencejs/HEAD/demo/jquerymobile/assets/jquery-logo.png -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./persistence').persistence; 2 | module.exports.StoreConfig = require('./persistence.store.config'); 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "persistencejs", 3 | "version": "0.3.0", 4 | "engine": "node >=0.2.0", 5 | "author": "Zef Hemel", 6 | "directories": {"lib": "./lib"} 7 | } 8 | -------------------------------------------------------------------------------- /test/titanium/Resources/runner.js: -------------------------------------------------------------------------------- 1 | // This file needs to sit in the Resources directory so that when 2 | // it is used as a URL to a window, the include structure doesn't change. 3 | Titanium.include('qunit/titanium_adaptor.js'); -------------------------------------------------------------------------------- /test/titanium/manifest: -------------------------------------------------------------------------------- 1 | #appname: titanium 2 | #publisher: staugaard 3 | #url: https://github.com/zefhemel/persistencejs 4 | #image: appicon.png 5 | #appid: persistencejs.titanium.test 6 | #desc: undefined 7 | #type: mobile 8 | #guid: add78b91-427a-456d-9d6f-e21a272adf95 9 | -------------------------------------------------------------------------------- /demo/jquerymobile/README.md: -------------------------------------------------------------------------------- 1 | To try this demo, you need to run it through a web server. 2 | `index.html` uses relative links to persistence.js (in 3 | `../../lib/persistence.js` to be exact), so this path needs to be 4 | available. 5 | 6 | Example images, design and text used in the demo are copy-pasted 7 | straight from jquerymobile documentation. 8 | -------------------------------------------------------------------------------- /demo/jquerymobile/order/form-fake-response.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |Hello!
11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /docs/jquery.md: -------------------------------------------------------------------------------- 1 | # persistence.jquery.js 2 | 3 | `persistence.jquery.js` is a jquery plugin for `persistence.js` that 4 | allows the usage of jquery notation for crossbrowser-access of 5 | persistencejs entities. 6 | 7 | Example 8 | ------- 9 | 10 | Simple example: 11 | 12 | var User = persistence.define('User', { 13 | firstname: "TEXT", 14 | lastname: "TEXT" 15 | }); 16 | 17 | var user = new User({firstname: "Joe", lastname: "Doo"}); 18 | 19 | // setter 20 | $(user).data('firstname', "Mike") 21 | 22 | // getter 23 | console.log($(user).data('firstname')); // => Mike 24 | 25 | You can find more examples in `test/test.persistence-jquery.js`. -------------------------------------------------------------------------------- /test/browser/tasks.client.js: -------------------------------------------------------------------------------- 1 | 2 | // Data model 3 | var Task = persistence.define('Task', { 4 | name: "TEXT", 5 | done: "BOOL", 6 | lastChange: "DATE" 7 | }); 8 | 9 | persistence.connect('taskdemo', 'database', 5 * 1024 * 1024, '1.0'); 10 | persistence.schemaSync(); 11 | 12 | function syncAll() { 13 | persistence.sync.synchronize('/recentChanges', Task, function(conflicts, updatesToPush, callback) { 14 | console.log(conflicts); 15 | callback(); 16 | }); 17 | } 18 | 19 | 20 | function addTask() { 21 | var t = new Task(); 22 | t.name = "Some new local task"; 23 | t.done = false; 24 | t.lastChange = new Date(); 25 | persistence.add(t); 26 | persistence.flush(); 27 | } 28 | -------------------------------------------------------------------------------- /test/titanium/Resources/test/tests_to_run.js: -------------------------------------------------------------------------------- 1 | //setting up stuff so that the environment kind of looks like a browser 2 | var window = {}; 3 | var console = { 4 | log: function() { 5 | Titanium.API.debug(arguments[0]); 6 | } 7 | }; 8 | var document = null; 9 | $ = function(document) { 10 | return {ready: function(f) {f();}}; 11 | }; 12 | 13 | //requiring persistencejs 14 | Titanium.include('lib/persistence.js', 15 | 'lib/persistence.store.sql.js', 16 | 'lib/persistence.store.titanium.js'); 17 | var persistence = window.persistence; 18 | //allows us to run unmodified browser tests in titanium 19 | persistence.store.websql = persistence.store.titanium; 20 | 21 | 22 | // Tests to run 23 | Titanium.include('test/browser/test.persistence.js'); 24 | //Titanium.include('test/browser/util.js'); 25 | //Titanium.include('test/browser/test.migrations.js'); 26 | -------------------------------------------------------------------------------- /test/node/test.store.config.js: -------------------------------------------------------------------------------- 1 | // $ expresso test.store.config.js 2 | 3 | var assert = require('assert'); 4 | var persistence = require('../../lib/persistence').persistence; 5 | 6 | var config = { 7 | adaptor: '', 8 | database: 'test', 9 | host: 'localhost', 10 | port: 3306, 11 | user: 'root', 12 | password: '' 13 | }; 14 | 15 | module.exports = { 16 | memory: function() { 17 | config.adaptor = 'memory'; 18 | var persistenceStore = require('../../lib/persistence.store.config').init(persistence, config); 19 | var session = persistenceStore.getSession(); 20 | session.close(); 21 | }, 22 | mysql: function() { 23 | config.adaptor = 'mysql'; 24 | var persistenceStore = require('../../lib/persistence.store.config').init(persistence, config); 25 | var session = persistenceStore.getSession(); 26 | session.close(); 27 | }, 28 | default: function() { 29 | var persistenceStore = require('../../lib/persistence.store.config').init(persistence, config); 30 | var session = persistenceStore.getSession(); 31 | session.close(); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /test/browser/test.migrations.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |jQuery’s mobile strategy can be summarized simply: Delivering top-of-the-line JavaScript in a unified User Interface that works across the most-used smartphone web browsers and tablet form factors.
16 | 17 |The critical difference with our approach is the wide variety of mobile platforms we’re targeting with jQuery Mobile. We’ve been working hard at bringing jQuery support to all mobile browsers that are sufficiently-capable and have at least a nominal amount of market share. In this way, we’re treating mobile web browsers exactly how we treat desktop web browsers.
18 | 19 |To make this broad support possible, all pages in jQuery Mobile are built on a foundation of clean, semantic HTML to ensure compatibility with pretty much any web-enabled device. In devices that interpret CSS and JavaScript, jQuery Mobile applies progressive enhancement techniques to unobtrusively transform the semantic page into a rich, interactive experience that leverages the power of jQuery and CSS. Accessibility features such as WAI-ARIA are tightly integrated throughout the framework to provide support for screen readers and other assistive technologies.
20 |jQuery’s mobile strategy can be summarized simply: Delivering top-of-the-line JavaScript in a unified User Interface that works across the most-used smartphone web browsers and tablet form factors.
16 |The critical difference with our approach is the wide variety of mobile platforms we’re targeting with jQuery Mobile. We’ve been working hard at bringing jQuery support to all mobile browsers that are sufficiently-capable and have at least a nominal amount of market share. In this way, we’re treating mobile web browsers exactly how we treat desktop web browsers.
17 |To make this broad support possible, all pages in jQuery Mobile are built on a foundation of clean, semantic HTML to ensure compatibility with pretty much any web-enabled device. In devices that interpret CSS and JavaScript, jQuery Mobile applies progressive enhancement techniques to unobtrusively transform the semantic page into a rich, interactive experience that leverages the power of jQuery and CSS. Accessibility features such as WAI-ARIA are tightly integrated throughout the framework to provide support for screen readers and other assistive technologies.
18 |
19 | 
Touch-Optimized Web Framework for Smartphones & Tablets - now with PersistenceJS integration
68 |Alpha Release
69 |Post added.
'); 150 | res.write('Go back'); 151 | htmlFooter(res); 152 | callback(); 153 | }); 154 | } 155 | 156 | function postComment(session, tx, req, res, callback) { 157 | htmlHeader(res, "Created new comment"); 158 | var query = parseUrl(req.url, true).query; 159 | var comment = new Comment(session, {text: query.text, author: query.author, date: new Date()}); 160 | Post.load(session, tx, query.post, function(post) { 161 | post.comments.add(comment); 162 | session.flush(tx, function() { 163 | res.write('Comment added.
'); 164 | res.write('Go back'); 165 | htmlFooter(res); 166 | callback(); 167 | }); 168 | }); 169 | } 170 | 171 | var urlMap = { 172 | '/init': initDatabase, 173 | '/reset': resetDatabase, 174 | '/post': post, 175 | '/postComment': postComment, 176 | '/show': showItem, 177 | '/': showItems 178 | }; 179 | 180 | var http = require('http'); 181 | http.createServer(function (req, res) { 182 | res.writeHead(200, {'Content-Type': 'text/html'}); 183 | var parsed = parseUrl(req.url, true); 184 | var fn = urlMap[parsed.pathname]; 185 | if(fn) { 186 | var session = persistenceStore.getSession(); 187 | session.transaction(function(tx) { 188 | fn(session, tx, req, res, function() { 189 | session.close(); 190 | res.end(); 191 | }); 192 | }); 193 | } else { 194 | res.end("Not found: " + req.url); 195 | } 196 | }).listen(8888, "127.0.0.1"); 197 | console.log('Server running at http://127.0.0.1:8888/'); 198 | -------------------------------------------------------------------------------- /lib/persistence.store.cordovasql.js: -------------------------------------------------------------------------------- 1 | try { 2 | if (!window) { 3 | window = {}; 4 | //exports.console = console; 5 | } 6 | } catch (e) { 7 | window = {}; 8 | exports.console = console; 9 | } 10 | 11 | var persistence = (window && window.persistence) ? window.persistence : {}; 12 | 13 | if (!persistence.store) { 14 | persistence.store = {}; 15 | } 16 | 17 | persistence.store.cordovasql = {}; 18 | 19 | /** 20 | * Configure the database connection (either sqliteplugin or websql) 21 | * 22 | * @param persistence 23 | * @param dbname 24 | * @param dbversion 25 | * @param description 26 | * @param size 27 | * @param backgroundProcessing 28 | * @param iOSLocation 29 | */ 30 | persistence.store.cordovasql.config = function (persistence, dbname, dbversion, description, size, backgroundProcessing, iOSLocation) { 31 | var conn = null; 32 | 33 | /** 34 | * Create a transaction 35 | * 36 | * @param callback 37 | * the callback function to be invoked when the transaction 38 | * starts, taking the transaction object as argument 39 | */ 40 | persistence.transaction = function (callback) { 41 | if (!conn) { 42 | throw new Error("No ongoing database connection, please connect first."); 43 | } else { 44 | conn.transaction(callback); 45 | } 46 | }; 47 | 48 | persistence.db = persistence.db || {}; 49 | persistence.db.implementation = "unsupported"; 50 | persistence.db.conn = null; 51 | 52 | /* Find out if sqliteplugin is loaded. Otherwise, we'll fall back to WebSql */ 53 | if (window && 'sqlitePlugin' in window) { 54 | persistence.db.implementation = 'sqliteplugin'; 55 | } else if (window && window.openDatabase) { 56 | persistence.db.implementation = "websql"; 57 | } else { 58 | // Well, we are stuck! 59 | } 60 | 61 | /* 62 | * Cordova SqlitePlugin 63 | */ 64 | persistence.db.sqliteplugin = {}; 65 | 66 | /** 67 | * Connect to Sqlite plugin database 68 | * 69 | * @param dbname 70 | * @param backgroundProcessing 71 | * @param iOSLocation 72 | * @returns {{}} 73 | */ 74 | persistence.db.sqliteplugin.connect = function (dbname, backgroundProcessing, iOSLocation) { 75 | var that = {}; 76 | var conn = window.sqlitePlugin.openDatabase({name: dbname, bgType: backgroundProcessing, location: (iOSLocation || 0)}); 77 | 78 | that.transaction = function (fn) { 79 | return conn.transaction(function (sqlt) { 80 | return fn(persistence.db.websql.transaction(sqlt)); 81 | }); 82 | }; 83 | return that; 84 | }; 85 | 86 | /** 87 | * Run transaction on Sqlite plugin database 88 | * 89 | * @param t 90 | * @returns {{}} 91 | */ 92 | persistence.db.sqliteplugin.transaction = function (t) { 93 | var that = {}; 94 | that.executeSql = function (query, args, successFn, errorFn) { 95 | if (persistence.debug) { 96 | console.log(query, args); 97 | } 98 | t.executeSql(query, args, function (_, result) { 99 | if (successFn) { 100 | var results = []; 101 | for (var i = 0; i < result.rows.length; i++) { 102 | results.push(result.rows.item(i)); 103 | } 104 | successFn(results); 105 | } 106 | }, errorFn); 107 | }; 108 | return that; 109 | }; 110 | 111 | /* 112 | * WebSQL 113 | */ 114 | persistence.db.websql = {}; 115 | 116 | /** 117 | * Connect to the default WebSQL database 118 | * 119 | * @param dbname 120 | * @param dbversion 121 | * @param description 122 | * @param size 123 | * @returns {{}} 124 | */ 125 | persistence.db.websql.connect = function (dbname, dbversion, description, size) { 126 | var that = {}; 127 | var conn = openDatabase(dbname, dbversion, description, size); 128 | 129 | that.transaction = function (fn) { 130 | return conn.transaction(function (sqlt) { 131 | return fn(persistence.db.websql.transaction(sqlt)); 132 | }); 133 | }; 134 | return that; 135 | }; 136 | 137 | /** 138 | * Run transaction on WebSQL database 139 | * 140 | * @param t 141 | * @returns {{}} 142 | */ 143 | persistence.db.websql.transaction = function (t) { 144 | var that = {}; 145 | that.executeSql = function (query, args, successFn, errorFn) { 146 | if (persistence.debug) { 147 | console.log(query, args); 148 | } 149 | t.executeSql(query, args, function (_, result) { 150 | if (successFn) { 151 | var results = []; 152 | for (var i = 0; i < result.rows.length; i++) { 153 | results.push(result.rows.item(i)); 154 | } 155 | successFn(results); 156 | } 157 | }, errorFn); 158 | }; 159 | return that; 160 | }; 161 | 162 | /** 163 | * Connect() wrapper 164 | * 165 | * @param dbname 166 | * @param dbversion 167 | * @param description 168 | * @param size 169 | * @param backgroundProcessing 170 | * @param iOSLocation 171 | * @returns {*} 172 | */ 173 | persistence.db.connect = function (dbname, dbversion, description, size, backgroundProcessing, iOSLocation) { 174 | if (persistence.db.implementation == "sqliteplugin") { 175 | return persistence.db.sqliteplugin.connect(dbname, backgroundProcessing, iOSLocation); 176 | } else if (persistence.db.implementation == "websql") { 177 | return persistence.db.websql.connect(dbname, dbversion, description, size); 178 | } 179 | 180 | return null; 181 | }; 182 | 183 | /** 184 | * Set the sqlite dialect 185 | * 186 | * @type {{createTable: createTable, createIndex: createIndex}} 187 | */ 188 | persistence.store.cordovasql.sqliteDialect = { 189 | 190 | /** 191 | * columns is an array of arrays, e.g. [["id", "VARCHAR(32)", "PRIMARY KEY"], ["name", "TEXT"]] 192 | * 193 | * @param tableName 194 | * @param columns 195 | * @returns {string} 196 | */ 197 | createTable: function (tableName, columns) { 198 | var tm = persistence.typeMapper; 199 | var sql = "CREATE TABLE IF NOT EXISTS `" + tableName + "` ("; 200 | var defs = []; 201 | for (var i = 0; i < columns.length; i++) { 202 | var column = columns[i]; 203 | defs.push("`" + column[0] + "` " + tm.columnType(column[1]) + (column[2] ? " " + column[2] : "")); 204 | } 205 | sql += defs.join(", "); 206 | sql += ')'; 207 | return sql; 208 | }, 209 | 210 | /** 211 | * columns is array of column names, e.g. ["id"] 212 | * @param tableName 213 | * @param columns 214 | * @param options 215 | * @returns {string} 216 | */ 217 | createIndex: function (tableName, columns, options) { 218 | options = options || {}; 219 | return "CREATE " + (options.unique ? "UNIQUE " : "") + "INDEX IF NOT EXISTS `" + tableName + "__" + columns.join("_") + 220 | "` ON `" + tableName + "` (" + 221 | columns.map(function (col) { 222 | return "`" + col + "`"; 223 | }).join(", ") + ")"; 224 | } 225 | }; 226 | 227 | // Configure persistence for generic sql persistence, using sqliteDialect 228 | persistence.store.sql.config(persistence, persistence.store.cordovasql.sqliteDialect); 229 | 230 | // Make the connection 231 | conn = persistence.db.connect(dbname, dbversion, description, size, backgroundProcessing, iOSLocation); 232 | if (!conn) { 233 | throw new Error("No supported database found in this browser."); 234 | } 235 | }; 236 | 237 | try { 238 | exports.persistence = persistence; 239 | } catch (e) { 240 | } 241 | -------------------------------------------------------------------------------- /lib/persistence.sync.server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010 Zef Hemel