├── .gitignore ├── .sencha ├── .cvsignore ├── .gitignore └── workspace │ ├── plugin.xml │ └── sencha.cfg ├── README.md ├── package.json ├── packages └── local │ └── dbproxies │ ├── .sencha │ └── package │ │ ├── Boot.js │ │ ├── Microloader.js │ │ ├── bootstrap-impl.xml │ │ ├── build-impl.xml │ │ ├── build.properties │ │ ├── codegen.json │ │ ├── defaults.properties │ │ ├── find-cmd-impl.xml │ │ ├── init-impl.xml │ │ ├── js-impl.xml │ │ ├── plugin.xml │ │ ├── refresh-impl.xml │ │ ├── resources-impl.xml │ │ ├── sass-impl.xml │ │ ├── sencha.cfg │ │ ├── slice-impl.xml │ │ ├── sub-builds.xml │ │ └── testing.properties │ ├── build.xml │ ├── package.json │ └── src │ ├── config │ └── Config.js │ ├── data │ ├── SqlConnection.js │ └── proxy │ │ ├── Db.js │ │ ├── Dynamic.js │ │ ├── IndexedDB.js │ │ └── Sql.js │ └── overrides │ └── ext │ └── data │ └── Model.js └── workspace.json /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /.sencha/.cvsignore: -------------------------------------------------------------------------------- 1 | /temp/ -------------------------------------------------------------------------------- /.sencha/.gitignore: -------------------------------------------------------------------------------- 1 | /temp/ -------------------------------------------------------------------------------- /.sencha/workspace/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.sencha/workspace/sencha.cfg: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # This file contains configuration options that apply to all applications in 3 | # the workspace. By convention, these options start with "workspace." but any 4 | # option can be set here. Options specified in an application's sencha.cfg will 5 | # take priority over those values contained in this file. These options will 6 | # take priority over configuration values in Sencha Cmd or a framework plugin. 7 | 8 | # ----------------------------------------------------------------------------- 9 | # This configuration property (if set) is included by default in all compile 10 | # commands executed according to this formulation: 11 | # 12 | # sencha compile -classpath=...,${framework.classpath},${workspace.classpath},${app.classpath} 13 | 14 | #workspace.classpath= 15 | 16 | #------------------------------------------------------------------------------ 17 | # This is the folder for build outputs in the workspace 18 | 19 | workspace.build.dir=${workspace.dir}/build 20 | 21 | #------------------------------------------------------------------------------ 22 | # This folder contains all generated and extracted packages. 23 | 24 | workspace.packages.dir=${workspace.dir}/packages 25 | 26 | workspace.theme.dir=${workspace.packages.dir}/${args.themeName} 27 | 28 | # ============================================================================= 29 | # Customizations go below this divider to avoid merge conflicts on upgrade 30 | # ============================================================================= 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # extjs-db-proxies 2 | WebSQL/SQLite, IndexedDB and dynamic proxies for ExtJS 3 | 4 | sql proxy: dynamically switches to websql or sqlite if the cordova plugin is found 5 | indexeddb: tested in chrome, safari and firefox. IE testing to come (although openCursor in place of getAll is working) 6 | dynamic: will automatically select sql or indexeddb, in the order you specify, based on support 7 | 8 | --HOW TO USE-- 9 | 10 | If you are a git guru, you may know how to clone only the package from this repository into an application's packages/local directory. 11 | 12 | If I just described you, please let me know how to do that so I can update these instructions accordingly! 13 | 14 | Otherwise... 15 | 16 | 1. Add the dependency to your app's package.json file: 17 | ``` 18 | "dependencies": { 19 | "extjs-db-proxies": "git+ssh://git@github.com/shepsii/extjs-db-proxies.git" 20 | } 21 | ``` 22 | 2. run `npm install` (you will need nodejs installed) 23 | 3. create a symlink in packages/local (this works for linux/mac; windows guys you'll need to work this one out on your own) 24 | ``` 25 | cd packages/local; 26 | ln -s ../node_modules/extjs-db-proxies/packages/local/dbproxies dbproxies 27 | ``` 28 | 4. Add to the requires in your app.json: 29 | ``` 30 | "requires": [ 31 | "dbproxies" 32 | ] 33 | ``` 34 | (4b. if you have any other packages that need to use the proxies, they will also need to require the package) 35 | 36 | 5. Require in the proxy you need from a model or store class, and set the proxy using e.g. `type: 'sql'` or see below for an example of the use of the dynamic proxy: 37 | ``` 38 | Ext.define('MyApp.model.Person', { 39 | extend: 'Ext.data.Model', 40 | 41 | requires: [ 42 | 'DBProxies.data.proxy.Sql', 43 | ... 44 | ], 45 | proxy: { 46 | type: 'dynamic', 47 | allConfig: { 48 | cloud: false 49 | }, 50 | proxies: [ 51 | { 52 | type: 'sql' 53 | }, 54 | { 55 | type: 'indexeddb' 56 | } 57 | ] 58 | }, 59 | ... 60 | ``` 61 | 6. Optionally, override the DBProxies.config.Config class to change the database name, description and size, e.g.: 62 | ``` 63 | Ext.define('MyApp.overrides.dbproxies.Config', { 64 | override: 'DBProxies.config.Config', 65 | 66 | dbName: 'myappdb', 67 | dbDescription: 'This is my db description', 68 | dbVersion: '1.0', 69 | dbSize: 5000000 70 | 71 | }); 72 | ``` 73 | You should then be good to go - if not, throw out a `sencha app refresh` and then try another `sencha app watch`. Before you go crazy on these, make sure a `sencha app build` works too. 74 | 75 | Please feel free to fork and pull request any changes. 76 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_args": [], 3 | "_from": "git+ssh://git@github.com/shepsii/extjs-db-proxies.git", 4 | "_id": "fbg-toolkit@1.0.1", 5 | "_inCache": true, 6 | "_installable": true, 7 | "_location": "/fbg-toolkit", 8 | "_phantomChildren": {}, 9 | "_requested": { 10 | "hosted": { 11 | "directUrl": "https://raw.githubusercontent.com/shepsii/extjs-db-proxies/master/package.json", 12 | "gitUrl": "git://github.com/shepsii/extjs-db-proxies.git", 13 | "httpsUrl": "git+https://github.com/shepsii/extjs-db-proxies.git", 14 | "shortcut": "github:shepsii/extjs-db-proxies", 15 | "ssh": "git@github.com:shepsii/extjs-db-proxies.git", 16 | "sshUrl": "git+ssh://git@github.com/shepsii/extjs-db-proxies.git", 17 | "type": "github" 18 | }, 19 | "name": null, 20 | "raw": "git+ssh://git@github.com/shepsii/extjs-db-proxies.git", 21 | "rawSpec": "git+ssh://git@github.com/shepsii/extjs-db-proxies.git", 22 | "scope": null, 23 | "spec": "git+ssh://git@github.com/shepsii/extjs-db-proxies.git", 24 | "type": "hosted" 25 | }, 26 | "_requiredBy": [ 27 | "/" 28 | ], 29 | "_resolved": "git+ssh://git@github.com/shepsii/extjs-db-proxies.git#eb622296ee2285330382d0b8c46184efe7faf793", 30 | "_shasum": "8706103f54531f6d0abcfc3dd20a02e9b33f9ddd", 31 | "_shrinkwrap": null, 32 | "_spec": "git+ssh://git@github.com/shepsii/extjs-db-proxies.git", 33 | "_where": "/Users/simon/fbgapps/fbg-draft-dominator", 34 | "author": "", 35 | "bugs": { 36 | "url": "https://github.com/shepsii/extjs-db-proxies/issues" 37 | }, 38 | "dependencies": {}, 39 | "description": "ExtJS proxies for WebSql/Sqlite and IndexedDB", 40 | "devDependencies": {}, 41 | "directories": { 42 | "test": "test" 43 | }, 44 | "framework": "ext", 45 | "homepage": "https://github.com/shepsii/extjs-db-proxies#readme", 46 | "license": "MIT", 47 | "main": "index.js", 48 | "name": "extjs-db-proxies", 49 | "optionalDependencies": {}, 50 | "readme": "ERROR: No README data found!", 51 | "repository": { 52 | "type": "git", 53 | "url": "git+https://github.com/shepsii/extjs-db-proxies.git" 54 | }, 55 | "scripts": { 56 | "test": "echo \"Error: no test specified\" && exit 1" 57 | }, 58 | "version": "1.0.1" 59 | } 60 | -------------------------------------------------------------------------------- /packages/local/dbproxies/.sencha/package/Microloader.js: -------------------------------------------------------------------------------- 1 | // here, the extra check for window['Ext'] is needed for use with cmd-test 2 | // code injection. we need to make that this file will sync up with page global 3 | // scope to avoid duplicate Ext.Boot state. That check is after the initial Ext check 4 | // to allow the sandboxing template to inject an appropriate Ext var and prevent the 5 | // global detection. 6 | var Ext = Ext || window['Ext'] || {}; 7 | 8 | 9 | // 10 | /** 11 | * @class Ext.Microloader 12 | * @private 13 | * @singleton 14 | */ 15 | Ext.Microloader = Ext.Microloader || (function () { 16 | var Boot = Ext.Boot, 17 | // 18 | _debug = function (message) { 19 | //console.log(message); 20 | }, 21 | // 22 | _warn = function (message) { 23 | console.log("[WARN] " + message); 24 | }, 25 | _privatePrefix = '_ext:' + location.pathname, 26 | 27 | /** 28 | * @method getStorageKey 29 | * The Following combination is used to create isolated local storage keys 30 | * '_ext' is used to scope all the local storage keys that we internally by Ext 31 | * 'location.pathname' is used to force each assets to cache by an absolute URL (/build/MyApp) (dev vs prod) 32 | * 'url' is used to force each asset to cache relative to the page (app.json vs resources/app.css) 33 | * 'profileId' is used to differentiate the builds of an application (neptune vs crisp) 34 | * 'Microloader.appId' is unique to the application and will differentiate apps on the same host (dev mode running app watch against multiple apps) 35 | */ 36 | getStorageKey = function(url, profileId) { 37 | return _privatePrefix + url + '-' + (profileId ? profileId + '-' : '') + Microloader.appId; 38 | }, 39 | postProcessor, _storage; 40 | 41 | try { 42 | _storage = window['localStorage']; 43 | } catch(ex) { 44 | // ignore 45 | } 46 | 47 | var _cache = window['applicationCache'], 48 | // Local Storage Controller 49 | LocalStorage = { 50 | clearAllPrivate: function(manifest) { 51 | if(_storage) { 52 | 53 | //Remove the entry for the manifest first 54 | _storage.removeItem(manifest.key); 55 | 56 | var i, key, 57 | removeKeys = [], 58 | suffix = manifest.profile + '-' + Microloader.appId, 59 | ln = _storage.length; 60 | for (i = 0; i < ln; i++) { 61 | key = _storage.key(i); 62 | // If key starts with the private key and the suffix is present we can clear this entry 63 | if (key.indexOf(_privatePrefix) === 0 && key.indexOf(suffix) !== -1) { 64 | removeKeys.push(key); 65 | } 66 | } 67 | 68 | for(i in removeKeys) { 69 | // 70 | _debug("Removing "+ removeKeys[i] + " from Local Storage"); 71 | // 72 | _storage.removeItem(removeKeys[i]); 73 | } 74 | } 75 | }, 76 | /** 77 | * @private 78 | */ 79 | retrieveAsset: function (key) { 80 | try { 81 | return _storage.getItem(key); 82 | } 83 | catch (e) { 84 | // Private browsing mode 85 | return null; 86 | } 87 | }, 88 | 89 | setAsset: function(key, content) { 90 | try { 91 | if (content === null || content == '') { 92 | _storage.removeItem(key); 93 | } else { 94 | _storage.setItem(key, content); 95 | } 96 | } 97 | catch (e) { 98 | if (_storage && e.code == e.QUOTA_EXCEEDED_ERR) { 99 | // 100 | _warn("LocalStorage Quota exceeded, cannot store " + key + " locally"); 101 | // 102 | } 103 | } 104 | } 105 | }; 106 | 107 | var Asset = function (cfg) { 108 | if (typeof cfg.assetConfig === 'string') { 109 | this.assetConfig = { 110 | path: cfg.assetConfig 111 | }; 112 | } else { 113 | this.assetConfig = cfg.assetConfig; 114 | } 115 | 116 | this.type = cfg.type; 117 | this.key = getStorageKey(this.assetConfig.path, cfg.manifest.profile); 118 | 119 | if (cfg.loadFromCache) { 120 | this.loadFromCache(); 121 | } 122 | }; 123 | 124 | Asset.prototype = { 125 | shouldCache: function() { 126 | return _storage && this.assetConfig.update && this.assetConfig.hash && !this.assetConfig.remote; 127 | }, 128 | 129 | is: function (asset) { 130 | return (!!asset && this.assetConfig && asset.assetConfig && (this.assetConfig.hash === asset.assetConfig.hash)) 131 | }, 132 | 133 | cache: function(content) { 134 | if (this.shouldCache()) { 135 | LocalStorage.setAsset(this.key, content || this.content); 136 | } 137 | }, 138 | 139 | uncache: function() { 140 | LocalStorage.setAsset(this.key, null); 141 | }, 142 | 143 | updateContent: function (content) { 144 | this.content = content; 145 | }, 146 | 147 | getSize: function () { 148 | return this.content ? this.content.length : 0; 149 | }, 150 | 151 | loadFromCache: function() { 152 | if (this.shouldCache()) { 153 | this.content = LocalStorage.retrieveAsset(this.key); 154 | } 155 | } 156 | }; 157 | 158 | var Manifest = function (cfg) { 159 | if (typeof cfg.content === "string") { 160 | this.content = JSON.parse(cfg.content); 161 | } else { 162 | this.content = cfg.content; 163 | } 164 | this.assetMap = {}; 165 | 166 | this.url = cfg.url; 167 | this.fromCache = !!cfg.cached; 168 | this.assetCache = !(cfg.assetCache === false); 169 | this.key = getStorageKey(this.url); 170 | 171 | // Pull out select properties for repetitive use 172 | this.profile = this.content.profile; 173 | this.hash = this.content.hash; 174 | this.loadOrder = this.content.loadOrder; 175 | this.deltas = this.content.cache ? this.content.cache.deltas : null; 176 | this.cacheEnabled = this.content.cache ? this.content.cache.enable : false; 177 | 178 | this.loadOrderMap = (this.loadOrder) ? Boot.createLoadOrderMap(this.loadOrder) : null; 179 | 180 | var tags = this.content.tags, 181 | platformTags = Ext.platformTags; 182 | 183 | if (tags) { 184 | if (tags instanceof Array) { 185 | for (var i = 0; i < tags.length; i++) { 186 | platformTags[tags[i]] = true; 187 | } 188 | } else { 189 | Boot.apply(platformTags, tags); 190 | } 191 | 192 | // re-apply the query parameters, so that the params as specified 193 | // in the url always has highest priority 194 | Boot.apply(platformTags, Boot.loadPlatformsParam()); 195 | } 196 | 197 | // Convert all assets into Assets 198 | this.js = this.processAssets(this.content.js, 'js'); 199 | this.css = this.processAssets(this.content.css, 'css'); 200 | }; 201 | 202 | Manifest.prototype = { 203 | processAsset: function(assetConfig, type) { 204 | var processedAsset = new Asset({ 205 | manifest: this, 206 | assetConfig: assetConfig, 207 | type: type, 208 | loadFromCache: this.assetCache 209 | }); 210 | this.assetMap[assetConfig.path] = processedAsset; 211 | return processedAsset; 212 | }, 213 | 214 | processAssets: function(assets, type) { 215 | var results = [], 216 | ln = assets.length, 217 | i, assetConfig; 218 | 219 | for (i = 0; i < ln; i++) { 220 | assetConfig = assets[i]; 221 | results.push(this.processAsset(assetConfig, type)); 222 | } 223 | 224 | return results; 225 | }, 226 | 227 | useAppCache: function() { 228 | return true; 229 | }, 230 | 231 | // Concatenate all assets for easy access 232 | getAssets: function () { 233 | return this.css.concat(this.js); 234 | }, 235 | 236 | getAsset: function (path) { 237 | return this.assetMap[path]; 238 | }, 239 | 240 | shouldCache: function() { 241 | return this.hash && this.cacheEnabled; 242 | }, 243 | 244 | cache: function(content) { 245 | if (this.shouldCache()) { 246 | LocalStorage.setAsset(this.key, JSON.stringify(content || this.content)); 247 | } 248 | // 249 | else { 250 | _debug("Manifest caching is disabled."); 251 | } 252 | // 253 | }, 254 | 255 | is: function(manifest) { 256 | // 257 | _debug("Testing Manifest: " + this.hash + " VS " + manifest.hash); 258 | // 259 | return this.hash === manifest.hash; 260 | }, 261 | 262 | // Clear the manifest from local storage 263 | uncache: function() { 264 | LocalStorage.setAsset(this.key, null); 265 | }, 266 | 267 | exportContent: function() { 268 | return Boot.apply({ 269 | loadOrderMap: this.loadOrderMap 270 | }, this.content); 271 | } 272 | }; 273 | 274 | /** 275 | * Microloader 276 | * @type {Array} 277 | * @private 278 | */ 279 | var _listeners = [], 280 | _loaded = false, 281 | Microloader = { 282 | init: function () { 283 | Ext.microloaded = true; 284 | 285 | // data-app is in the dev template for an application and is also 286 | // injected into the app my CMD for production 287 | // We use this to prefix localStorage cache to prevent collisions 288 | var microloaderElement = document.getElementById('microloader'); 289 | Microloader.appId = microloaderElement ? microloaderElement.getAttribute('data-app') : ''; 290 | 291 | if (Ext.beforeLoad) { 292 | postProcessor = Ext.beforeLoad(Ext.platformTags); 293 | } 294 | 295 | var readyHandler = Ext._beforereadyhandler; 296 | 297 | Ext._beforereadyhandler = function () { 298 | if (Ext.Boot !== Boot) { 299 | Ext.apply(Ext.Boot, Boot); 300 | Ext.Boot = Boot; 301 | } 302 | if (readyHandler) { 303 | readyHandler(); 304 | } 305 | }; 306 | }, 307 | 308 | applyCacheBuster: function(url) { 309 | var tstamp = new Date().getTime(), 310 | sep = url.indexOf('?') === -1 ? '?' : '&'; 311 | url = url + sep + "_dc=" + tstamp; 312 | return url; 313 | }, 314 | 315 | run: function() { 316 | Microloader.init(); 317 | var manifest = Ext.manifest; 318 | 319 | if (typeof manifest === "string") { 320 | var extension = ".json", 321 | url = manifest.indexOf(extension) === manifest.length - extension.length 322 | ? manifest 323 | : manifest + ".json", 324 | key = getStorageKey(url), 325 | content = LocalStorage.retrieveAsset(key); 326 | 327 | // Manifest found in local storage, use this for immediate boot except in PhantomJS environments for building. 328 | if (content) { 329 | // 330 | _debug("Manifest file, '" + url + "', was found in Local Storage"); 331 | // 332 | manifest = new Manifest({ 333 | url: url, 334 | content: content, 335 | cached: true 336 | }); 337 | if (postProcessor) { 338 | postProcessor(manifest); 339 | } 340 | Microloader.load(manifest); 341 | 342 | 343 | // Manifest is not in local storage. Fetch it from the server 344 | } else { 345 | // 346 | _debug("Manifest file was not found in Local Storage, loading: " + url); 347 | // 348 | 349 | if (location.href.indexOf('file:/') === 0) { 350 | Manifest.url = Microloader.applyCacheBuster(url + 'p'); 351 | Boot.load(Manifest.url); 352 | } 353 | else { 354 | Manifest.url = url; 355 | Boot.fetch(Microloader.applyCacheBuster(url), function(result) { 356 | Microloader.setManifest(result.content); 357 | }); 358 | } 359 | } 360 | 361 | // Embedded Manifest into JS file 362 | } else { 363 | // 364 | _debug("Manifest was embedded into application javascript file"); 365 | // 366 | manifest = new Manifest({ 367 | content: manifest 368 | }); 369 | Microloader.load(manifest); 370 | } 371 | }, 372 | 373 | /** 374 | * 375 | * @param cfg 376 | */ 377 | setManifest: function(cfg) { 378 | var manifest = new Manifest({ 379 | url: Manifest.url, 380 | content: cfg 381 | }); 382 | manifest.cache(); 383 | if (postProcessor) { 384 | postProcessor(manifest); 385 | } 386 | Microloader.load(manifest); 387 | }, 388 | 389 | /** 390 | * @param {Manifest} manifest 391 | */ 392 | load: function (manifest) { 393 | Microloader.urls = []; 394 | Microloader.manifest = manifest; 395 | Ext.manifest = Microloader.manifest.exportContent(); 396 | 397 | var assets = manifest.getAssets(), 398 | cachedAssets = [], 399 | asset, i, len, include, entry; 400 | 401 | for (len = assets.length, i = 0; i < len; i++) { 402 | asset = assets[i]; 403 | include = Microloader.filterAsset(asset); 404 | if (include) { 405 | // Asset is using the localStorage caching system 406 | if (manifest.shouldCache() && asset.shouldCache()) { 407 | // Asset already has content from localStorage, instantly seed that into boot 408 | if (asset.content) { 409 | // 410 | _debug("Asset: " + asset.assetConfig.path + " was found in local storage. No remote load for this file"); 411 | // 412 | entry = Boot.registerContent(asset.assetConfig.path, asset.type, asset.content); 413 | if (entry.evaluated) { 414 | _warn("Asset: " + asset.assetConfig.path + " was evaluated prior to local storage being consulted."); 415 | } 416 | //load via AJAX and seed content into Boot 417 | } else { 418 | // 419 | _debug("Asset: " + asset.assetConfig.path + " was NOT found in local storage. Adding to load queue"); 420 | // 421 | cachedAssets.push(asset); 422 | } 423 | } 424 | Microloader.urls.push(asset.assetConfig.path); 425 | Boot.assetConfig[asset.assetConfig.path] = Boot.apply({type: asset.type}, asset.assetConfig); 426 | } 427 | } 428 | 429 | // If any assets are using the caching system and do not have local versions load them first via AJAX 430 | if (cachedAssets.length > 0) { 431 | Microloader.remainingCachedAssets = cachedAssets.length; 432 | while (cachedAssets.length > 0) { 433 | asset = cachedAssets.pop(); 434 | // 435 | _debug("Preloading/Fetching Cached Assets from: " + asset.assetConfig.path); 436 | // 437 | Boot.fetch(asset.assetConfig.path, (function(asset) { 438 | return function(result) { 439 | Microloader.onCachedAssetLoaded(asset, result); 440 | } 441 | })(asset)); 442 | } 443 | } else { 444 | Microloader.onCachedAssetsReady(); 445 | } 446 | }, 447 | 448 | // Load the asset and seed its content into Boot to be evaluated in sequence 449 | onCachedAssetLoaded: function (asset, result) { 450 | var checksum; 451 | result = Microloader.parseResult(result); 452 | Microloader.remainingCachedAssets--; 453 | 454 | if (!result.error) { 455 | checksum = Microloader.checksum(result.content, asset.assetConfig.hash); 456 | if (!checksum) { 457 | _warn("Cached Asset '" + asset.assetConfig.path + "' has failed checksum. This asset will be uncached for future loading"); 458 | 459 | // Un cache this asset so it is loaded next time 460 | asset.uncache(); 461 | } 462 | 463 | // 464 | _debug("Checksum for Cached Asset: " + asset.assetConfig.path + " is " + checksum); 465 | // 466 | Boot.registerContent(asset.assetConfig.path, asset.type, result.content); 467 | asset.updateContent(result.content); 468 | asset.cache(); 469 | } else { 470 | _warn("There was an error pre-loading the asset '" + asset.assetConfig.path + "'. This asset will be uncached for future loading"); 471 | 472 | // Un cache this asset so it is loaded next time 473 | asset.uncache(); 474 | } 475 | 476 | if (Microloader.remainingCachedAssets === 0) { 477 | Microloader.onCachedAssetsReady(); 478 | } 479 | }, 480 | 481 | onCachedAssetsReady: function(){ 482 | Boot.load({ 483 | url: Microloader.urls, 484 | loadOrder: Microloader.manifest.loadOrder, 485 | loadOrderMap: Microloader.manifest.loadOrderMap, 486 | sequential: true, 487 | success: Microloader.onAllAssetsReady, 488 | failure: Microloader.onAllAssetsReady 489 | }); 490 | }, 491 | 492 | onAllAssetsReady: function() { 493 | _loaded = true; 494 | Microloader.notify(); 495 | 496 | if (navigator.onLine !== false) { 497 | // 498 | _debug("Application is online, checking for updates"); 499 | // 500 | Microloader.checkAllUpdates(); 501 | } 502 | else { 503 | // 504 | _debug("Application is offline, adding online listener to check for updates"); 505 | // 506 | if(window['addEventListener']) { 507 | window.addEventListener('online', Microloader.checkAllUpdates, false); 508 | } 509 | } 510 | }, 511 | 512 | onMicroloaderReady: function (listener) { 513 | if (_loaded) { 514 | listener(); 515 | } else { 516 | _listeners.push(listener); 517 | } 518 | }, 519 | 520 | /** 521 | * @private 522 | */ 523 | notify: function () { 524 | // 525 | _debug("notifying microloader ready listeners."); 526 | // 527 | var listener; 528 | while((listener = _listeners.shift())) { 529 | listener(); 530 | } 531 | }, 532 | 533 | // Delta patches content 534 | patch: function (content, delta) { 535 | var output = [], 536 | chunk, i, ln; 537 | 538 | if (delta.length === 0) { 539 | return content; 540 | } 541 | 542 | for (i = 0,ln = delta.length; i < ln; i++) { 543 | chunk = delta[i]; 544 | 545 | if (typeof chunk === 'number') { 546 | output.push(content.substring(chunk, chunk + delta[++i])); 547 | } 548 | else { 549 | output.push(chunk); 550 | } 551 | } 552 | 553 | return output.join(''); 554 | }, 555 | 556 | checkAllUpdates: function() { 557 | // 558 | _debug("Checking for All Updates"); 559 | // 560 | if(window['removeEventListener']) { 561 | window.removeEventListener('online', Microloader.checkAllUpdates, false); 562 | } 563 | 564 | if(_cache) { 565 | Microloader.checkForAppCacheUpdate(); 566 | } 567 | 568 | // Manifest came from a cached instance, check for updates 569 | if (Microloader.manifest.fromCache) { 570 | Microloader.checkForUpdates(); 571 | } 572 | }, 573 | 574 | checkForAppCacheUpdate: function() { 575 | // 576 | _debug("Checking App Cache status"); 577 | // 578 | if (_cache.status === _cache.UPDATEREADY || _cache.status === _cache.OBSOLETE) { 579 | // 580 | _debug("App Cache is already in an updated"); 581 | // 582 | Microloader.appCacheState = 'updated'; 583 | } else if (_cache.status !== _cache.IDLE && _cache.status !== _cache.UNCACHED) { 584 | // 585 | _debug("App Cache is checking or downloading updates, adding listeners"); 586 | // 587 | Microloader.appCacheState = 'checking'; 588 | _cache.addEventListener('error', Microloader.onAppCacheError); 589 | _cache.addEventListener('noupdate', Microloader.onAppCacheNotUpdated); 590 | _cache.addEventListener('cached', Microloader.onAppCacheNotUpdated); 591 | _cache.addEventListener('updateready', Microloader.onAppCacheReady); 592 | _cache.addEventListener('obsolete', Microloader.onAppCacheObsolete); 593 | } else { 594 | // 595 | _debug("App Cache is current or uncached"); 596 | // 597 | Microloader.appCacheState = 'current'; 598 | } 599 | }, 600 | 601 | checkForUpdates: function() { 602 | // Fetch the Latest Manifest from the server 603 | // 604 | _debug("Checking for updates at: " + Microloader.manifest.url); 605 | // 606 | Boot.fetch(Microloader.applyCacheBuster(Microloader.manifest.url), Microloader.onUpdatedManifestLoaded); 607 | }, 608 | 609 | onAppCacheError: function(e) { 610 | _warn(e.message); 611 | 612 | Microloader.appCacheState = 'error'; 613 | Microloader.notifyUpdateReady(); 614 | }, 615 | 616 | onAppCacheReady: function() { 617 | _cache.swapCache(); 618 | Microloader.appCacheUpdated(); 619 | }, 620 | 621 | onAppCacheObsolete: function() { 622 | Microloader.appCacheUpdated(); 623 | }, 624 | 625 | appCacheUpdated: function() { 626 | // 627 | _debug("App Cache Updated"); 628 | // 629 | Microloader.appCacheState = 'updated'; 630 | Microloader.notifyUpdateReady(); 631 | }, 632 | 633 | onAppCacheNotUpdated: function() { 634 | // 635 | _debug("App Cache Not Updated Callback"); 636 | // 637 | Microloader.appCacheState = 'current'; 638 | Microloader.notifyUpdateReady(); 639 | }, 640 | 641 | 642 | filterAsset: function(asset) { 643 | var cfg = (asset && asset.assetConfig) || {}; 644 | if(cfg.platform || cfg.exclude) { 645 | return Boot.filterPlatform(cfg.platform, cfg.exclude); 646 | } 647 | return true; 648 | }, 649 | 650 | onUpdatedManifestLoaded: function (result) { 651 | result = Microloader.parseResult(result); 652 | 653 | if (!result.error) { 654 | var currentAssets, newAssets, currentAsset, newAsset, prop, 655 | assets, deltas, deltaPath, include, 656 | updatingAssets = [], 657 | manifest = new Manifest({ 658 | url: Microloader.manifest.url, 659 | content: result.content, 660 | assetCache: false 661 | }); 662 | 663 | Microloader.remainingUpdatingAssets = 0; 664 | Microloader.updatedAssets = []; 665 | Microloader.removedAssets = []; 666 | Microloader.updatedManifest = null; 667 | Microloader.updatedAssetsReady = false; 668 | 669 | // If the updated manifest has turned off caching we need to clear out all local storage 670 | // and trigger a appupdate as all content is now uncached 671 | if (!manifest.shouldCache()) { 672 | // 673 | _debug("New Manifest has caching disabled, clearing out any private storage"); 674 | // 675 | 676 | Microloader.updatedManifest = manifest; 677 | LocalStorage.clearAllPrivate(manifest); 678 | Microloader.onAllUpdatedAssetsReady(); 679 | return; 680 | } 681 | 682 | // Manifest itself has changed 683 | if (!Microloader.manifest.is(manifest)) { 684 | Microloader.updatedManifest = manifest; 685 | 686 | currentAssets = Microloader.manifest.getAssets(); 687 | newAssets = manifest.getAssets(); 688 | 689 | // Look through new assets for assets that do not exist or assets that have different versions 690 | for (prop in newAssets) { 691 | newAsset = newAssets[prop]; 692 | currentAsset = Microloader.manifest.getAsset(newAsset.assetConfig.path); 693 | include = Microloader.filterAsset(newAsset); 694 | 695 | if (include && (!currentAsset || (newAsset.shouldCache() && (!currentAsset.is(newAsset))))) { 696 | // 697 | _debug("New/Updated Version of Asset: " + newAsset.assetConfig.path + " was found in new manifest"); 698 | // 699 | updatingAssets.push({_new: newAsset, _current: currentAsset}); 700 | } 701 | } 702 | 703 | // Look through current assets for stale/old assets that have been removed 704 | for (prop in currentAssets) { 705 | currentAsset = currentAssets[prop]; 706 | newAsset = manifest.getAsset(currentAsset.assetConfig.path); 707 | 708 | //New version of this asset has been filtered out 709 | include = !Microloader.filterAsset(newAsset); 710 | 711 | if (!include || !newAsset || (currentAsset.shouldCache() && !newAsset.shouldCache())) { 712 | // 713 | _debug("Asset: " + currentAsset.assetConfig.path + " was not found in new manifest, has been filtered out or has been switched to not cache. Marked for removal"); 714 | // 715 | Microloader.removedAssets.push(currentAsset); 716 | } 717 | } 718 | 719 | // Loop through all assets that need updating 720 | if (updatingAssets.length > 0) { 721 | Microloader.remainingUpdatingAssets = updatingAssets.length; 722 | while (updatingAssets.length > 0) { 723 | assets = updatingAssets.pop(); 724 | newAsset = assets._new; 725 | currentAsset = assets._current; 726 | 727 | // Full Updates will simply download the file and replace its current content 728 | if (newAsset.assetConfig.update === "full" || !currentAsset) { 729 | 730 | // 731 | if (newAsset.assetConfig.update === "delta") { 732 | _debug("Delta updated asset found without current asset available: " + newAsset.assetConfig.path + " fetching full file"); 733 | } else { 734 | _debug("Full update found for: " + newAsset.assetConfig.path + " fetching"); 735 | } 736 | // 737 | 738 | // Load the asset and cache its its content into Boot to be evaluated in sequence 739 | Boot.fetch(newAsset.assetConfig.path, (function (asset) { 740 | return function (result) { 741 | Microloader.onFullAssetUpdateLoaded(asset, result) 742 | }; 743 | }(newAsset)) 744 | ); 745 | 746 | // Delta updates will be given a delta patch 747 | } else if (newAsset.assetConfig.update === "delta") { 748 | deltas = manifest.deltas; 749 | deltaPath = deltas + "/" + newAsset.assetConfig.path + "/" + currentAsset.assetConfig.hash + ".json"; 750 | // Fetch the Delta Patch and update the contents of the asset 751 | // 752 | _debug("Delta update found for: " + newAsset.assetConfig.path + " fetching"); 753 | // 754 | Boot.fetch(deltaPath, 755 | (function (asset, oldAsset) { 756 | return function (result) { 757 | Microloader.onDeltaAssetUpdateLoaded(asset, oldAsset, result) 758 | }; 759 | }(newAsset, currentAsset)) 760 | ); 761 | } 762 | } 763 | } else { 764 | // 765 | _debug("No Assets needed updating"); 766 | // 767 | Microloader.onAllUpdatedAssetsReady(); 768 | } 769 | } else { 770 | // 771 | _debug("Manifest files have matching hash's"); 772 | // 773 | Microloader.onAllUpdatedAssetsReady(); 774 | } 775 | } else { 776 | _warn("Error loading manifest file to check for updates"); 777 | Microloader.onAllUpdatedAssetsReady(); 778 | } 779 | }, 780 | 781 | onFullAssetUpdateLoaded: function(asset, result) { 782 | var checksum; 783 | result = Microloader.parseResult(result); 784 | Microloader.remainingUpdatingAssets--; 785 | 786 | if (!result.error) { 787 | checksum = Microloader.checksum(result.content, asset.assetConfig.hash); 788 | // 789 | _debug("Checksum for Full asset: " + asset.assetConfig.path + " is " + checksum); 790 | // 791 | if (!checksum) { 792 | // 793 | _debug("Full Update Asset: " + asset.assetConfig.path + " has failed checksum. This asset will be uncached for future loading"); 794 | // 795 | 796 | // uncache this asset as there is a new version somewhere that has not been loaded. 797 | asset.uncache(); 798 | } else { 799 | asset.updateContent(result.content); 800 | Microloader.updatedAssets.push(asset); 801 | } 802 | } else { 803 | // 804 | _debug("Error loading file at" + asset.assetConfig.path + ". This asset will be uncached for future loading"); 805 | // 806 | 807 | // uncache this asset as there is a new version somewhere that has not been loaded. 808 | asset.uncache(); 809 | } 810 | 811 | if (Microloader.remainingUpdatingAssets === 0) { 812 | Microloader.onAllUpdatedAssetsReady(); 813 | } 814 | }, 815 | 816 | onDeltaAssetUpdateLoaded: function(asset, oldAsset, result) { 817 | var json, checksum, content; 818 | result = Microloader.parseResult(result); 819 | Microloader.remainingUpdatingAssets--; 820 | 821 | if (!result.error) { 822 | // 823 | _debug("Delta patch loaded successfully, patching content"); 824 | // 825 | try { 826 | json = JSON.parse(result.content); 827 | content = Microloader.patch(oldAsset.content, json); 828 | checksum = Microloader.checksum(content, asset.assetConfig.hash); 829 | // 830 | _debug("Checksum for Delta Patched asset: " + asset.assetConfig.path + " is " + checksum); 831 | // 832 | if (!checksum) { 833 | // 834 | _debug("Delta Update Asset: " + asset.assetConfig.path + " has failed checksum. This asset will be uncached for future loading"); 835 | // 836 | 837 | // uncache this asset as there is a new version somewhere that has not been loaded. 838 | asset.uncache(); 839 | } else { 840 | asset.updateContent(content); 841 | Microloader.updatedAssets.push(asset); 842 | } 843 | } catch (e) { 844 | _warn("Error parsing delta patch for " + asset.assetConfig.path + " with hash " + oldAsset.assetConfig.hash + " . This asset will be uncached for future loading"); 845 | // uncache this asset as there is a new version somewhere that has not been loaded. 846 | asset.uncache(); 847 | } 848 | } else { 849 | _warn("Error loading delta patch for " + asset.assetConfig.path + " with hash " + oldAsset.assetConfig.hash + " . This asset will be uncached for future loading"); 850 | 851 | // uncache this asset as there is a new version somewhere that has not been loaded. 852 | asset.uncache(); 853 | } 854 | if (Microloader.remainingUpdatingAssets === 0) { 855 | Microloader.onAllUpdatedAssetsReady(); 856 | } 857 | }, 858 | 859 | //TODO: Make this all transaction based to allow for reverting if quota is exceeded 860 | onAllUpdatedAssetsReady: function() { 861 | var asset; 862 | Microloader.updatedAssetsReady = true; 863 | 864 | if (Microloader.updatedManifest) { 865 | while (Microloader.removedAssets.length > 0) { 866 | asset = Microloader.removedAssets.pop(); 867 | // 868 | _debug("Asset: " + asset.assetConfig.path + " was removed, un-caching"); 869 | // 870 | asset.uncache(); 871 | } 872 | 873 | if (Microloader.updatedManifest) { 874 | // 875 | _debug("Manifest was updated, re-caching"); 876 | // 877 | Microloader.updatedManifest.cache(); 878 | } 879 | 880 | while (Microloader.updatedAssets.length > 0) { 881 | asset = Microloader.updatedAssets.pop(); 882 | // 883 | _debug("Asset: " + asset.assetConfig.path + " was updated, re-caching"); 884 | // 885 | asset.cache(); 886 | } 887 | 888 | } 889 | 890 | Microloader.notifyUpdateReady(); 891 | }, 892 | 893 | notifyUpdateReady: function () { 894 | if (Microloader.appCacheState !== 'checking' && Microloader.updatedAssetsReady) { 895 | if (Microloader.appCacheState === 'updated' || Microloader.updatedManifest) { 896 | // 897 | _debug("There was an update here you will want to reload the app, trigger an event"); 898 | // 899 | Microloader.appUpdate = { 900 | updated: true, 901 | app: Microloader.appCacheState === 'updated', 902 | manifest: Microloader.updatedManifest && Microloader.updatedManifest.exportContent() 903 | }; 904 | 905 | Microloader.fireAppUpdate(); 906 | } 907 | // 908 | else { 909 | _debug("AppCache and LocalStorage Cache are current, no updating needed"); 910 | Microloader.appUpdate = {}; 911 | } 912 | // 913 | } 914 | }, 915 | 916 | fireAppUpdate: function() { 917 | if (Ext.GlobalEvents) { 918 | // We defer dispatching this event slightly in order to let the application finish loading 919 | // as we are still very early in the lifecycle 920 | Ext.defer(function() { 921 | Ext.GlobalEvents.fireEvent('appupdate', Microloader.appUpdate); 922 | }, 1000); 923 | } 924 | }, 925 | 926 | checksum: function(content, hash) { 927 | if(!content || !hash) { 928 | return false; 929 | } 930 | 931 | var passed = true, 932 | hashLn = hash.length, 933 | checksumType = content.substring(0, 1); 934 | 935 | if (checksumType == '/') { 936 | if (content.substring(2, hashLn + 2) !== hash) { 937 | passed = false; 938 | } 939 | } else if (checksumType == 'f') { 940 | if (content.substring(10, hashLn + 10) !== hash) { 941 | passed = false; 942 | } 943 | } else if (checksumType == '.') { 944 | if (content.substring(1, hashLn + 1) !== hash) { 945 | passed = false; 946 | } 947 | } 948 | return passed; 949 | }, 950 | parseResult: function(result) { 951 | var rst = {}; 952 | if ((result.exception || result.status === 0) && !Boot.env.phantom) { 953 | rst.error = true; 954 | } else if ((result.status >= 200 && result.status < 300) || result.status === 304 955 | || Boot.env.phantom 956 | || (result.status === 0 && result.content.length > 0) 957 | ) { 958 | rst.content = result.content; 959 | } else { 960 | rst.error = true; 961 | } 962 | return rst; 963 | } 964 | }; 965 | 966 | return Microloader; 967 | }()); 968 | 969 | /** 970 | * @type {String/Object} 971 | */ 972 | Ext.manifest = Ext.manifest || "bootstrap"; 973 | 974 | Ext.Microloader.run(); -------------------------------------------------------------------------------- /packages/local/dbproxies/.sencha/package/bootstrap-impl.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 32 | @{launchcode} 33 | 34 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /packages/local/dbproxies/.sencha/package/build-impl.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 26 | 27 | 31 | Using Sencha Cmd from ${cmd.dir} for ${ant.file} 32 | 33 | 48 | 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | 69 | 70 | 71 | 81 | 82 | 90 | 91 | 100 | 101 | 104 | 105 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 136 | 137 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 154 | 155 | 156 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 183 | 184 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 202 | 203 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 223 | 224 | 225 | 228 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 261 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 294 | 295 | 296 | 297 | 301 | 302 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 319 | 320 | 321 | 337 | 338 | 339 | 340 | 346 | 348 | 349 | 350 | 351 | 357 | 359 | 360 | 361 | 362 | 387 | 388 | 389 | 390 | -------------------------------------------------------------------------------- /packages/local/dbproxies/.sencha/package/build.properties: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # This file provides an override point for default variables defined in 3 | # defaults.properties. 4 | # 5 | # IMPORTANT - Sencha Cmd will merge your changes with its own during upgrades. 6 | # To avoid potential merge conflicts avoid making large, sweeping changes to 7 | # this file. 8 | # ============================================================================= 9 | -------------------------------------------------------------------------------- /packages/local/dbproxies/.sencha/package/codegen.json: -------------------------------------------------------------------------------- 1 | { 2 | "sources": { 3 | "config.rb.tpl.merge": { 4 | "33f446bd02c3fd24eb27891582eff6a2e789796b": "eJxLLi2KT8ksUrBVcMvMSdUDMvMSc1M14uPdPH1c4+M1ufJLSwpKS+KLSypzUoGqrPJSi0tSU7gALskTcA\u003d\u003d" 5 | }, 6 | "all.scss.merge": { 7 | "da39a3ee5e6b4b0d3255bfef95601890afd80709": "eJwDAAAAAAE\u003d" 8 | }, 9 | "custom.js.merge": { 10 | "199e99bbd15c3c0415569425cb21e77c95e9042a": "eJxlj0FOxDAMRfdziq9ZwUjTHIAlYslquIAncdtA4lSxC8PtSdoREmIV6f/4+dmdDjjhbY6KMSZGeycWrmQcQAqCGlWLMmEpUQzXb1xY/Ex4zgFnRMNXTAlSWseovCTybbbUDl6XsJHa1FH3sYX8B03cqqlS4OPQ//2V8CQ7K5fPriEBNjPU17gYjCZE6UnmYbacfj/GsaUNslUIhbVzu5lwq/2qVjIohGixCCVkkjiyWrOFzqWaXw0sViPr0IRYGVQ7yq+55X2HdObg7meo45udt4XnKyk7Je0Z5SWxqyyB6/Cu/Uh3ODj3crNhN28ar/f1D49P/7rLXUd7+QPuPI9g" 11 | }, 12 | "fashion.html.tpl.merge": { 13 | "5117b27d384b8c5ae0bd4aafdb1fad14eaddcc62": "eJytVE2P0zAQvfdXzOYEq7ppFw6om/RSFgFCAmnLgePUmTbedWxjT9NWiP3tOE6hH8AF4UOsmeeZ9+xnp7h6/XG++PLpDmpu9GxQXAkxAIC5dXuv1jXDM/kcbsaTGxE/L0ZwT0bWCO+MHA2EiAWHupqwmnWVULBiTbO7HcP7e1jU1BC8RW8ohCLvsX5dQ4yRlp2grxvVltncGibDYrF3lIHsozJj2nHe0dxCpPaBuPy8eCNeZad9DDZUZq2irbOeT6q3quK6rKhVkkQKhqCMYoVaBImayskQGtypZtMcE5tAPkW4jAljI1dPdjgf+GCxAtQafCfeUwVamccAaCoI0ivHIa3rzigV9knguLfDlh6wxT6bQfCyzPIn2VR5hMgEZU3IQzpsscJQxzi/mEcPIZsVed/i/7EEDEFI2zilyf8jx9JaDuzRXdQfGvD+5yXoxii6siYWnWWoDHn49gvrRoN+rcwUJmO3uz1Dkp1TeDn+DXE2RIttrPKkkVVL57htya+03U6hVUFFj4/w98FR2U4EHe+NFxw1EA//isD1hWjrUCreR9V/7JxfQzz+jSdY2moPlaUAxnLSpdEB1wTebkwVL5a08fH4AHYFKx+veQUODel8q0xlt7CtyUAnRpk1XOenEtM60TGcKe8LU/5C9RLl4zrxTiHaZ4JDH1/R6RaSg/nBwiI/efddv+h1mjok/Rh+ACC6Vqw\u003d" 14 | }, 15 | "theme.html.tpl.merge": { 16 | "2c775eb1b3eb10df6efa0743d8aa426af3f9a562": "eJx1U8GO0zAQvfcrZnOCqm7ahQPqJr2URYCQQNpy4Og608a7jm3saZIK8e/YTqHpAj4k8rx5855n7OLm7efN9tuXe6ipUetJccPYBAA2xp6cPNQEL8RLuF0sb1n4vJrDA2pRc/igxXzCWCCceTXyah2ZUJAkhev7nuDjA2xrbBDec6fR+yIfsCGvQeJBlizD70fZltnGaEJNbHuymIEYdmVG2FMeZe4gSDuPVH7dvmNvsnEdzRsss1ZiZ42jEbuTFdVlha0UyNJmBlJLklwxL7jCcjmDhveyOTaXwNGjSzu+CwFtgtYgdu4PfDK8Aq4UuGjeYQVK6icPXFfghZOWfMqLPUrECIdkVWaeTgp9jRhsUjjq+YTC+wxqh/syw543VuE8hfJzgaHqmPHIWz5EM/BOlNnOGPLkuJ0/+mxd5AP423rSHYrFNQ/NOCCx2CkuNTr48QeLq+HuIPUKlgvb310hqYsreL34C7HGh86awAon5SRbvMZNi26vTLeCVnoZWnuBf04uznrmVRiXYxQ8IM3+i8D0mWljuZB0Cq7/WTmfAmp/dAg7U52gMuhBG0q+FLdANYIzR12FeQoT7qzzYPawd+F2VWC5RpV3Ulemg65GDdGM1AeY5mOLKY9FhSvnAzHFn7necfF0SLorCOPT3nIXLu/4CGmC+XmERT56brFemHX6RSS9x1+JIiOn" 17 | }, 18 | "build.properties.merge": { 19 | "8b81315dbe73ce9c08478f4c1d2df84f456efcf5": "eJytkEtOxEAMRPdzipKyhbkBCzQrFnzE5AKetJNYdOzI3UnE7XGA3GC8K5f9/GnwdM84NWhHKeglM2a3VRIXkMJWdg+B2UQrenMk7mnJFSu50C1HXWREOUEUAfr3yzk4M3sVLudTE8bL68f7Z/v81uIRV9ZuJFymhE1yxsQ+ML5tcUReh6BuUkdILbBNkRYXHbDMg1P6BaI10GqSYrXKWoUOSmfaZ+mi88+f6GvvzRTmA8rGPO/6mFMtYPW4fiff97U/al6C1w\u003d\u003d" 20 | }, 21 | "sencha.cfg.tpl.merge": { 22 | "057f5f361104adf05c1c49b01b08cd389b3822b2": "eJytkDFyw0AIRfs9BWOlteLOlRvnAHGRMg1eIYnRatGwSBpnxnc3TqIbmPJ/PvCo4KsnaCU1pGA9GkTJhpwLlPm6nzAO2FEBad3lAv9CDZ853WDBxI2nFXat4kir6LAL1dYFdpuoDlXYUj5yGrpSN6ynt+/5cDieN8ul+/u2LoTq9NLymz7mYjLCRWUiNXamPVwSRoL46/APGotzXynJ+kebODuEAC7inCNpRz7JP9QmjlZgZesh0+q/W0jLMx4e5yNt/w\u003d\u003d" 23 | }, 24 | "testing.properties.merge": { 25 | "e65f969c42eb4f355c850fc58fea852582f20db8": "eJyVkUFywyAQBO9+xVb5oIutH/gX+QCCkbUOAooFOf59FsmqpHKKOFEwOzM0Z7r9f53O9DGx0Mge5DBygFDKMSEX1m0VOBpepLqhsndXnpPvv2Z/oefEdiKdLRNoMAJqdyqMI5lAJiXP1hSOQbbZ5msh0mskmuOvnDHHWY32JjbmDEkxOCqxBai6K5DC4d693RAWzjHMCOVCkmB5ZLhW9EWdINjJtBJv9T7cU0vXsk/2rWwxn9AisHA6AooLcgNhqi8riYXdimAn0P+07vXsCOuD8rNimLWaiDKkmBrK7UOUyR0B2RRQdzXedyp+CMVaUi0rQn3ninMxvurPspjBQ/54jjHvYLbHycGKG5Fm2SIf0u/ut9M3l43NIg\u003d\u003d" 26 | } 27 | }, 28 | "targets": { 29 | "sass/config.rb": { 30 | "source": "config.rb.tpl.merge", 31 | "version": "33f446bd02c3fd24eb27891582eff6a2e789796b", 32 | "parameters": { 33 | "extRelPath": "../../../${ext.dir}", 34 | "pkgExtend": "${args.extend}", 35 | "pkgFramework": "", 36 | "pkgName": "dbproxies", 37 | "pkgNamespace": "dbproxies", 38 | "pkgTheme": "", 39 | "pkgToolkit": "", 40 | "pkgType": "code", 41 | "senchadir": ".sencha", 42 | "touchRelPath": "../../../${touch.dir}" 43 | } 44 | }, 45 | "sass/etc/all.scss": { 46 | "source": "all.scss.merge", 47 | "version": "da39a3ee5e6b4b0d3255bfef95601890afd80709", 48 | "parameters": { 49 | "extRelPath": "../../../${ext.dir}", 50 | "pkgExtend": "${args.extend}", 51 | "pkgFramework": "", 52 | "pkgName": "dbproxies", 53 | "pkgNamespace": "dbproxies", 54 | "pkgTheme": "", 55 | "pkgToolkit": "", 56 | "pkgType": "code", 57 | "senchadir": ".sencha", 58 | "touchRelPath": "../../../${touch.dir}" 59 | } 60 | }, 61 | "sass/example/custom.js": { 62 | "source": "custom.js.merge", 63 | "version": "199e99bbd15c3c0415569425cb21e77c95e9042a", 64 | "parameters": { 65 | "extRelPath": "../../../${ext.dir}", 66 | "pkgExtend": "${args.extend}", 67 | "pkgFramework": "", 68 | "pkgName": "dbproxies", 69 | "pkgNamespace": "dbproxies", 70 | "pkgTheme": "", 71 | "pkgToolkit": "", 72 | "pkgType": "code", 73 | "senchadir": ".sencha", 74 | "touchRelPath": "../../../${touch.dir}" 75 | } 76 | }, 77 | "sass/example/fashion.html": { 78 | "source": "fashion.html.tpl.merge", 79 | "version": "5117b27d384b8c5ae0bd4aafdb1fad14eaddcc62", 80 | "parameters": { 81 | "extRelPath": "../../../${ext.dir}", 82 | "pkgExtend": "${args.extend}", 83 | "pkgFramework": "", 84 | "pkgName": "dbproxies", 85 | "pkgNamespace": "dbproxies", 86 | "pkgTheme": "", 87 | "pkgToolkit": "", 88 | "pkgType": "code", 89 | "senchadir": ".sencha", 90 | "touchRelPath": "../../../${touch.dir}" 91 | } 92 | }, 93 | "sass/example/theme.html": { 94 | "source": "theme.html.tpl.merge", 95 | "version": "2c775eb1b3eb10df6efa0743d8aa426af3f9a562", 96 | "parameters": { 97 | "extRelPath": "../../../${ext.dir}", 98 | "pkgExtend": "${args.extend}", 99 | "pkgFramework": "", 100 | "pkgName": "dbproxies", 101 | "pkgNamespace": "dbproxies", 102 | "pkgTheme": "", 103 | "pkgToolkit": "", 104 | "pkgType": "code", 105 | "senchadir": ".sencha", 106 | "touchRelPath": "../../../${touch.dir}" 107 | } 108 | }, 109 | ".sencha/package/build.properties": { 110 | "source": "build.properties.merge", 111 | "version": "8b81315dbe73ce9c08478f4c1d2df84f456efcf5", 112 | "parameters": { 113 | "extRelPath": "../../../${ext.dir}", 114 | "pkgExtend": "${args.extend}", 115 | "pkgFramework": "", 116 | "pkgName": "dbproxies", 117 | "pkgNamespace": "dbproxies", 118 | "pkgTheme": "", 119 | "pkgToolkit": "", 120 | "pkgType": "code", 121 | "senchadir": ".sencha", 122 | "touchRelPath": "../../../${touch.dir}" 123 | } 124 | }, 125 | ".sencha/package/sencha.cfg": { 126 | "source": "sencha.cfg.tpl.merge", 127 | "version": "057f5f361104adf05c1c49b01b08cd389b3822b2", 128 | "parameters": { 129 | "extRelPath": "../../../${ext.dir}", 130 | "pkgExtend": "${args.extend}", 131 | "pkgFramework": "", 132 | "pkgName": "dbproxies", 133 | "pkgNamespace": "dbproxies", 134 | "pkgTheme": "", 135 | "pkgToolkit": "", 136 | "pkgType": "code", 137 | "senchadir": ".sencha", 138 | "touchRelPath": "../../../${touch.dir}" 139 | } 140 | }, 141 | ".sencha/package/testing.properties": { 142 | "source": "testing.properties.merge", 143 | "version": "e65f969c42eb4f355c850fc58fea852582f20db8", 144 | "parameters": { 145 | "extRelPath": "../../../${ext.dir}", 146 | "pkgExtend": "${args.extend}", 147 | "pkgFramework": "", 148 | "pkgName": "dbproxies", 149 | "pkgNamespace": "dbproxies", 150 | "pkgTheme": "", 151 | "pkgToolkit": "", 152 | "pkgType": "code", 153 | "senchadir": ".sencha", 154 | "touchRelPath": "../../../${touch.dir}" 155 | } 156 | } 157 | } 158 | } -------------------------------------------------------------------------------- /packages/local/dbproxies/.sencha/package/defaults.properties: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # This file defines properties used by build-impl.xml and the associated 3 | # *-impl.xml files (sass-impl.xml, js-impl.xml, etc.), which are the core of 4 | # the applications build process. 5 | # 6 | # IMPORTANT - This file is not modifiable by a package, and will be overwritten 7 | # during each app upgrade. Please use build.properties for defining package 8 | # customizations to these properties. 9 | # ============================================================================= 10 | 11 | # =========================================== 12 | # properties defining various directory 13 | # locations 14 | # =========================================== 15 | build.dir=${package.build.dir} 16 | 17 | package.output=${build.dir} 18 | package.output.base=${package.output} 19 | 20 | package.output.js= 21 | package.output.css=resources 22 | package.output.sass=${package.output.js} 23 | package.output.resources=${package.output.css} 24 | 25 | build.out.js.dir=${package.output.base}/${package.output.js} 26 | build.out.css.dir=${package.output.base}/${package.output.css} 27 | build.out.sass.dir=${package.output.base}/${package.output.sass} 28 | build.out.resources.dir=${package.output.base}/${package.output.resources} 29 | 30 | 31 | # a temporary output directory used for staging intermediate build artifacts 32 | build.temp.dir=${workspace.build.dir}/temp/${package.name} 33 | 34 | build.resources.dir=${build.out.resources.dir} 35 | package.resources.dir=${package.dir}/resources 36 | package.sass.dir=${package.dir}/sass 37 | package.licenses.dir=${package.dir}/licenses 38 | 39 | # =========================================== 40 | # definitions of various file name patterns 41 | # used for output artifacts 42 | # =========================================== 43 | 44 | build.name.prefix=${package.name} 45 | build.name.css.prefix=${build.resources.dir}/${package.name} 46 | build.name.ruby=config.rb 47 | 48 | build.debug.suffix=-debug 49 | build.all.suffix=-all 50 | build.rtl.suffix=-rtl 51 | 52 | build.all.debug.suffix=${build.all.suffix}${build.debug.suffix} 53 | build.all.rtl.suffix=${build.all.suffix}${build.rtl.suffix} 54 | build.all.rtl.debug.suffix=${build.all.suffix}${build.rtl.suffix}${build.debug.suffix} 55 | 56 | # =========================================== 57 | # define the output js file names for dev, 58 | # debug, and compressed (no suffix) 59 | # =========================================== 60 | build.all.js=${build.out.js.dir}/${build.name.prefix}.js 61 | build.all.debug.js=${build.out.js.dir}/${build.name.prefix}${build.debug.suffix}.js 62 | 63 | package.sass.build.dir=${build.out.sass.dir} 64 | 65 | # =========================================== 66 | # output file names for the scss files 67 | # =========================================== 68 | build.all.scss=${package.sass.build.dir}/${build.name.prefix}${build.all.debug.suffix}.scss 69 | build.all.rtl.scss=${package.sass.build.dir}/${build.name.prefix}${build.all.rtl.debug.suffix}.scss 70 | 71 | # =========================================== 72 | # output file names for the css files 73 | # generated from the scss files by running 74 | # a compass compilation 75 | # =========================================== 76 | build.all.css.debug.prefix=${package.name}${build.all.debug.suffix} 77 | build.all.css.debug=${build.out.css.dir}/${build.all.css.debug.prefix}.css 78 | build.all.rtl.css.debug.prefix=${package.name}${build.all.rtl.debug.suffix} 79 | build.all.rtl.css.debug=${build.out.css.dir}/${build.all.rtl.css.debug.prefix}.css 80 | build.all.css.prefix=${package.name}${build.all.suffix} 81 | build.all.css=${build.out.css.dir}/${build.all.css.prefix}.css 82 | build.all.rtl.css.prefix=${package.name}${build.all.rtl.suffix} 83 | build.all.rtl.css=${build.out.css.dir}/${build.all.rtl.css.prefix}.css 84 | 85 | build.all.ruby=${package.sass.build.dir}/${build.name.ruby} 86 | 87 | # =========================================== 88 | # options to pass to the 'sencha fs slice' command 89 | # =========================================== 90 | build.slice.options= 91 | 92 | # =========================================== 93 | # preprocessor options used when generating 94 | # concatenated js output files 95 | # =========================================== 96 | build.compile.js.debug.options=debug:true 97 | build.compile.js.options=debug:false 98 | 99 | # enables / disables removing text references from 100 | # package js build files 101 | build.remove.references=false 102 | 103 | # This property can be modified to change general build options 104 | # such as excluding files from the set. The format expects newlines 105 | # for each argument, for example: 106 | # 107 | # build.operations=\ 108 | # exclude\n \ 109 | # -namespace=Ext\n 110 | # 111 | # NOTE: modifications to build.operations are intended to be 112 | # placed in an override of the "-after-init" target, where it 113 | # can be calculated based on other 114 | # ant properties 115 | # 116 | # build.operations= 117 | 118 | # =========================================== 119 | # compression option used to generate '-all' 120 | # js output file 121 | # =========================================== 122 | build.compile.js.compress=+yui 123 | 124 | build.compile.temp.dir=${build.temp.dir}/sencha-compiler 125 | 126 | # controles whether to keep the temp compile dir after the build 127 | build.compile.temp.dir.keep=true 128 | 129 | 130 | # =========================================== 131 | # selector count threshold to use when 132 | # splitting a single css file into multiple 133 | # css files (IE selector limit workaround) 134 | # =========================================== 135 | build.css.selector.limit=4095 136 | 137 | # controls the ruby command used to execute compass. a full path 138 | # to ruby may be specified rather than allowing the system shell 139 | # to resolve the command 140 | build.ruby.path=ruby 141 | 142 | # controls the working directory of the child compass process 143 | # and the output location for the .sass-cache folder 144 | compass.working.dir=${package.sass.build.dir} 145 | 146 | # enables / disables console highlighting for compass 147 | compass.compile.boring=false 148 | 149 | # enables / disables forced rebuilds for compass 150 | compass.compile.force=true 151 | 152 | # enables / disables stack traces in compass failure output 153 | compass.compile.trace=true 154 | 155 | # the directory containing sass files for compass to compile 156 | compass.sass.dir=${package.sass.build.dir} 157 | 158 | # the output directory where compass should place built css files 159 | compass.css.dir=${build.out.css.dir} 160 | 161 | # the directory containing the ruby config file for compass 162 | compass.config.file=${build.all.ruby} 163 | 164 | compass.cache.dir=${workspace.build.dir}/.sass-cache 165 | 166 | # =========================================== 167 | # Options for sub-packages 168 | 169 | # Set to true/1 to enable build.version inheritance by sub-pacakges 170 | build.subpkgs.inherit.version=0 171 | 172 | # =========================================== 173 | # theme slicing example page settings 174 | # =========================================== 175 | package.example.dir=${package.dir}/sass/example 176 | package.example.build.dir=${build.temp.dir}/slicer-temp 177 | package.example.base=${build.all.rtl.css.debug.prefix} 178 | package.example.css=${package.example.build.dir}/${package.example.base}.css 179 | package.example.scss=${package.example.build.dir}/${package.example.base}.scss 180 | package.example.theme.html=${package.example.dir}/theme.html 181 | package.example.fashion.html=${package.example.dir}/fashion.html 182 | 183 | # the name of the intermediate screenshot file used for image slicing 184 | build.capture.png=${package.example.build.dir}/theme-capture.png 185 | 186 | # the name of the intermediate widget manifest file used for image slicing 187 | build.capture.json=${package.example.build.dir}/theme-capture.json 188 | 189 | 190 | 191 | # the microloader to use for bootstrapping operations 192 | package.microloader.bootstrap=${package.microloader.dir}/${package.microloader.development} 193 | 194 | build.boot.name=Boot.js 195 | build.boot.file=${package.config.dir}/${build.boot.name} 196 | build.slicer.microloader.name=Microloader.js 197 | build.slicer.microloader.file=${package.config.dir}/${build.slicer.microloader.name} 198 | 199 | 200 | # the ruby compass config file to generate for slicer page scss 201 | package.example.out.ruby=${package.example.build.dir}/config.rb 202 | package.example.compass.config=${package.example.out.ruby} 203 | 204 | 205 | bootstrap.base.path=${package.example.dir} 206 | bootstrap.example.js=${package.example.dir}/bootstrap.js 207 | bootstrap.example.json=${package.example.dir}/bootstrap.json 208 | 209 | 210 | # =========================================== 211 | # options controlling output packaging 212 | # operations for output '.pkg' file 213 | # =========================================== 214 | pkg.build.dir=${workspace.build.dir}/${package.name} 215 | pkg.file.name=${package.name}.pkg 216 | pkg.includes=**/* 217 | pkg.excludes=package.json 218 | 219 | 220 | # the port number to start the local web server on 221 | build.web.port=1841 222 | 223 | # the directory representing the root web folder 224 | build.web.root=${workspace.dir} 225 | 226 | -------------------------------------------------------------------------------- /packages/local/dbproxies/.sencha/package/find-cmd-impl.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 27 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 50 | 51 | 52 | source ~/.bash_profile; sencha which -p cmd.dir -o '$cmddir$' 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /packages/local/dbproxies/.sencha/package/init-impl.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 11 | 12 | 13 | 19 | 41 | 42 | 43 | 44 | 46 | 47 | 50 | 51 | 52 | 53 | Switch package version to ${build.version} 54 | 55 | 56 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 71 | 72 | 73 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 196 | 197 | 198 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 242 | 243 | 244 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 270 | 271 | 275 | 285 | 286 | 287 | 288 | 289 | 294 | 295 | 296 | 297 | Package web server available at http://localhost:${build.web.port} 298 | 299 | 300 | -------------------------------------------------------------------------------- /packages/local/dbproxies/.sencha/package/js-impl.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 45 | 46 | 47 | 48 | 49 | 50 | 63 | 64 | 65 | 66 | 67 | 68 | 70 | 71 | -------------------------------------------------------------------------------- /packages/local/dbproxies/.sencha/package/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 22 | 23 | 24 | 32 | 33 | -------------------------------------------------------------------------------- /packages/local/dbproxies/.sencha/package/refresh-impl.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /packages/local/dbproxies/.sencha/package/resources-impl.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Merging resources from base package ${base.path} 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | Merging resources from current package ${package.resources.dir} 28 | 29 | 30 | 31 | 32 | 33 | 34 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /packages/local/dbproxies/.sencha/package/sass-impl.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 31 | 32 | 33 | 34 | 35 | 36 | 58 | 59 | 60 | 61 | 62 | 63 | 105 | 106 | 107 | 108 | 109 | 110 | 156 | 157 | 158 | 159 | 161 | 162 | require '${build.all.ruby}' 163 | cache_path = '${compass.cache.dir}' 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 187 | Compressing @{cssfile} to ${css.output.name} 188 | 190 | 191 | 192 | 193 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 232 | 233 | 234 | 238 | 239 | Building @{cssfile} to ${css.output.name} 240 | 241 | 242 | fashion 243 | -compress=@{compress} 244 | -split=${build.css.selector.limit} 245 | @{cssfile} 246 | ${css.output.name} 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 266 | 271 | 272 | 273 | 276 | 277 | 278 | 279 | 280 | 281 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 302 | 303 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | -------------------------------------------------------------------------------- /packages/local/dbproxies/.sencha/package/sencha.cfg: -------------------------------------------------------------------------------- 1 | # The folder that contains sub-packages of this package. Only valid for "framework" 2 | # package type. 3 | # 4 | package.subpkgs.dir=${package.dir}/packages 5 | 6 | #============================================================================== 7 | # Custom Properties - Place customizations below this line to avoid merge 8 | # conflicts with newer versions 9 | 10 | package.cmd.version=6.2.2.36 11 | -------------------------------------------------------------------------------- /packages/local/dbproxies/.sencha/package/slice-impl.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 13 | 14 | 15 | 16 | 19 | 20 | 21 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 75 | 76 | 77 | 78 | 79 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 91 | 92 | 93 | 94 | fashion 95 | -compress=false 96 | ${package.example.build.dir} 97 | ${package.example.build.dir} 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 146 | 154 | 155 | 156 | 157 | 158 | 163 | 164 | 165 | 166 | /* 167 | * This file is generated by Sencha Cmd and should NOT be edited. It redirects 168 | * to the most recently built CSS file for the application to allow theme.html 169 | * to load properly for image slicing (required to support non-CSS3 browsers 170 | * such as IE9 and below). 171 | */ 172 | @import '${package.example.css.path}'; 173 | 174 | 175 | 176 | 177 | 179 | Capture theme image to ${build.dir}/theme-capture.png 180 | 181 | 188 | 189 | 190 | 191 | 192 | Slicing theme images to ${build.resources.dir} 193 | 194 | 202 | 203 | 204 | 205 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 231 | 232 | -------------------------------------------------------------------------------- /packages/local/dbproxies/.sencha/package/sub-builds.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 70 | 71 | Processing examples in "@{dir}" (${example.dir}) 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | No app at ${example.dir}/@{app} 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 140 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 181 | Building sub package ${sub.name} 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | package 190 | build 191 | 192 | 193 | 194 | 195 | 196 | 197 | package 198 | build 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | Cleaning sub package in @{pkg-dir} 210 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | Upgrading sub package in @{pkg-dir} 221 | 222 | 223 | package 224 | upgrade 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | Building example in @{example-dir} 235 | 236 | 237 | app 238 | build 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | Upgrading example in @{example-dir} 248 | 249 | 250 | app 251 | upgrade 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | Cleaning example in @{example-dir} 262 | 264 | 265 | 266 | 267 | 269 | 270 | @{example-dir} 271 | 272 | 273 | 274 | 275 | -------------------------------------------------------------------------------- /packages/local/dbproxies/.sencha/package/testing.properties: -------------------------------------------------------------------------------- 1 | # =========================================== 2 | # This file defines properties used by 3 | # build-impl.xml, which is the base impl 4 | # of an applications build process. The 5 | # properties from this file correspond to the 6 | # 'testing' build environment, specified 7 | # by 'sencha app build testing'. These will 8 | # take precedence over defaults provided by 9 | # build.properties. 10 | # =========================================== 11 | 12 | # =========================================== 13 | # compression option used to generate '-all' 14 | # js output file. this value disables 15 | # compression for testing builds 16 | # =========================================== 17 | build.compile.js.compress= 18 | -------------------------------------------------------------------------------- /packages/local/dbproxies/build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /packages/local/dbproxies/package.json: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | * The name of the package. 4 | */ 5 | "name": "dbproxies", 6 | 7 | /** 8 | * Alternate names for this package. 9 | * 10 | * "alternateName": [], 11 | */ 12 | 13 | /** 14 | * The namespace of this package. 15 | * 16 | * As a general rule, all classes that belong to this package should be under this namespace 17 | * if multiple namespaces are part of this package, set this to "". 18 | */ 19 | "namespace": "DBProxies", 20 | 21 | /** 22 | * The package type. 23 | * 24 | * Sencha Cmd understands the following types of packages: 25 | * - code : An arbitrary package of code for use by applications or other packages. 26 | * - theme : A package to be used as an application’s theme. 27 | * - locale : A package containing localization strings or locale-specific code. 28 | * - template : A package containing one or more templates. 29 | */ 30 | "type": "code", 31 | 32 | /** 33 | * The author of the package. 34 | * 35 | * Required only if you are distributing this package through a Sencha Cmd repository, 36 | * in which case it should match the name you assign to your local package repository. 37 | */ 38 | "creator": "shepsii", 39 | 40 | /** 41 | * A summarized description of this package. 42 | */ 43 | "summary": "ExtJS proxies for Websql/Sqlite and IndexedDB", 44 | 45 | /** 46 | * A detailed description of this package. 47 | */ 48 | "detailedDescription": "ExtJS proxies for Websql/Sqlite and IndexedDB", 49 | 50 | /** 51 | * The package version. 52 | * 53 | * Typically, changes to the package should come along with changes to the version. 54 | * This number should be in this format: d+(.d+)* 55 | */ 56 | "version": "1.0.0", 57 | 58 | /** 59 | * The version that users can transparently update from without requiring code changes. 60 | * 61 | * In addition the version property, packages can also indicate the degree to which 62 | * they are backward compatible using the compatVersion property. 63 | */ 64 | "compatVersion": "1.0.0", 65 | 66 | /** 67 | * Spec. version of this package.json file. 68 | * This is set automatically by Sencha Cmd when first generating this file 69 | */ 70 | "format": "1", 71 | 72 | /** 73 | * Additional resources used during theme slicing operations 74 | */ 75 | "slicer": { 76 | "js": [ 77 | { 78 | "path": "${package.dir}/sass/example/custom.js", 79 | "isWidgetManifest": true 80 | } 81 | ] 82 | }, 83 | 84 | /** 85 | * Controls the output directory. 86 | */ 87 | "output": "${package.dir}/build", 88 | 89 | /** 90 | * Indicates whether this is a locally developed package or downloaded form a repository. 91 | * Defaults to true on newly generated packages, should not be changed. 92 | */ 93 | "local": true, 94 | 95 | /** 96 | * The theme (package) this package will use (e.g., "ext-theme-neptune", etc.). 97 | * This is only needed if the built package will be used by a non-Cmd application. 98 | * 99 | * "theme": "ext-theme-classic", 100 | */ 101 | 102 | /** 103 | * Sass configuration properties. 104 | */ 105 | "sass" : { 106 | 107 | }, 108 | 109 | /** 110 | * This is the comma-separated list of folders where classes reside. These 111 | * classes must be explicitly required to be included in the build. 112 | */ 113 | "classpath": [ 114 | "${package.dir}/src" 115 | ], 116 | 117 | /** 118 | * Comma-separated string with the paths of directories or files to search. Any classes 119 | * declared in these locations will be automatically required and included in the build. 120 | * If any file defines an Ext JS override (using Ext.define with an "override" property), 121 | * that override will in fact only be included in the build if the target class specified 122 | * in the "override" property is also included. 123 | */ 124 | "overrides": [ 125 | "${package.dir}/src/overrides" 126 | ], 127 | 128 | "example": { 129 | /** 130 | * One or more folders that contain example applications for this package. 131 | */ 132 | "path": [ 133 | "${package.dir}/examples" 134 | ] 135 | 136 | /** 137 | * You can list apps specifically. 138 | * 139 | * "apps": [ 140 | * "demo1", 141 | * "demo2" 142 | * ] 143 | * 144 | * By default, all subfolders in the path are considered example applications. 145 | */ 146 | }, 147 | 148 | /** 149 | * The framework this package will use (i.e., "ext" or "touch"). 150 | * This is only needed if the built package will be used by a non-Cmd application. 151 | * 152 | * "framework": "ext", 153 | */ 154 | 155 | /** 156 | * Packages can require other packages in the same way that applications can require 157 | * packages. 158 | * 159 | * Can be specified as an array of package names or configuration objects. 160 | * 161 | * "requires": [ 162 | * "foo", 163 | * "bar@1.1-2.0", 164 | * { 165 | * "name": "baz" 166 | * "version": "1.5" 167 | * } 168 | * ] 169 | * 170 | * Can also be specified as an object: 171 | * 172 | * "requires": { 173 | * "foo": "2.2", 174 | * "bar": { 175 | * "minVersion": "1.1", 176 | * "version": "2.0" 177 | * } 178 | * } 179 | */ 180 | "requires": [] 181 | } 182 | -------------------------------------------------------------------------------- /packages/local/dbproxies/src/config/Config.js: -------------------------------------------------------------------------------- 1 | Ext.define('DBProxies.config.Config', { 2 | singleton: true, 3 | 4 | dbName: 'extjs', 5 | dbDescription: 'extjs', 6 | dbVersion: '1.0', 7 | dbSize: 5000000 8 | 9 | }); 10 | -------------------------------------------------------------------------------- /packages/local/dbproxies/src/data/SqlConnection.js: -------------------------------------------------------------------------------- 1 | Ext.define('DBProxies.data.SqlConnection', { 2 | singleton: true, 3 | 4 | requires: [ 5 | 'DBProxies.config.Config' 6 | ], 7 | 8 | getConn: function() { 9 | if (!Ext.isDefined(this.conn)) { 10 | if (window.sqlitePlugin) { 11 | this.conn = window.sqlitePlugin.openDatabase({ 12 | name: DBProxies.config.Config.dbName + '.db', 13 | location: 'default' 14 | }); 15 | } else { 16 | this.conn = window.openDatabase( 17 | DBProxies.config.Config.dbName, 18 | DBProxies.config.Config.dbVersion, 19 | DBProxies.config.Config.dbDescription, 20 | DBProxies.config.Config.dbSize 21 | ); 22 | } 23 | 24 | } 25 | return this.conn; 26 | } 27 | 28 | }); 29 | -------------------------------------------------------------------------------- /packages/local/dbproxies/src/data/proxy/Db.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Db proxy is a superclass for the {@link DBProxies.data.proxy.IndexedDB IndexedDB} and {@link DBProxies.data.proxy.Sql 3 | * Sql} proxies. 4 | * @private 5 | */ 6 | Ext.define('DBProxies.data.proxy.Db', { 7 | extend: 'Ext.data.proxy.Client', 8 | 9 | config: { 10 | /** 11 | * @cfg {Boolean} cloud 12 | * Whether or not to store local operations in the session's local operations store for upload to the cloud. 13 | * This probably means nothing to you if you are not using the cloud system that is compatible with these 14 | * proxies 15 | */ 16 | cloud: false, 17 | 18 | /** 19 | * @cfg {Boolean} implicitFields 20 | * Whether or not to also save implicit fields on a record. Implicit fields are were a field that was not 21 | * explicitly defined in the model's fields config has been set on the record 22 | */ 23 | implicitFields: false 24 | }, 25 | 26 | /** 27 | * @cfg {Object} reader 28 | * Not used by db proxies 29 | * @hide 30 | */ 31 | 32 | /** 33 | * @cfg {Object} writer 34 | * Not used by db proxies 35 | * @hide 36 | */ 37 | 38 | setException: function(operation, error) { 39 | 40 | operation.setException(error); 41 | 42 | } 43 | 44 | }); 45 | -------------------------------------------------------------------------------- /packages/local/dbproxies/src/data/proxy/Dynamic.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Mock class used to define docs/configs for dynamic proxies 3 | * @private 4 | */ 5 | Ext.define('DBProxies.data.proxy.Dynamic', { 6 | extend: 'Ext.data.proxy.Proxy', 7 | 8 | config: { 9 | /** 10 | * @cfg {Object} allConfig 11 | * Config to apply to whichever proxy is dynamically chosen 12 | */ 13 | allConfig: {}, 14 | 15 | /** 16 | * @cfg {Array} proxies 17 | * Array of proxy definitions to try. Each must comply with Ext.data.Model.proxy config. Configs defined here 18 | * override those defined in allConfig 19 | */ 20 | proxies: [] 21 | }, 22 | 23 | statics: { 24 | applyDynamicProxy: function(dynamicProxy) { 25 | var allConfig = dynamicProxy.allConfig || {}, 26 | proxies = dynamicProxy.proxies || [], 27 | ln = proxies.length, 28 | i, 29 | proxyCls, 30 | types = [], 31 | proxy; 32 | 33 | for (i = 0; i < ln; i += 1) { 34 | proxy = proxies[i]; 35 | if (typeof proxy === 'string') { 36 | proxy = { 37 | type: proxy 38 | }; 39 | } 40 | proxyCls = Ext.ClassManager.getByAlias('proxy.' + proxy.type); 41 | types.push(proxy.type); 42 | if (!proxyCls) { 43 | console.warn('Dynamic proxy: proxy type not defined (' + proxy.type + ')'); 44 | continue; 45 | } 46 | if (Ext.isFunction(proxyCls.isSupported) && !proxyCls.isSupported()) { 47 | continue; 48 | } 49 | return Ext.applyIf(proxy, allConfig); 50 | } 51 | 52 | console.warn('Dynamic proxy: no supported proxies found: tried ' + types.join(', ')); 53 | return false; 54 | } 55 | } 56 | 57 | }); 58 | -------------------------------------------------------------------------------- /packages/local/dbproxies/src/data/proxy/IndexedDB.js: -------------------------------------------------------------------------------- 1 | /** 2 | * IndexedDB proxy to save Ext.data.Model instances for offline use 3 | */ 4 | Ext.define('DBProxies.data.proxy.IndexedDB', { 5 | alias: 'proxy.indexeddb', 6 | extend: 'DBProxies.data.proxy.Db', 7 | 8 | isIndexedDBProxy: true, 9 | 10 | config: { 11 | /** 12 | * @cfg {String} dbName 13 | * The name of the indexedDB database and data store. Will default to the string after the last '.' in the 14 | * model's class name 15 | */ 16 | dbName: null, 17 | 18 | /** 19 | * @cfg {String} indices 20 | * N.B. redefining indices is NOT supported! Once a client sets the indices, there is no way of this being 21 | * changed without clearing the database and starting again. So once you deploy to production, this cannot 22 | * be changed and manual code would need to be written to port all data into a new dbName with the new indices 23 | */ 24 | indices: [] 25 | }, 26 | 27 | statics: { 28 | isSupported: function() { 29 | return !!window.indexedDB; 30 | } 31 | }, 32 | 33 | updateModel: function(model) { 34 | 35 | var modelName; 36 | var dbName; 37 | 38 | if (model) { 39 | modelName = model.prototype.entityName; 40 | dbName = modelName.slice(modelName.lastIndexOf('.') + 1); 41 | if (!this.getDbName()) { 42 | this.setDbName(dbName); 43 | } 44 | this.idProp = model.prototype.getIdProperty(); 45 | } 46 | 47 | this.callParent(arguments); 48 | 49 | }, 50 | 51 | deleteDb: function(callback, scope) { 52 | this.ensureDb({}, function() { 53 | indexedDB.deleteDatabase(this.getDbName()); 54 | Ext.callback(callback, scope); 55 | }, this); 56 | }, 57 | 58 | ensureDb: function(options, callback, scope) { 59 | if (this.db) { 60 | Ext.callback(callback, scope); 61 | return; 62 | } 63 | var request = indexedDB.open(this.getDbName(), 1); 64 | request.onsuccess = Ext.bind(this.openDbSuccess, this, [request, callback, scope], false); 65 | request.onerror = Ext.bind(this.openDbError, this, [options, callback, scope], true); 66 | request.onupgradeneeded = Ext.bind(this.openDbSetSchema, this, [request, callback, scope], false); 67 | }, 68 | 69 | openDbSuccess: function(request, callback, scope) { 70 | this.db = request.result; 71 | Ext.callback(callback, scope); 72 | }, 73 | 74 | openDbError: function(err) { 75 | var args = arguments; 76 | var options = args[args.length - 3]; 77 | console.error('open indexeddb error: ', err.target.error); 78 | Ext.callback(options.callback, options.scope, [false, 'indexed db open error: ' + err.target.error]); 79 | }, 80 | 81 | openDbSetSchema: function(request) { 82 | var store = request.result.createObjectStore(this.getDbName(), {keyPath: this.idProp}); 83 | var i; 84 | var ln; 85 | var indices = this.getIndices(); 86 | var index; 87 | for (i = 0, ln = indices.length; i < ln; i += 1) { 88 | index = indices[i]; 89 | store.createIndex(index, index, { unique: false }); 90 | } 91 | }, 92 | 93 | getIndexFromFilters: function(filters, options) { 94 | if (!filters || filters.length !== 1) { 95 | return false; 96 | } 97 | var filter = filters[0]; 98 | var property = filter.getProperty(); 99 | var value = filter.getValue(); 100 | var indices = this.getIndices(); 101 | if (!Ext.Array.contains(indices, property)) { 102 | return false; 103 | } 104 | options.index_value = value; 105 | return options.object_store.index(property); 106 | }, 107 | 108 | getDbTx: function(type, options, callbackArg, scopeArg) { 109 | var callback = callbackArg || Ext.emptyFn; 110 | var scope = scopeArg || {}; 111 | this.ensureDb(options, Ext.bind(this.getDbTxWithDb, this, [type, options, callback, scope], false)); 112 | }, 113 | 114 | getDbTxWithDb: function(type, options, callback, scope) { 115 | var tx = this.db.transaction([this.getDbName()], type); 116 | tx.onerror = Ext.bind(this.transactionError, this, [options], true); 117 | Ext.callback(callback, scope, [tx]); 118 | }, 119 | 120 | transactionError: function(err, options) { 121 | var args = arguments; 122 | console.error('indexeddb proxy transaction error: ', err.target.error); 123 | this.setException(options.operation, err.target.error); 124 | if (options.callback) { 125 | Ext.callback(options.callback, options.scope, [options.operation]); 126 | } 127 | }, 128 | 129 | getRecordData: function(record) { 130 | var fields = record.getFields(); 131 | var data = {}; 132 | var name; 133 | var explicitFieldNames = []; 134 | var field; 135 | 136 | Ext.each(fields, function(field) { 137 | name = field.name; 138 | explicitFieldNames.push(name); 139 | if (!Ext.isDefined(field.persist) || field.persist) { 140 | data[name] = record.get(name); 141 | } 142 | 143 | }, this); 144 | 145 | if (this.getImplicitFields()) { 146 | for (field in record.data) { 147 | if (!Ext.Array.contains(explicitFieldNames, field)) { 148 | data[field] = record.data[field]; 149 | } 150 | } 151 | } 152 | 153 | return data; 154 | }, 155 | 156 | 157 | /* CREATE */ 158 | create: function(operation) { 159 | 160 | var options = { 161 | operation: operation, 162 | records: operation.getRecords() 163 | }; 164 | operation.setStarted(); 165 | this.getDbTx('readwrite', options, Ext.bind(this.createTransaction, this, [options], true)); 166 | 167 | }, 168 | 169 | createTransaction: function(tx) { 170 | 171 | var args = arguments; 172 | var options = args[args.length - 1]; 173 | 174 | Ext.apply(options, { 175 | tx: tx, 176 | object_store: tx.objectStore(this.getDbName()), 177 | resultSet: new Ext.data.ResultSet({ 178 | success: true 179 | }), 180 | totalRecords: options.records.length, 181 | executedRecords: 0, 182 | errors: [] 183 | }); 184 | 185 | Ext.each(options.records, Ext.bind(this.createRecord, this, [options], true)); 186 | 187 | }, 188 | 189 | createRecord: function(record, i, records, options) { 190 | 191 | if (!record.phantom) { 192 | options.executedRecords += 1; 193 | this.createRecordCallback(options); 194 | return; 195 | } 196 | 197 | var id = record.getId(); 198 | var data = this.getRecordData(record); 199 | var request = options.object_store.add(data); 200 | request.onsuccess = Ext.bind(this.createRecordSuccess, this, [options, record, data], true); 201 | request.onerror = Ext.bind(this.createRecordError, this, [options, record], true); 202 | 203 | }, 204 | 205 | createRecordSuccess: function(evt, options, record, data) { 206 | 207 | if (this.getCloud() && record.session) { 208 | record.session.addOperation({ 209 | model: record.get('model'), 210 | record_id: record.getId(), 211 | type: 'create', 212 | fields: data 213 | }); 214 | } 215 | 216 | options.executedRecords += 1; 217 | 218 | record.phantom = false; 219 | record.commit(); 220 | 221 | this.createRecordCallback(options); 222 | 223 | }, 224 | 225 | createRecordError: function(error, options, record) { 226 | 227 | console.error('INSERT ERROR:', error); 228 | 229 | options.executedRecords += 1; 230 | options.errors.push({ 231 | clientId: record.getId(), 232 | error: error 233 | }); 234 | 235 | this.createRecordCallback(options); 236 | 237 | }, 238 | 239 | createRecordCallback: function(options) { 240 | if (options.executedRecords === options.totalRecords) { 241 | this.createComplete(options); 242 | } 243 | }, 244 | 245 | createComplete: function(options) { 246 | 247 | if (options.operation.process(options.resultSet) === false) { 248 | this.fireEvent('exception', this, options.operation); 249 | } 250 | 251 | if (options.errors) { 252 | options.operation.setException(options.errors); 253 | } 254 | 255 | }, 256 | 257 | 258 | /* ERASE */ 259 | erase: function(operation, callback, scope) { 260 | 261 | var erasedRecords = []; 262 | var options = { 263 | operation: operation, 264 | callback: callback || Ext.emptyFn, 265 | scope: scope || {}, 266 | records: operation.getRecords(), 267 | erasedRecords: erasedRecords, 268 | resultSet: new Ext.data.ResultSet({ 269 | records: erasedRecords, 270 | success: true 271 | }) 272 | }; 273 | 274 | operation.setStarted(); 275 | this.getDbTx('readwrite', options, Ext.bind(this.eraseTransaction, this, [options], true)); 276 | 277 | }, 278 | 279 | eraseTransaction: function(tx) { 280 | 281 | var args = arguments; 282 | var options = args[args.length - 1]; 283 | 284 | tx.oncomplete = Ext.bind(this.eraseTransactionSuccess, this, [options], true); 285 | 286 | Ext.apply(options, { 287 | tx: tx, 288 | object_store: tx.objectStore(this.getDbName()), 289 | errors: [] 290 | }); 291 | 292 | Ext.each(options.records, Ext.bind(this.eraseRecord, this, [options], true)); 293 | 294 | }, 295 | 296 | eraseRecord: function(record, i, records, options) { 297 | var request = options.object_store.delete(record.getId()); 298 | request.onsuccess = Ext.bind(this.eraseRecordSuccess, this, [options, record], true); 299 | request.onerror = Ext.bind(this.eraseRecordError, this, [options, record], true); 300 | }, 301 | 302 | eraseRecordSuccess: function(tx, options, record) { 303 | 304 | if (this.getCloud() && record.session) { 305 | record.session.addOperation({ 306 | model: record.get('model'), 307 | record_id: record.getId(), 308 | type: 'delete' 309 | }); 310 | } 311 | 312 | options.erasedRecords.push(record); 313 | 314 | }, 315 | 316 | eraseRecordError: function(err, options, record) { 317 | 318 | console.error('ERASE ERROR:', err.target.error); 319 | 320 | options.errors.push({ 321 | clientId: record.getId(), 322 | error: err.target.error 323 | }); 324 | 325 | }, 326 | 327 | eraseTransactionSuccess: function() { 328 | var args = arguments; 329 | var options = args[args.length - 1]; 330 | 331 | if (options.operation.process(options.resultSet) === false) { 332 | this.fireEvent('exception', this, options.operation); 333 | } 334 | 335 | if (options.errors.length) { 336 | options.operation.setException(options.errors.join(', ')); 337 | } 338 | 339 | Ext.callback(options.callback, options.scope, [options.operation]); 340 | }, 341 | 342 | 343 | /* READ */ 344 | read: function(operation, callback, scope) { 345 | 346 | var options = { 347 | operation: operation, 348 | callback: callback || Ext.emptyFn, 349 | scope: scope || {} 350 | }; 351 | 352 | operation.setStarted(); 353 | this.getDbTx('readonly', options, Ext.bind(this.readTransaction, this, [options], true)); 354 | 355 | }, 356 | 357 | readTransaction: function(tx) { 358 | 359 | var args = arguments; 360 | var options = args[args.length - 1]; 361 | var records = []; 362 | var params = options.operation.getParams() || {}; 363 | 364 | Ext.apply(params, { 365 | page: options.operation.getPage(), 366 | start: options.operation.getStart(), 367 | limit: options.operation.getLimit(), 368 | sorters: options.operation.getSorters(), 369 | filters: options.operation.getFilters(), 370 | recordId: options.operation.getId() 371 | }); 372 | 373 | Ext.apply(options, { 374 | tx: tx, 375 | object_store: tx.objectStore(this.getDbName()), 376 | idProperty: this.getModel().prototype.getIdProperty(), 377 | recordCreator: options.operation.getRecordCreator(), 378 | params: params, 379 | records: records, 380 | resultSet: new Ext.data.ResultSet({ 381 | records: records, 382 | success: true 383 | }), 384 | errors: [] 385 | }); 386 | 387 | options.tx.onerror = Ext.bind(this.readQueryError, this, [options], true); 388 | 389 | if (options.params.recordId) { 390 | this.readRecordFromId(options); 391 | } else { 392 | this.readRecordsFromParams(options); 393 | } 394 | 395 | }, 396 | 397 | readRecordFromId: function(options) { 398 | var request = options.object_store.get(options.params.recordId); 399 | request.onsuccess = Ext.bind(this.readRecordFromIdSuccess, this, [request, options], false); 400 | }, 401 | 402 | readRecordFromIdSuccess: function(request, options) { 403 | this.readSuccess([request.result], options); 404 | }, 405 | 406 | readRecordsFromParams: function(options) { 407 | 408 | var index = this.getIndexFromFilters(options.params.filters, options); 409 | if (index) { 410 | options.params.filters = []; 411 | options.idb_getfrom = index; 412 | } else { 413 | options.idb_getfrom = options.object_store; 414 | } 415 | 416 | 417 | if (options.idb_getfrom.getAll) { 418 | this.readAllRecordsGetAll(options, Ext.bind(this.readSuccess, this, [options], true)); 419 | } else { 420 | this.readAllRecordsCursor(options, Ext.bind(this.readSuccess, this, [options], true)); 421 | } 422 | 423 | }, 424 | 425 | readAllRecordsGetAll: function(options, callbackArg, scopeArg) { 426 | var callback = callbackArg || Ext.emptyFn; 427 | var scope = scopeArg || {}; 428 | var items = []; 429 | var request; 430 | 431 | if (options.index_value) { 432 | request = options.idb_getfrom.getAll(options.index_value); 433 | } else { 434 | request = options.idb_getfrom.getAll(); 435 | } 436 | 437 | request.onsuccess = Ext.bind(this.readAllRecordsGetAllSuccess, this, [options, callback, scope], true); 438 | }, 439 | 440 | readAllRecordsGetAllSuccess: function(evt, options, callback, scope) { 441 | Ext.callback(callback, scope, [evt.target.result]); 442 | }, 443 | 444 | readAllRecordsCursor: function(options, callbackArg, scopeArg) { 445 | var callback = callbackArg || Ext.emptyFn; 446 | var scope = scopeArg || {}; 447 | var items = []; 448 | var request; 449 | 450 | if (options.index_value) { 451 | request = options.idb_getfrom.openCursor(IDBKeyRange.only(options.index_value)); 452 | } else { 453 | request = options.idb_getfrom.openCursor(); 454 | } 455 | request.onsuccess = Ext.bind(this.readAllRecordsOnCursor, this, [items, options, callback, scope], true); 456 | 457 | }, 458 | 459 | readAllRecordsOnCursor: function(evt, items, options, callback, scope) { 460 | var cursor = evt.target.result; 461 | if (cursor) { 462 | items.push(cursor.value); 463 | cursor.continue(); 464 | } else { 465 | Ext.callback(callback, scope, [items]); 466 | } 467 | }, 468 | 469 | readSuccess: function(items, options) { 470 | 471 | var model = this.getModel(); 472 | var count = items.length; 473 | var i; 474 | var data; 475 | var sorters; 476 | var filters; 477 | var limit; 478 | var start; 479 | var record; 480 | var allRecords; 481 | var validCount = 0; 482 | var valid; 483 | var filterLn; 484 | var j; 485 | 486 | for (i = 0; i < count; i += 1) { 487 | data = items[i]; 488 | options.records.push(Ext.isFunction(options.recordCreator) ? 489 | options.recordCreator(data, model) : 490 | new model(data)); 491 | } 492 | 493 | // apply filters, sorters, limit? 494 | if (!options.params.recordId) { 495 | sorters = options.params.sorters; 496 | filters = options.params.filters; 497 | limit = options.params.limit; 498 | start = options.params.start; 499 | allRecords = options.records; 500 | options.records = []; 501 | 502 | if (sorters) { 503 | Ext.Array.sort(allRecords, Ext.util.Sorter.createComparator(sorters)); 504 | } 505 | 506 | for (i = start || 0; i < count; i++) { 507 | record = allRecords[i]; 508 | valid = true; 509 | 510 | if (filters) { 511 | for (j = 0, filterLn = filters.length; j < filterLn; j++) { 512 | valid = filters[j].filter(record); 513 | } 514 | } 515 | 516 | if (valid) { 517 | options.records.push(record); 518 | validCount++; 519 | } 520 | 521 | if (limit && validCount === limit) { 522 | break; 523 | } 524 | } 525 | } 526 | 527 | options.resultSet.setSuccess(true); 528 | options.resultSet.setTotal(options.records.length); 529 | options.resultSet.setCount(options.records.length); 530 | 531 | this.readComplete(options); 532 | 533 | }, 534 | 535 | readQueryError: function(err, options) { 536 | console.error('READ ERROR:', err.target.error); 537 | options.errors.push(err.target.error); 538 | 539 | options.resultSet.setSuccess(false); 540 | options.resultSet.setTotal(0); 541 | options.resultSet.setCount(0); 542 | 543 | this.readComplete(options); 544 | }, 545 | 546 | readComplete: function(options) { 547 | 548 | if (options.operation.process(options.resultSet) === false) { 549 | this.fireEvent('exception', this, options.operation); 550 | } 551 | 552 | if (options.errors) { 553 | options.operation.setException(options.errors); 554 | } 555 | 556 | Ext.callback(options.callback, options.scope, [options.operation]); 557 | 558 | }, 559 | 560 | 561 | /* UPDATE */ 562 | update: function(operation, callback, scope) { 563 | 564 | var options = { 565 | operation: operation, 566 | callback: callback || Ext.emptyFn, 567 | scope: scope || {}, 568 | records: operation.getRecords() 569 | }; 570 | 571 | operation.setStarted(); 572 | 573 | this.getDbTx('readwrite', options, Ext.bind(this.updateTransaction, this, [options], true)); 574 | 575 | }, 576 | 577 | updateTransaction: function(tx) { 578 | 579 | var args = arguments; 580 | var options = args[args.length - 1]; 581 | var updatedRecords = []; 582 | 583 | Ext.apply(options, { 584 | tx: tx, 585 | object_store: tx.objectStore(this.getDbName()), 586 | updatedRecords: updatedRecords, 587 | resultSet: new Ext.data.ResultSet({ 588 | records: updatedRecords, 589 | success: true 590 | }), 591 | totalRecords: options.records.length, 592 | executedRecords: 0, 593 | errors: [] 594 | }); 595 | 596 | Ext.each(options.records, Ext.bind(this.updateRecord, this, [options], true)); 597 | 598 | }, 599 | 600 | updateRecord: function(record, rI, records, options) { 601 | 602 | var id = record.getId(); 603 | var data = this.getRecordData(record); 604 | var request = options.object_store.put(data); 605 | var modData = {}; 606 | var modifiedKeys = Ext.isObject(record.modified) ? Ext.Object.getKeys(record.modified) : []; 607 | 608 | Ext.each(modifiedKeys, function(key) { 609 | if (Ext.isDefined(data[key])) { 610 | modData[key] = data[key]; 611 | } 612 | }, this); 613 | request.onsuccess = Ext.bind(this.updateRecordSuccess, this, [options, record, data, modData], true); 614 | request.onerror = Ext.bind(this.updateRecordError, this, [options, record], true); 615 | 616 | }, 617 | 618 | updateRecordSuccess: function(evt, options, record, data, modData) { 619 | 620 | var recordId = record.getId(); 621 | var key; 622 | var model = record.get('model'); 623 | 624 | if (this.getCloud() && record.session) { 625 | for (key in modData) { 626 | record.session.addOperation({ 627 | model: model, 628 | record_id: recordId, 629 | type: 'update', 630 | field: key, 631 | value: modData[key] 632 | }); 633 | } 634 | } 635 | 636 | options.updatedRecords.push(data); 637 | 638 | options.executedRecords += 1; 639 | 640 | this.updateRecordCallback(options); 641 | 642 | }, 643 | 644 | updateRecordError: function(err, options, record) { 645 | 646 | console.error('UPDATE ERROR:', err.target.error); 647 | 648 | options.executedRecords += 1; 649 | options.errors.push({ 650 | clientId: record.getId(), 651 | error: error 652 | }); 653 | 654 | this.updateRecordCallback(options); 655 | 656 | }, 657 | 658 | updateRecordCallback: function(options) { 659 | 660 | if (options.executedRecords === options.totalRecords) { 661 | this.updateComplete(options); 662 | } 663 | 664 | }, 665 | 666 | updateComplete: function(options) { 667 | 668 | if (options.operation.process(options.resultSet) === false) { 669 | this.fireEvent('exception', this, options.operation); 670 | } 671 | 672 | if (options.errors) { 673 | options.operation.setException(options.errors); 674 | } 675 | 676 | Ext.callback(options.callback, options.scope, [options.operation]); 677 | 678 | } 679 | 680 | }); 681 | -------------------------------------------------------------------------------- /packages/local/dbproxies/src/data/proxy/Sql.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sql proxy to save Ext.data.Model instances for offline use. Will default to cordova sqlitePlugin if present, 3 | * otherwise will use websql. Model schema changes are not supported once deployed to production. 4 | */ 5 | Ext.define('DBProxies.data.proxy.Sql', { 6 | alias: 'proxy.sql', 7 | extend: 'DBProxies.data.proxy.Db', 8 | 9 | requires: [ 10 | 'DBProxies.data.SqlConnection' 11 | ], 12 | 13 | isSQLProxy: true, 14 | 15 | config: { 16 | /** 17 | * @cfg {String} tableName 18 | * The name of the sql table. Will default to the string after the last '.' in the model's class name 19 | */ 20 | tableName: null, 21 | 22 | /** 23 | * @cfg {String} defaultDateFormat 24 | * The date format to use to store data in sql 25 | */ 26 | defaultDateFormat: 'Y-m-d H:i:s.u', 27 | 28 | /** 29 | * @cfg {String} implicitFieldsColName 30 | * The name of the database column that will store all the implicit fields. This only needs to be changed if 31 | * for some reason the model has an explicitly defined field named 'implicit' 32 | */ 33 | implicitFieldsColName: 'implicit' 34 | }, 35 | 36 | statics: { 37 | isSupported: function() { 38 | return !!window.openDatabase; 39 | } 40 | }, 41 | 42 | getDatabaseObject: function() { 43 | return DBProxies.data.SqlConnection.getConn(); 44 | }, 45 | 46 | updateModel: function(model) { 47 | 48 | var modelName; 49 | var defaultDateFormat; 50 | var table; 51 | 52 | if (model) { 53 | 54 | modelName = model.prototype.entityName; 55 | defaultDateFormat = this.getDefaultDateFormat(); 56 | table = modelName.slice(modelName.lastIndexOf('.') + 1); 57 | 58 | Ext.each(model.getFields(), function(field) { 59 | if (field.getType().type === 'date' && !field.getDateFormat()) { 60 | field.setDateFormat(defaultDateFormat); 61 | } 62 | }, this); 63 | 64 | if (!this.getTableName()) { 65 | this.setTableName(table); 66 | } 67 | 68 | this.columns = this.getPersistedModelColumns(model); 69 | } 70 | 71 | this.callParent(arguments); 72 | 73 | }, 74 | 75 | createTable: function(transaction) { 76 | 77 | transaction.executeSql([ 78 | 'CREATE TABLE IF NOT EXISTS ', 79 | this.getTableName(), 80 | ' (', this.getSchemaString(), ')' 81 | ].join('')); 82 | this.tableExists = true; 83 | 84 | }, 85 | 86 | getColumnValues: function(columns, data) { 87 | 88 | var ln = columns.length, 89 | values = [], 90 | i, column, value; 91 | 92 | for (i = 0; i < ln; i++) { 93 | column = columns[i]; 94 | value = data[column]; 95 | if (value !== undefined) { 96 | values.push(value); 97 | } 98 | } 99 | 100 | return values; 101 | 102 | }, 103 | 104 | getPersistedModelColumns: function(model) { 105 | 106 | var fields = model.getFields().items; 107 | var columns = []; 108 | var ln = fields.length; 109 | var i; 110 | var field; 111 | var name; 112 | 113 | for (i = 0; i < ln; i++) { 114 | field = fields[i]; 115 | name = field.getName(); 116 | 117 | if (field.getPersist()) { 118 | columns.push(field.getName()); 119 | } 120 | } 121 | 122 | if (this.getImplicitFields()) { 123 | columns.push(this.getImplicitFieldsColName()); 124 | } 125 | 126 | return columns; 127 | 128 | }, 129 | 130 | writeDate: function(field, date) { 131 | 132 | if (Ext.isEmpty(date)) { 133 | return null; 134 | } 135 | 136 | var dateFormat = field.getDateFormat() || this.getDefaultDateFormat(); 137 | switch (dateFormat) { 138 | case 'timestamp': 139 | return date.getTime() / 1000; 140 | case 'time': 141 | return date.getTime(); 142 | default: 143 | return date.getTime(); 144 | } 145 | 146 | }, 147 | 148 | dropTable: function(config) { 149 | 150 | var me = this; 151 | var table = me.getTableName(); 152 | var callback = config ? config.callback : null; 153 | var scope = config ? config.scope || me : null; 154 | var db = me.getDatabaseObject(); 155 | 156 | db.transaction(function(transaction) { 157 | transaction.executeSql('DROP TABLE ' + table); 158 | }, 159 | function(transaction, error) { 160 | if (typeof callback == 'function') { 161 | callback.call(scope || me, false, table, error); 162 | } 163 | }, 164 | function(transaction) { 165 | if (typeof callback == 'function') { 166 | callback.call(scope || me, true, table); 167 | } 168 | } 169 | ); 170 | 171 | me.tableExists = false; 172 | 173 | }, 174 | 175 | getSchemaString: function() { 176 | 177 | var schema = []; 178 | var model = this.getModel(); 179 | var idProperty = model.prototype.getIdProperty(); 180 | var fields = model.getFields().items; 181 | var ln = fields.length; 182 | var i; 183 | var field; 184 | var type; 185 | var name; 186 | var persist; 187 | 188 | for (i = 0; i < ln; i++) { 189 | field = fields[i]; 190 | 191 | if (!field.type) { 192 | continue; 193 | } 194 | type = field.type; 195 | name = field.name; 196 | persist = field.persist; 197 | if (!persist) { 198 | continue; 199 | } 200 | 201 | type = this.convertToSqlType(type); 202 | 203 | if (name === idProperty) { 204 | schema.unshift(idProperty + ' ' + type + ' PRIMARY KEY'); 205 | } else { 206 | schema.push(name + ' ' + type); 207 | } 208 | } 209 | 210 | if (this.getImplicitFields()) { 211 | schema.push(this.getImplicitFieldsColName() + ' TEXT'); 212 | } 213 | 214 | return schema.join(', '); 215 | 216 | }, 217 | 218 | decodeRecordData: function(data) { 219 | 220 | var key; 221 | var newData = {}; 222 | var fields = this.getModel().getFields().items; 223 | var fieldTypes = {}; 224 | 225 | Ext.each(fields, function(field) { 226 | fieldTypes[field.getName()] = field.type; 227 | }); 228 | 229 | for (key in data) { 230 | if (Ext.isDefined(fieldTypes[key]) && 231 | (fieldTypes[key] == 'auto') && 232 | Ext.isString(data[key]) && 233 | Ext.Array.contains(['[', '{'], data[key][0])) { 234 | if (Ext.isEmpty(data[key])) { 235 | newData[key] = null; 236 | } else { 237 | newData[key] = Ext.decode(data[key]); 238 | } 239 | } else if (key === this.getImplicitFieldsColName()) { 240 | Ext.apply(newData, Ext.JSON.decode(data[key])); 241 | } else { 242 | newData[key] = data[key]; 243 | } 244 | } 245 | 246 | return newData; 247 | }, 248 | 249 | getRecordData: function(record) { 250 | 251 | var fields = record.getFields(); 252 | var data = {}; 253 | var name; 254 | var value; 255 | var newValue; 256 | var explicitFieldNames = []; 257 | var implicitData = {}; 258 | var field; 259 | 260 | Ext.each(fields, function(field) { 261 | 262 | explicitFieldNames.push(field.name); 263 | if (!Ext.isDefined(field.persist) || field.persist) { 264 | name = field.name; 265 | 266 | value = record.get(name); 267 | if (field.type == 'date') { 268 | newValue = this.writeDate(field, value); 269 | } 270 | else if (!Ext.isDefined(value)) { 271 | newValue = ""; 272 | } 273 | else if (field.type == 'auto' && (Ext.isObject(value) || Ext.isArray(value))) { 274 | if (Ext.isEmpty(value)) { 275 | newValue = ""; 276 | } else { 277 | newValue = Ext.encode(value); 278 | } 279 | } else { 280 | newValue = value; 281 | } 282 | data[name] = newValue; 283 | } 284 | 285 | }, this); 286 | 287 | if (this.getImplicitFields()) { 288 | for (field in record.data) { 289 | if (!Ext.Array.contains(explicitFieldNames, field)) { 290 | implicitData[field] = record.data[field]; 291 | } 292 | } 293 | data[this.getImplicitFieldsColName()] = Ext.JSON.encode(implicitData); 294 | } 295 | 296 | return data; 297 | }, 298 | 299 | convertToSqlType: function(type) { 300 | switch (type.toLowerCase()) { 301 | case 'date': 302 | case 'string': 303 | case 'array': 304 | case 'object': 305 | case 'auto': 306 | return 'TEXT'; 307 | case 'int': 308 | case 'integer': 309 | return 'INTEGER'; 310 | case 'float': 311 | return 'REAL'; 312 | case 'bool': 313 | case 'boolean': 314 | return 'NUMERIC'; 315 | } 316 | }, 317 | 318 | transactionError: function(tx, error) { 319 | //return; 320 | var args = arguments; 321 | var options = args[args.length - 1]; 322 | console.error('sql proxy transaction error: ', arguments); 323 | this.setException(options.operation, error); 324 | if (options.callback) { 325 | Ext.callback(options.callback, options.scope, [options.operation]); 326 | } 327 | }, 328 | 329 | 330 | /* CREATE */ 331 | create: function(operation) { 332 | 333 | var options = { 334 | operation: operation, 335 | records: operation.getRecords() 336 | }; 337 | 338 | operation.setStarted(); 339 | this.getDatabaseObject().transaction( 340 | Ext.bind(this.createTransaction, this, [options], true), 341 | Ext.bind(this.transactionError, this, [options], true) 342 | ); 343 | 344 | }, 345 | 346 | createTransaction: function(tx) { 347 | 348 | var args = arguments; 349 | var options = args[args.length - 1]; 350 | var tmp = []; 351 | var i; 352 | var ln; 353 | var placeholders; 354 | 355 | if (!this.tableExists) { 356 | this.createTable(tx); 357 | } 358 | 359 | for (i = 0, ln = this.columns.length; i < ln; i++) { 360 | tmp.push('?'); 361 | } 362 | placeholders = tmp.join(', '); 363 | 364 | Ext.apply(options, { 365 | tx: tx, 366 | resultSet: new Ext.data.ResultSet({ 367 | success: true 368 | }), 369 | table: this.getTableName(), 370 | columns: this.columns, 371 | totalRecords: options.records.length, 372 | executedRecords: 0, 373 | errors: [], 374 | placeholders: placeholders 375 | }); 376 | 377 | Ext.each(options.records, Ext.bind(this.createRecord, this, [options], true)); 378 | 379 | }, 380 | 381 | createRecord: function(record, i, records, options) { 382 | 383 | if (!record.phantom) { 384 | options.executedRecords += 1; 385 | this.createRecordCallback(options); 386 | return; 387 | } 388 | 389 | var id = record.getId(); 390 | var data = this.getRecordData(record); 391 | var values = this.getColumnValues(options.columns, data); 392 | 393 | options.tx.executeSql([ 394 | 'INSERT INTO ', options.table, 395 | ' (', options.columns.join(', '), ')', 396 | ' VALUES (', options.placeholders, ')' 397 | ].join(''), values, 398 | Ext.bind(this.createRecordSuccess, this, [options, record, data], true), 399 | Ext.bind(this.createRecordError, this, [options, record], true) 400 | ); 401 | 402 | }, 403 | 404 | createRecordSuccess: function(tx, result, options, record, data) { 405 | 406 | data = this.decodeRecordData(data); 407 | 408 | if (this.getCloud() && record.session) { 409 | record.session.addOperation({ 410 | model: record.get('model'), 411 | record_id: record.getId(), 412 | type: 'create', 413 | fields: data 414 | }); 415 | } 416 | 417 | options.executedRecords += 1; 418 | 419 | record.phantom = false; 420 | record.commit(); 421 | 422 | this.createRecordCallback(options); 423 | 424 | }, 425 | 426 | createRecordError: function(tx, error, options, record) { 427 | 428 | console.error('INSERT ERROR:', error); 429 | 430 | options.executedRecords += 1; 431 | options.errors.push({ 432 | clientId: record.getId(), 433 | error: error 434 | }); 435 | 436 | this.createRecordCallback(options); 437 | 438 | }, 439 | 440 | createRecordCallback: function(options) { 441 | if (options.executedRecords === options.totalRecords) { 442 | this.createComplete(options); 443 | } 444 | }, 445 | 446 | createComplete: function(options) { 447 | 448 | if (options.operation.process(options.resultSet) === false) { 449 | this.fireEvent('exception', this, options.operation); 450 | } 451 | 452 | if (options.errors) { 453 | options.operation.setException(options.errors); 454 | } 455 | 456 | }, 457 | 458 | 459 | /* ERASE */ 460 | erase: function(operation, callback, scope) { 461 | 462 | var erasedRecords = []; 463 | var options = { 464 | operation: operation, 465 | callback: callback || Ext.emptyFn, 466 | scope: scope || {}, 467 | records: operation.getRecords(), 468 | erasedRecords: erasedRecords, 469 | resultSet: new Ext.data.ResultSet({ 470 | records: erasedRecords, 471 | success: true 472 | }) 473 | }; 474 | 475 | operation.setStarted(); 476 | 477 | this.getDatabaseObject().transaction( 478 | Ext.bind(this.eraseTransaction, this, [options], true), 479 | Ext.bind(this.transactionError, this, [options], true), 480 | Ext.bind(this.eraseTransactionSuccess, this, [options], true) 481 | ); 482 | 483 | }, 484 | 485 | eraseTransaction: function(tx) { 486 | 487 | var args = arguments; 488 | var options = args[args.length - 1]; 489 | 490 | if (!this.tableExists) { 491 | this.createTable(tx); 492 | } 493 | 494 | Ext.apply(options, { 495 | tx: tx, 496 | idProperty: this.getModel().prototype.getIdProperty(), 497 | table: this.getTableName(), 498 | errors: [] 499 | }); 500 | 501 | Ext.each(options.records, Ext.bind(this.eraseRecord, this, [options], true)); 502 | 503 | }, 504 | 505 | eraseRecord: function(record, i, records, options) { 506 | options.tx.executeSql([ 507 | 'DELETE FROM ', options.table, 508 | ' WHERE ', options.idProperty, ' = ?' 509 | ].join(''), [record.getId()], 510 | Ext.bind(this.eraseRecordSuccess, this, [options, record], true), 511 | Ext.emptyFn 512 | ); 513 | }, 514 | 515 | eraseRecordSuccess: function(tx, result, options, record) { 516 | 517 | if (this.getCloud() && record.session) { 518 | record.session.addOperation({ 519 | model: record.get('model'), 520 | record_id: record.getId(), 521 | type: 'delete' 522 | }); 523 | } 524 | 525 | options.erasedRecords.push(record); 526 | 527 | }, 528 | 529 | eraseTransactionSuccess: function() { 530 | var args = arguments; 531 | var options = args[args.length - 1]; 532 | 533 | if (options.operation.process(options.resultSet) === false) { 534 | this.fireEvent('exception', this, options.operation); 535 | } 536 | 537 | if (options.error) { 538 | options.operation.setException(options.error); 539 | } 540 | 541 | Ext.callback(options.callback, options.scope, [options.operation]); 542 | }, 543 | 544 | 545 | /* READ */ 546 | read: function(operation, callback, scope) { 547 | 548 | var options = { 549 | operation: operation, 550 | callback: callback || Ext.emptyFn, 551 | scope: scope || {} 552 | }; 553 | 554 | operation.setStarted(); 555 | this.getDatabaseObject().transaction( 556 | Ext.bind(this.readTransaction, this, [options], true), 557 | Ext.bind(this.transactionError, this, [options], true) 558 | ); 559 | 560 | }, 561 | 562 | readTransaction: function(tx) { 563 | 564 | var args = arguments; 565 | var options = args[args.length - 1]; 566 | var records = []; 567 | var values = []; 568 | var sql; 569 | var params = options.operation.getParams() || {}; 570 | 571 | if (!this.tableExists) { 572 | this.createTable(tx); 573 | } 574 | 575 | Ext.apply(params, { 576 | page: options.operation.getPage(), 577 | start: options.operation.getStart(), 578 | limit: options.operation.getLimit(), 579 | sorters: options.operation.getSorters(), 580 | filters: options.operation.getFilters(), 581 | recordId: options.operation.getId() 582 | }); 583 | 584 | Ext.apply(options, { 585 | tx: tx, 586 | idProperty: this.getModel().prototype.getIdProperty(), 587 | recordCreator: options.operation.getRecordCreator(), 588 | params: params, 589 | records: records, 590 | resultSet: new Ext.data.ResultSet({ 591 | records: records, 592 | success: true 593 | }), 594 | table: this.getTableName(), 595 | errors: [] 596 | }); 597 | 598 | if (options.params.recordId) { 599 | sql = this.readFromIdBuildQuery(options, values); 600 | } else { 601 | sql = this.readMultipleBuildQuery(options, values); 602 | } 603 | 604 | options.tx.executeSql(sql, values, 605 | Ext.bind(this.readQuerySuccess, this, [options], true), 606 | Ext.bind(this.readQueryError, this, [options], true) 607 | ); 608 | 609 | }, 610 | 611 | readQuerySuccess: function(tx, result, options) { 612 | 613 | var rows = result.rows; 614 | var count = rows.length; 615 | var i; 616 | var data; 617 | var model = this.getModel(); 618 | 619 | for (i = 0; i < count; i += 1) { 620 | data = this.decodeRecordData(rows.item(i)); 621 | options.records.push(Ext.isFunction(options.recordCreator) ? 622 | options.recordCreator(data, model) : 623 | new model(data)); 624 | } 625 | 626 | options.resultSet.setSuccess(true); 627 | options.resultSet.setTotal(count); 628 | options.resultSet.setCount(count); 629 | 630 | this.readComplete(options); 631 | 632 | }, 633 | 634 | readQueryError: function(tx, errors, options) { 635 | 636 | console.error('READ ERROR:', errors); 637 | options.errors.push(errors); 638 | 639 | options.resultSet.setSuccess(false); 640 | options.resultSet.setTotal(0); 641 | options.resultSet.setCount(0); 642 | }, 643 | 644 | readFromIdBuildQuery: function(options, values) { 645 | values.push(options.params.recordId); 646 | return [ 647 | 'SELECT * FROM ', options.table, 648 | ' WHERE ', options.idProperty, ' = ?' 649 | ].join(''); 650 | }, 651 | 652 | readMultipleBuildQuery: function(options, values) { 653 | 654 | var ln; 655 | var i; 656 | var filter; 657 | var sorter; 658 | var property; 659 | var value; 660 | var sql = [ 661 | 'SELECT * FROM ', options.table 662 | ].join(''); 663 | 664 | // filters 665 | if (options.params.filters && options.params.filters.length) { 666 | ln = options.params.filters.length; 667 | for (i = 0; i < ln; i += 1) { 668 | filter = options.params.filters[i]; 669 | property = filter.getProperty(); 670 | value = filter.getValue(); 671 | if (property !== null) { 672 | sql += [ 673 | i === 0 ? ' WHERE ' : ' AND ', property, 674 | ' ', (filter.getAnyMatch() ? ('LIKE \'%' + value + '%\'') : '= ?') 675 | ].join(''); 676 | if (!filter.getAnyMatch()) { 677 | values.push(value); 678 | } 679 | } 680 | } 681 | } 682 | 683 | // sorters 684 | if (options.params.sorters && options.params.sorters.length) { 685 | ln = options.params.sorters.length; 686 | for (i = 0; i < ln; i += 1) { 687 | sorter = options.params.sorters[i]; 688 | property = sorter.getProperty(); 689 | if (property !== null) { 690 | sql += [ 691 | i === 0 ? ' ORDER BY ' : ', ', property, ' ', sorter.getDirection() 692 | ].join(''); 693 | } 694 | } 695 | } 696 | 697 | // handle start, limit, sort, filter and group params 698 | if (Ext.isDefined(options.params.page)) { 699 | sql += [ 700 | ' LIMIT ' + parseInt(options.params.start, 10) + ', ' + parseInt(options.params.limit, 10) 701 | ].join(''); 702 | } 703 | 704 | return sql; 705 | }, 706 | 707 | readComplete: function(options) { 708 | 709 | if (options.operation.process(options.resultSet) === false) { 710 | this.fireEvent('exception', this, options.operation); 711 | } 712 | 713 | if (options.errors) { 714 | options.operation.setException(options.errors); 715 | } 716 | 717 | Ext.callback(options.callback, options.scope, [options.operation]); 718 | 719 | }, 720 | 721 | 722 | /* UPDATE */ 723 | update: function(operation, callback, scope) { 724 | 725 | var options = { 726 | operation: operation, 727 | callback: callback || Ext.emptyFn, 728 | scope: scope || {}, 729 | records: operation.getRecords() 730 | }; 731 | 732 | operation.setStarted(); 733 | this.getDatabaseObject().transaction( 734 | Ext.bind(this.updateTransaction, this, [options], true), 735 | Ext.bind(this.transactionError, this, [options], true) 736 | ); 737 | 738 | }, 739 | 740 | updateTransaction: function(tx) { 741 | 742 | var args = arguments; 743 | var options = args[args.length - 1]; 744 | var updatedRecords = []; 745 | 746 | if (!this.tableExists) { 747 | this.createTable(tx); 748 | } 749 | 750 | Ext.apply(options, { 751 | tx: tx, 752 | idProperty: this.getModel().prototype.getIdProperty(), 753 | updatedRecords: updatedRecords, 754 | resultSet: new Ext.data.ResultSet({ 755 | records: updatedRecords, 756 | success: true 757 | }), 758 | table: this.getTableName(), 759 | columns: this.columns, 760 | totalRecords: options.records.length, 761 | executedRecords: 0, 762 | errors: [] 763 | }); 764 | 765 | Ext.each(options.records, Ext.bind(this.updateRecord, this, [options], true)); 766 | 767 | }, 768 | 769 | updateRecord: function(record, rI, records, options) { 770 | 771 | var id = record.getId(); 772 | var data = this.getRecordData(record); 773 | var values = this.getColumnValues(options.columns, data); 774 | var modValues = []; 775 | var updates = []; 776 | var col; 777 | var modifiedKeys = Ext.isObject(record.modified) ? Ext.Object.getKeys(record.modified) : []; 778 | var modData = {}; 779 | var ln; 780 | var i; 781 | var fields = record.getFields(); 782 | var explicitFieldNames = []; 783 | var field; 784 | var implicitData = {}; 785 | var implicitChanged = false; 786 | 787 | for (i = 0, ln = options.columns.length; i < ln; i++) { 788 | col = options.columns[i]; 789 | if (!Ext.Array.contains(modifiedKeys, col)) { 790 | continue; 791 | } 792 | updates.push(col + ' = ?'); 793 | modValues.push(values[i]); 794 | modData[col] = record.data[col]; 795 | } 796 | 797 | if (this.getImplicitFields()) { 798 | Ext.each(fields, function(field) { 799 | explicitFieldNames.push(field.name); 800 | }, this); 801 | for (field in record.data) { 802 | if (Ext.Array.contains(explicitFieldNames, field)) { 803 | continue; 804 | } 805 | implicitData[field] = record.data[field]; 806 | if (!Ext.Array.contains(modifiedKeys, field)) { 807 | continue; 808 | } 809 | implicitChanged = true; 810 | modData[field] = record.data[field]; 811 | } 812 | } 813 | 814 | if (implicitChanged) { 815 | updates.push(this.getImplicitFieldsColName() + ' = ?'); 816 | modValues.push(Ext.JSON.encode(implicitData)); 817 | } 818 | 819 | if (!updates.length) { 820 | this.updateRecordSuccess(options.tx, null, options, record, data, modData); 821 | return; 822 | } 823 | 824 | options.tx.executeSql([ 825 | 'UPDATE ', options.table, 826 | ' SET ', updates.join(', '), 827 | ' WHERE ', options.idProperty, ' = ?' 828 | ].join(''), modValues.concat(id), 829 | Ext.bind(this.updateRecordSuccess, this, [options, record, data, modData], true), 830 | Ext.bind(this.updateRecordError, this, [options, record], true) 831 | ); 832 | 833 | }, 834 | 835 | updateRecordSuccess: function(tx, result, options, record, data, modData) { 836 | 837 | var recordId = record.getId(); 838 | var key; 839 | var model = record.get('model'); 840 | 841 | if (this.getCloud() && record.session) { 842 | for (key in modData) { 843 | record.session.addOperation({ 844 | model: model, 845 | record_id: recordId, 846 | type: 'update', 847 | field: key, 848 | value: modData[key] 849 | }); 850 | } 851 | } 852 | 853 | data = this.decodeRecordData(data); 854 | options.updatedRecords.push(data); 855 | 856 | options.executedRecords += 1; 857 | 858 | this.updateRecordCallback(options); 859 | }, 860 | 861 | updateRecordError: function(tx, error, options, record) { 862 | 863 | console.error('UPDATE ERROR:', error); 864 | 865 | options.executedRecords += 1; 866 | options.errors.push({ 867 | clientId: record.getId(), 868 | error: error 869 | }); 870 | 871 | this.updateRecordCallback(options); 872 | 873 | }, 874 | 875 | updateRecordCallback: function(options) { 876 | if (options.executedRecords === options.totalRecords) { 877 | this.updateComplete(options); 878 | } 879 | }, 880 | 881 | updateComplete: function(options) { 882 | if (options.operation.process(options.resultSet) === false) { 883 | this.fireEvent('exception', this, options.operation); 884 | } 885 | 886 | if (options.errors) { 887 | options.operation.setException(options.errors); 888 | } 889 | 890 | Ext.callback(options.callback, options.scope, [options.operation]); 891 | } 892 | 893 | }); 894 | -------------------------------------------------------------------------------- /packages/local/dbproxies/src/overrides/ext/data/Model.js: -------------------------------------------------------------------------------- 1 | Ext.define('DBProxies.overrides.ext.data.Model', { 2 | override: 'Ext.data.Model', 3 | 4 | inheritableStatics: { 5 | 6 | getProxy: function() { 7 | 8 | var me = this, 9 | proxy = me.proxy, 10 | defaultProxy = me.defaultProxy, 11 | defaults; 12 | 13 | if (!proxy) { 14 | // Check what was defined by the class (via onClassExtended): 15 | proxy = me.proxyConfig; 16 | 17 | if (!proxy && defaultProxy) { 18 | proxy = defaultProxy; 19 | } 20 | 21 | if (!proxy || !proxy.isProxy) { 22 | if (typeof proxy === 'string') { 23 | proxy = { 24 | type: proxy 25 | }; 26 | } 27 | 28 | if (proxy && proxy.type === 'dynamic') { 29 | proxy = DBProxies.data.proxy.Dynamic.applyDynamicProxy(proxy); 30 | } 31 | 32 | // We have nothing or a config for the proxy. Get some defaults from 33 | // the Schema and smash anything we've provided over the top. 34 | defaults = me.schema.constructProxy(me); 35 | proxy = proxy ? Ext.merge(defaults, proxy) : defaults; 36 | } 37 | 38 | proxy = me.setProxy(proxy); 39 | } 40 | 41 | return proxy; 42 | } 43 | 44 | } 45 | 46 | }); 47 | -------------------------------------------------------------------------------- /workspace.json: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | * This is the folder for build outputs in the workspace. 4 | */ 5 | "build": { 6 | "dir": "${workspace.dir}/build" 7 | }, 8 | 9 | /** 10 | * These configs determine where packages are generated and extracted to (when downloaded). 11 | */ 12 | "packages": { 13 | /** 14 | * This folder contains all local packages. 15 | * If a comma-separated string is used as value the first path will be used as the path to generate new packages. 16 | */ 17 | "dir": "${workspace.dir}/packages/local,${workspace.dir}/packages", 18 | 19 | /** 20 | * This folder contains all extracted (remote) packages. 21 | */ 22 | "extract": "${workspace.dir}/packages/remote" 23 | } 24 | } 25 | --------------------------------------------------------------------------------