├── .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 |
--------------------------------------------------------------------------------