Stefan Huber

init

1 +node_modules/
2 +.vscode/
1 +# bsync-client
2 +
3 +Allows downloading of files referenced by a http/https URI inside a couchdb/pouchdb record. The plugin works for cordova and electron projects.
4 +
5 +# Usage
6 +
7 +### In cordova projects
8 +
9 +### In electron projects
10 +
11 +Within the main process bsync needs to be integrated an initiated.
12 +
13 + import {Bsync} from 'bsync';
14 + Bsync.init(ipcMain, filePath);
15 +
1 +'use strict';
2 +
3 +Object.defineProperty(exports, '__esModule', { value: true });
4 +
5 +var events = require('events');
6 +var rxjs = require('rxjs');
7 +
8 +function __extends(d, b) {
9 + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
10 + function __() { this.constructor = d; }
11 + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
12 +}
13 +
14 +var ElectronFileHandler = (function () {
15 + function ElectronFileHandler(ipcRenderer) {
16 + this.ipcRenderer = ipcRenderer;
17 + }
18 + ElectronFileHandler.prototype.download = function (source, target) {
19 + var _this = this;
20 + return rxjs.Observable.create(function (subscriber) {
21 + _this.ipcRenderer.once('bsync-download-complete', function () {
22 + _this.ipcRenderer.removeAllListeners('bsync-download-progress');
23 + _this.ipcRenderer.removeAllListeners('bsync-download-error');
24 + subscriber.complete();
25 + });
26 + _this.ipcRenderer.on('bsync-download-progress', function (progress) {
27 + subscriber.next(progress);
28 + });
29 + _this.ipcRenderer.once('bsync-download-error', function (error) {
30 + _this.ipcRenderer.removeAllListeners('bsync-download-progress');
31 + _this.ipcRenderer.removeAllListeners('bsync-download-complete');
32 + subscriber.error(error);
33 + });
34 + _this.ipcRenderer.send('bsync-download', {
35 + source: source,
36 + target: target
37 + });
38 + });
39 + };
40 + return ElectronFileHandler;
41 +}());
42 +
43 +var Util = (function () {
44 + function Util() {
45 + }
46 + Util.getNameHash = function (path) {
47 + for (var r = 0, i = 0; i < path.length; i++) {
48 + r = (r << 5) - r + path.charCodeAt(i), r &= r;
49 + }
50 + return "bsync_" + Math.abs(r);
51 + };
52 + return Util;
53 +}());
54 +
55 +var FileReplicator = (function (_super) {
56 + __extends(FileReplicator, _super);
57 + function FileReplicator() {
58 + _super.call(this);
59 + this._files = [];
60 + this._itemValidator = null;
61 + this._fileHandler = null;
62 + this._retryTimeout = 0;
63 + this._itemKey = "type";
64 + this._itemValue = "asset";
65 + this._itemSourceAttribute = "source";
66 + this._itemTargetAttribute = "target";
67 + }
68 + Object.defineProperty(FileReplicator.prototype, "files", {
69 + get: function () {
70 + return this._files;
71 + },
72 + enumerable: true,
73 + configurable: true
74 + });
75 + Object.defineProperty(FileReplicator.prototype, "fileHandler", {
76 + set: function (handler) {
77 + this._fileHandler = handler;
78 + },
79 + enumerable: true,
80 + configurable: true
81 + });
82 + Object.defineProperty(FileReplicator.prototype, "retryTimeout", {
83 + set: function (timeout) {
84 + this._retryTimeout = timeout;
85 + },
86 + enumerable: true,
87 + configurable: true
88 + });
89 + Object.defineProperty(FileReplicator.prototype, "itemValidator", {
90 + set: function (validator) {
91 + this._itemValidator = validator;
92 + },
93 + enumerable: true,
94 + configurable: true
95 + });
96 + Object.defineProperty(FileReplicator.prototype, "itemKey", {
97 + get: function () {
98 + return this._itemKey;
99 + },
100 + set: function (key) {
101 + this._itemKey = key;
102 + },
103 + enumerable: true,
104 + configurable: true
105 + });
106 + Object.defineProperty(FileReplicator.prototype, "itemValue", {
107 + get: function () {
108 + return this._itemValue;
109 + },
110 + set: function (value) {
111 + this._itemValue = value;
112 + },
113 + enumerable: true,
114 + configurable: true
115 + });
116 + Object.defineProperty(FileReplicator.prototype, "itemSourceAttribute", {
117 + get: function () {
118 + return this._itemSourceAttribute;
119 + },
120 + set: function (sourceAttribute) {
121 + this._itemSourceAttribute = sourceAttribute;
122 + },
123 + enumerable: true,
124 + configurable: true
125 + });
126 + Object.defineProperty(FileReplicator.prototype, "itemTargetAttribute", {
127 + get: function () {
128 + return this._itemTargetAttribute;
129 + },
130 + set: function (targetAttribute) {
131 + this._itemTargetAttribute = targetAttribute;
132 + },
133 + enumerable: true,
134 + configurable: true
135 + });
136 + FileReplicator.prototype.init = function (files) {
137 + if (files === void 0) { files = []; }
138 + this._files = files;
139 + };
140 + /**
141 + * change from pouchdb replicate
142 + */
143 + FileReplicator.prototype.pushChanges = function (change) {
144 + var items = [];
145 + if (change && change.docs && change.docs.length > 0) {
146 + for (var _i = 0, _a = change.docs; _i < _a.length; _i++) {
147 + var item = _a[_i];
148 + if (item[this._itemKey] && item[this._itemKey] === this._itemValue) {
149 + items.push(item);
150 + }
151 + }
152 + }
153 + var files = this.prepareFiles(items);
154 + for (var _b = 0, files_1 = files; _b < files_1.length; _b++) {
155 + var file = files_1[_b];
156 + this._files.push(file);
157 + }
158 + };
159 + FileReplicator.prototype.downloadFiles = function (files, fileHandler, index) {
160 + var _this = this;
161 + if (index === void 0) { index = 0; }
162 + if (index >= files.length) {
163 + return;
164 + }
165 + this.emit('start', { progress: 0, index: index, length: files.length });
166 + fileHandler
167 + .download(files[index].source, files[index].target)
168 + .subscribe(function (progress) {
169 + _this.emit('progress', { progress: progress, index: index, length: files.length });
170 + }, function (error) {
171 + _this.emit('error', { progress: 0, index: index, length: files.length, error: error });
172 + }, function () {
173 + _this.emit('complete', { progress: 100, index: index, length: files.length });
174 + _this.downloadFiles(files, fileHandler, index + 1);
175 + });
176 + };
177 + FileReplicator.prototype.prepareFiles = function (items) {
178 + var output = [];
179 + for (var _i = 0, items_1 = items; _i < items_1.length; _i++) {
180 + var item = items_1[_i];
181 + if (item[this._itemSourceAttribute] && (!this._itemValidator || this._itemValidator(item))) {
182 + var file = { source: item[this._itemSourceAttribute], target: '' };
183 + if (item[this._itemTargetAttribute]) {
184 + file.target = item[this._itemTargetAttribute];
185 + }
186 + else {
187 + file.target = Util.getNameHash(file.source);
188 + }
189 + output.push(file);
190 + }
191 + }
192 + return output;
193 + };
194 + FileReplicator.prototype.start = function () {
195 + var _this = this;
196 + this.on('complete', function (event) {
197 + if ((event.index + 1) >= event.length) {
198 + _this.replicationFinalized(event.index);
199 + }
200 + });
201 + this.on('error', function (event) {
202 + _this.replicationFinalized(event.index);
203 + });
204 + this.downloadFiles(this._files, this._fileHandler);
205 + };
206 + FileReplicator.prototype.replicationFinalized = function (lastIndex) {
207 + var _this = this;
208 + if (lastIndex + 1 >= this._files.length) {
209 + this._files = [];
210 + this.emit('final');
211 + }
212 + else if (this._retryTimeout > 0) {
213 + this._files.splice(0, lastIndex);
214 + setTimeout(function () {
215 + _this.downloadFiles(_this._files, _this._fileHandler);
216 + }, this._retryTimeout);
217 + }
218 + };
219 + return FileReplicator;
220 +}(events.EventEmitter));
221 +
222 +var CONFIG_ITEM_KEY = "itemKey";
223 +var CONFIG_ITEM_VALUE = "itemValue";
224 +var CONFIG_ITEM_SOURCE_ATTRIBUTE = "itemSourceAttribute";
225 +var CONFIG_ITEM_TARGET_ATTRIBUTE = "itemTargetAttribute";
226 +
227 +var CONFIG_RETRY_TIMEOUT = "retryTimeout";
228 +
229 +var Config = (function () {
230 + function Config() {
231 + this.config = {};
232 + }
233 + Config.prototype.hasConfig = function (key) {
234 + if (this.config[key]) {
235 + return true;
236 + }
237 + return false;
238 + };
239 + Config.prototype.getConfig = function (key) {
240 + return this.config[key];
241 + };
242 + Config.prototype.setConfig = function (key, value) {
243 + this.config[key] = value;
244 + };
245 + return Config;
246 +}());
247 +
248 +var ENV_ELECTRON = "electron";
249 +var ENV_CORDOVA = "cordova";
250 +var ENV_UNKNOWN = "unknown";
251 +var ServiceLocator = (function () {
252 + function ServiceLocator() {
253 + }
254 + ServiceLocator.addFileHandler = function (environment, fileHandler) {
255 + ServiceLocator.fileHandlers[environment] = fileHandler;
256 + };
257 + ServiceLocator.getConfig = function () {
258 + if (!ServiceLocator.config) {
259 + ServiceLocator.config = new Config();
260 + }
261 + return ServiceLocator.config;
262 + };
263 + ServiceLocator.getEnvironment = function () {
264 + if (typeof window['require'] === 'function' && window['require']('electron')) {
265 + return ENV_ELECTRON;
266 + }
267 + if (typeof window['FileTransfer'] === 'function') {
268 + return ENV_CORDOVA;
269 + }
270 + return ENV_UNKNOWN;
271 + };
272 + ServiceLocator.getFileHandler = function () {
273 + var environment = ServiceLocator.getEnvironment();
274 + if (ServiceLocator.fileHandlers[environment]) {
275 + return ServiceLocator.fileHandlers[environment];
276 + }
277 + if (environment === ENV_ELECTRON) {
278 + return new ElectronFileHandler(window['require']('electron').ipcRenderer);
279 + }
280 + return null;
281 + };
282 + ServiceLocator.getFileReplicator = function () {
283 + if (!ServiceLocator.fileReplicator) {
284 + ServiceLocator.fileReplicator = new FileReplicator();
285 + ServiceLocator.fileReplicator.fileHandler = ServiceLocator.getFileHandler();
286 + if (ServiceLocator.getConfig().hasConfig(CONFIG_RETRY_TIMEOUT)) {
287 + ServiceLocator.fileReplicator.retryTimeout = ServiceLocator.getConfig().getConfig(CONFIG_RETRY_TIMEOUT);
288 + }
289 + if (ServiceLocator.getConfig().hasConfig(CONFIG_ITEM_KEY)) {
290 + ServiceLocator.fileReplicator.itemKey = ServiceLocator.getConfig().getConfig(CONFIG_ITEM_KEY);
291 + }
292 + if (ServiceLocator.getConfig().hasConfig(CONFIG_ITEM_VALUE)) {
293 + ServiceLocator.fileReplicator.itemValue = ServiceLocator.getConfig().getConfig(CONFIG_ITEM_VALUE);
294 + }
295 + if (ServiceLocator.getConfig().hasConfig(CONFIG_ITEM_SOURCE_ATTRIBUTE)) {
296 + ServiceLocator.fileReplicator.itemSourceAttribute = ServiceLocator.getConfig().getConfig(CONFIG_ITEM_SOURCE_ATTRIBUTE);
297 + }
298 + if (ServiceLocator.getConfig().hasConfig(CONFIG_ITEM_TARGET_ATTRIBUTE)) {
299 + ServiceLocator.fileReplicator.itemTargetAttribute = ServiceLocator.getConfig().getConfig(CONFIG_ITEM_TARGET_ATTRIBUTE);
300 + }
301 + }
302 + return ServiceLocator.fileReplicator;
303 + };
304 + ServiceLocator.fileHandlers = {};
305 + return ServiceLocator;
306 +}());
307 +
308 +function loadBsyncPlugin(PouchDB) {
309 + var pouchReplicate = PouchDB.replicate;
310 + PouchDB.plugin(function (PouchDB) {
311 + PouchDB.replicate = function () {
312 + var eventEmitter = new events.EventEmitter();
313 + var emitter = pouchReplicate.apply(this, arguments);
314 + var replicator = ServiceLocator.getFileReplicator();
315 + var db = arguments[1];
316 + replicator.once('final', function (event) {
317 + eventEmitter.emit('complete');
318 + eventEmitter.removeAllListeners();
319 + });
320 + replicator.on('error', function (event) {
321 + eventEmitter.emit('file-replicator-error', event);
322 + });
323 + replicator.on('complete', function (event) {
324 + eventEmitter.emit('file-replicator-complete', event);
325 + });
326 + replicator.on('progress', function (event) {
327 + eventEmitter.emit('file-replicator-progress', event);
328 + });
329 + emitter.once('change', function (info) {
330 + eventEmitter.emit('change', info);
331 + });
332 + emitter.once('complete', function (info) {
333 + db.query('index_type/type', {
334 + include_docs: true,
335 + key: replicator.itemValue
336 + }).then(function (res) {
337 + var docs = { docs: [] };
338 + for (var _i = 0, _a = res.rows; _i < _a.length; _i++) {
339 + var r = _a[_i];
340 + docs.docs.push(r.doc);
341 + }
342 + replicator.pushChanges(docs);
343 + replicator.start();
344 + }).catch(function (error) {
345 + eventEmitter.emit('error', error);
346 + });
347 + });
348 + emitter.once('error', function (error) {
349 + eventEmitter.emit('error', error);
350 + });
351 + return eventEmitter;
352 + };
353 + });
354 +}
355 +
356 +if (typeof window !== 'undefined' && window['PouchDB']) {
357 + loadBsyncPlugin(window['PouchDB']);
358 +}
359 +
360 +exports.loadBsyncPlugin = loadBsyncPlugin;
361 +exports.ENV_ELECTRON = ENV_ELECTRON;
362 +exports.ENV_CORDOVA = ENV_CORDOVA;
363 +exports.ENV_UNKNOWN = ENV_UNKNOWN;
364 +exports.ServiceLocator = ServiceLocator;
365 +//# sourceMappingURL=browser-build.js.map
1 +{"version":3,"file":null,"sources":["../src/file-handler/electron-file-handler.ts","../src/util.ts","../src/file-replicator.ts","../src/config.ts","../src/service-locator.ts","../src/browser-main.ts"],"sourcesContent":["import { Observable, Subscriber } from 'rxjs';\nimport { FileHandler } from '../api/file-handler';\n\nexport class ElectronFileHandler implements FileHandler {\n\n constructor (private ipcRenderer:any) {\n }\n\n download(source:string, target:string) : Observable<number> {\n return Observable.create((subscriber:Subscriber<number>) => {\n\n this.ipcRenderer.once('bsync-download-complete', () => {\n this.ipcRenderer.removeAllListeners('bsync-download-progress');\n this.ipcRenderer.removeAllListeners('bsync-download-error');\n subscriber.complete();\n });\n\n this.ipcRenderer.on('bsync-download-progress', (progress:number) => {\n subscriber.next(progress);\n });\n\n this.ipcRenderer.once('bsync-download-error', (error:any) => {\n this.ipcRenderer.removeAllListeners('bsync-download-progress');\n this.ipcRenderer.removeAllListeners('bsync-download-complete');\n subscriber.error(error);\n });\n\n this.ipcRenderer.send('bsync-download', {\n source : source ,\n target : target\n });\n });\n }\n\n}","export class Util {\n\n static getNameHash(path:string) {\n for(var r=0,i=0;i<path.length;i++) {\n r=(r<<5)-r+path.charCodeAt(i),r&=r;\n }\n return \"bsync_\" + Math.abs(r);\n }\n\n}","import {FileHandler} from './api/file-handler';\nimport {File} from './api/file';\nimport {Util} from './util';\nimport {EventEmitter} from 'events';\n\nexport class FileReplicator extends EventEmitter {\n\n constructor() {\n super();\n }\n\n protected _files:Array<File> = [];\n\n protected _itemValidator: (item:any) => boolean = null; \n protected _fileHandler:FileHandler = null; \n protected _retryTimeout:number = 0;\n \n protected _itemKey = \"type\";\n protected _itemValue = \"asset\";\n protected _itemSourceAttribute = \"source\";\n protected _itemTargetAttribute = \"target\";\n\n get files(): Array<File> {\n return this._files;\n }\n\n set fileHandler (handler:FileHandler) {\n this._fileHandler = handler;\n }\n\n set retryTimeout (timeout:number) {\n this._retryTimeout = timeout;\n }\n\n set itemValidator(validator:(item:any) => boolean) {\n this._itemValidator = validator;\n }\n\n set itemKey(key:string) {\n this._itemKey = key;\n }\n\n set itemValue(value:string) {\n this._itemValue = value;\n }\n\n set itemSourceAttribute(sourceAttribute:string) {\n this._itemSourceAttribute = sourceAttribute;\n }\n\n set itemTargetAttribute(targetAttribute:string) {\n this._itemTargetAttribute = targetAttribute;\n }\n\n get itemKey() {\n return this._itemKey;\n }\n\n get itemValue() {\n return this._itemValue;\n }\n\n get itemSourceAttribute() {\n return this._itemSourceAttribute;\n }\n\n get itemTargetAttribute() {\n return this._itemTargetAttribute;\n }\n\n init(files: Array<File> = []) {\n this._files = files;\n }\n\n /**\n * change from pouchdb replicate\n */\n pushChanges(change:any) {\n let items:Array<any> = [];\n\n if (change && change.docs && change.docs.length > 0) {\n for (let item of change.docs) {\n if (item[this._itemKey] && item[this._itemKey] === this._itemValue) {\n items.push(item);\n }\n }\n }\n \n let files = this.prepareFiles(items);\n\n for (let file of files) {\n this._files.push(file);\n }\n }\n\n downloadFiles(files:Array<File>, fileHandler:FileHandler, index:number = 0) { \n if (index >= files.length) {\n return;\n }\n\n this.emit('start', { progress: 0, index : index, length : files.length });\n\n fileHandler\n .download(files[index].source, files[index].target)\n .subscribe(\n progress => {\n this.emit('progress', { progress : progress, index : index, length : files.length })\n } ,\n error => {\n this.emit('error', { progress : 0, index : index, length : files.length, error: error });\n } ,\n () => {\n this.emit('complete', { progress : 100 , index : index, length : files.length });\n this.downloadFiles(files, fileHandler, index+1);\n }\n );\n }\n\n prepareFiles(items: Array<any>) : Array<File> {\n let output = [];\n\n for (let item of items) {\n if (item[this._itemSourceAttribute] && (!this._itemValidator || this._itemValidator(item))) {\n let file = { source : item[this._itemSourceAttribute] , target : '' };\n\n if (item[this._itemTargetAttribute]) {\n file.target = item[this._itemTargetAttribute];\n } else {\n file.target = Util.getNameHash(file.source);\n }\n\n output.push(file);\n }\n }\n\n return output;\n }\n\n start() {\n this.on('complete', (event:any) => {\n if ((event.index + 1) >= event.length) {\n this.replicationFinalized(event.index);\n }\n });\n\n this.on('error', (event:any) => {\n this.replicationFinalized(event.index);\n }); \n\n this.downloadFiles(this._files, this._fileHandler);\n }\n\n replicationFinalized(lastIndex:number) { \n if (lastIndex+1 >= this._files.length) { // all finished\n this._files = [];\n this.emit('final');\n } else if (this._retryTimeout > 0) { // restart after last success \n this._files.splice(0,lastIndex);\n setTimeout(() => { \n this.downloadFiles(this._files, this._fileHandler);\n }, this._retryTimeout);\n } \n }\n\n}","export const CONFIG_ITEM_KEY = \"itemKey\";\nexport const CONFIG_ITEM_VALUE = \"itemValue\";\nexport const CONFIG_ITEM_SOURCE_ATTRIBUTE = \"itemSourceAttribute\";\nexport const CONFIG_ITEM_TARGET_ATTRIBUTE = \"itemTargetAttribute\";\nexport const CONFIG_ITEM_VALIDATOR = \"itemValidator\";\nexport const CONFIG_RETRY_TIMEOUT = \"retryTimeout\";\nexport const CONFIG_FILE_HANDLER = \"fileHandler\";\n\nexport class Config {\n\n protected config:any = {};\n\n hasConfig(key:string) {\n if (this.config[key]) {\n return true;\n }\n return false;\n }\n\n getConfig(key:string) {\n return this.config[key];\n }\n\n setConfig(key:string, value:any) {\n this.config[key] = value;\n }\n\n}","import {FileHandler} from './api/file-handler';\nimport {ElectronFileHandler} from './file-handler/electron-file-handler';\nimport {FileReplicator} from './file-replicator';\nimport {\n Config,\n CONFIG_RETRY_TIMEOUT,\n CONFIG_ITEM_KEY,\n CONFIG_ITEM_VALUE,\n CONFIG_ITEM_TARGET_ATTRIBUTE,\n CONFIG_ITEM_SOURCE_ATTRIBUTE\n} from './config';\n\nexport const ENV_ELECTRON = \"electron\";\nexport const ENV_CORDOVA = \"cordova\";\nexport const ENV_UNKNOWN = \"unknown\";\n\nexport class ServiceLocator {\n\n protected static fileHandlers:any = {};\n protected static fileReplicator: FileReplicator;\n protected static config: Config;\n\n static addFileHandler(environment:string, fileHandler:FileHandler) {\n ServiceLocator.fileHandlers[environment] = fileHandler;\n }\n\n static getConfig() : Config {\n if (!ServiceLocator.config) {\n ServiceLocator.config = new Config();\n }\n\n return ServiceLocator.config;\n }\n\n static getEnvironment() {\n if (typeof window['require'] === 'function' && window['require']('electron')) {\n return ENV_ELECTRON;\n }\n if (typeof window['FileTransfer'] === 'function') {\n return ENV_CORDOVA;\n }\n\n return ENV_UNKNOWN;\n }\n\n static getFileHandler() : FileHandler {\n let environment = ServiceLocator.getEnvironment();\n\n if (ServiceLocator.fileHandlers[environment]) {\n return ServiceLocator.fileHandlers[environment];\n }\n\n if (environment === ENV_ELECTRON) {\n return new ElectronFileHandler(window['require']('electron').ipcRenderer);\n }\n \n return null;\n }\n\n static getFileReplicator() : FileReplicator {\n if (!ServiceLocator.fileReplicator) {\n\n ServiceLocator.fileReplicator = new FileReplicator();\n\n ServiceLocator.fileReplicator.fileHandler = ServiceLocator.getFileHandler();\n\n if (ServiceLocator.getConfig().hasConfig(CONFIG_RETRY_TIMEOUT)) {\n ServiceLocator.fileReplicator.retryTimeout = ServiceLocator.getConfig().getConfig(CONFIG_RETRY_TIMEOUT);\n }\n if (ServiceLocator.getConfig().hasConfig(CONFIG_ITEM_KEY)) {\n ServiceLocator.fileReplicator.itemKey = ServiceLocator.getConfig().getConfig(CONFIG_ITEM_KEY);\n }\n if (ServiceLocator.getConfig().hasConfig(CONFIG_ITEM_VALUE)) {\n ServiceLocator.fileReplicator.itemValue = ServiceLocator.getConfig().getConfig(CONFIG_ITEM_VALUE);\n }\n if (ServiceLocator.getConfig().hasConfig(CONFIG_ITEM_SOURCE_ATTRIBUTE)) {\n ServiceLocator.fileReplicator.itemSourceAttribute = ServiceLocator.getConfig().getConfig(CONFIG_ITEM_SOURCE_ATTRIBUTE);\n }\n if (ServiceLocator.getConfig().hasConfig(CONFIG_ITEM_TARGET_ATTRIBUTE)) {\n ServiceLocator.fileReplicator.itemTargetAttribute = ServiceLocator.getConfig().getConfig(CONFIG_ITEM_TARGET_ATTRIBUTE);\n }\n } \n\n return ServiceLocator.fileReplicator;\n }\n\n}","import { EventEmitter } from 'events';\nimport { ServiceLocator } from './service-locator';\n\nexport * from './service-locator';\n\nexport function loadBsyncPlugin (PouchDB) {\n let pouchReplicate = PouchDB.replicate; \n\n PouchDB.plugin((PouchDB) => {\n PouchDB.replicate = function() {\n let eventEmitter = new EventEmitter();\n let emitter = pouchReplicate.apply(this, arguments);\n let replicator = ServiceLocator.getFileReplicator();\n let db = arguments[1]; \n \n replicator.once('final', event => { \n eventEmitter.emit('complete');\n eventEmitter.removeAllListeners();\n });\n\n replicator.on('error', event => {\n eventEmitter.emit('file-replicator-error', event);\n });\n\n replicator.on('complete', event => {\n eventEmitter.emit('file-replicator-complete', event);\n });\n\n replicator.on('progress', event => {\n eventEmitter.emit('file-replicator-progress', event);\n });\n \n emitter.once('change', info => { \n eventEmitter.emit('change', info);\n });\n\n emitter.once('complete', info => { \n db.query('index_type/type',{\n include_docs : true,\n key : replicator.itemValue\n }).then((res) => { \n let docs = { docs : [] }; \n for (let r of res.rows) {\n docs.docs.push(r.doc);\n }\n\n replicator.pushChanges(docs);\n replicator.start();\n }).catch(error => {\n eventEmitter.emit('error', error);\n });\n });\n\n emitter.once('error', (error) => {\n eventEmitter.emit('error', error);\n });\n\n return eventEmitter;\n };\n });\n};\n\nif (typeof window !== 'undefined' && window['PouchDB']) {\n loadBsyncPlugin(window['PouchDB']);\n}\n"],"names":["Observable","EventEmitter"],"mappings":"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"}
...\ No newline at end of file ...\ No newline at end of file
1 +'use strict';
2 +
3 +var rxjs = require('rxjs');
4 +var http = require('http');
5 +var https = require('https');
6 +var fs = require('fs');
7 +
8 +var NodeFileHandler = (function () {
9 + function NodeFileHandler() {
10 + }
11 + NodeFileHandler.prototype.selectProtocol = function (url) {
12 + if (url.search(/^http:\/\//) === 0) {
13 + return http;
14 + }
15 + else if (url.search(/^https:\/\//) === 0) {
16 + return https;
17 + }
18 + else {
19 + return null;
20 + }
21 + };
22 + NodeFileHandler.prototype.download = function (source, target) {
23 + var handler = this.selectProtocol(source);
24 + return rxjs.Observable.create(function (subscriber) {
25 + if (!handler) {
26 + subscriber.error("No handler for source: " + source);
27 + return;
28 + }
29 + // file already exists and is not empty
30 + if (fs.existsSync(target) && (fs.statSync(target)['size'] > 0)) {
31 + subscriber.complete();
32 + return;
33 + }
34 + var file = fs.createWriteStream(target, { 'flags': 'a' });
35 + handler.get(source, function (response) {
36 + var size = response.headers['content-length']; // in bytes
37 + var prog = 0; // already downloaded
38 + var progCounts = 100; // how many progress events should be triggerd (1-100 %)
39 + var nextProg = (1 / progCounts);
40 + response.on('data', function (chunk) {
41 + prog += chunk.length;
42 + file.write(chunk, 'binary');
43 + if ((prog / size) > nextProg) {
44 + subscriber.next(prog / size);
45 + nextProg += (1 / progCounts);
46 + }
47 + });
48 + response.on('end', function () {
49 + file.end();
50 + subscriber.complete();
51 + });
52 + }).on('error', function (error) {
53 + fs.unlink(target);
54 + subscriber.error("Error while downloading: " + error);
55 + });
56 + });
57 + };
58 + return NodeFileHandler;
59 +}());
60 +
61 +var Bsync = (function () {
62 + function Bsync() {
63 + }
64 + Bsync.configIpcMain = function (ipcMain, downloadDir) {
65 + var nodeFileHander = new NodeFileHandler();
66 + ipcMain.on('bsync-download', function (event, args) {
67 + nodeFileHander.download(args.source, downloadDir + args.target)
68 + .subscribe(function (progress) { event.sender.send('bsync-download-progress', progress); }, function (error) { event.sender.send('bsync-download-error', error); }, function () { event.sender.send('bsync-download-complete'); });
69 + });
70 + };
71 + return Bsync;
72 +}());
73 +
74 +module.exports = Bsync;
75 +//# sourceMappingURL=node-build.js.map
1 +{"version":3,"file":null,"sources":["../src/file-handler/node-file-handler.ts","../src/node-main.ts"],"sourcesContent":["import { Observable, Subscriber } from 'rxjs';\nimport { FileHandler } from '../api/file-handler';\nimport * as http from 'http';\nimport * as https from 'https';\nimport * as fs from 'fs';\n\nexport class NodeFileHandler implements FileHandler {\n\n selectProtocol(url:string) : any {\n if (url.search(/^http:\\/\\//) === 0) {\n return http;\n } else if (url.search(/^https:\\/\\//) === 0) {\n return https;\n } else {\n return null;\n }\n }\n\n download(source:string, target:string) : Observable<number> {\n\n let handler = this.selectProtocol(source);\n\n return Observable.create((subscriber:Subscriber<number>) => {\n \n if (!handler) {\n subscriber.error(\"No handler for source: \" + source);\n return;\n }\n\n // file already exists and is not empty\n if (fs.existsSync(target) && (fs.statSync(target)['size'] > 0)) {\n subscriber.complete();\n return;\n }\n\n let file = fs.createWriteStream(target, {'flags': 'a'});\n\n handler.get(source, (response) => {\n let size = response.headers['content-length']; // in bytes\n let prog = 0; // already downloaded\n let progCounts = 100; // how many progress events should be triggerd (1-100 %)\n let nextProg = (1/progCounts);\n \n response.on('data', (chunk) => {\n prog += chunk.length;\n file.write(chunk, 'binary');\n\n if ((prog / size) > nextProg) {\n subscriber.next(prog / size);\n nextProg += (1 / progCounts);\n } \n });\n\n response.on('end', () => {\n file.end();\n subscriber.complete();\n });\n \n }).on('error', (error) => {\n fs.unlink(target);\n subscriber.error(\"Error while downloading: \" + error);\n });\n\n });\n\n }\n\n}","import { NodeFileHandler } from './file-handler/node-file-handler';\n\nexport default class Bsync {\n\n static configIpcMain(ipcMain: any, downloadDir:string) {\n let nodeFileHander = new NodeFileHandler();\n\n ipcMain.on('bsync-download', (event, args) => {\n nodeFileHander.download(args.source, downloadDir + args.target)\n .subscribe(\n (progress:number) => { event.sender.send('bsync-download-progress', progress); } ,\n (error:any) => { event.sender.send('bsync-download-error', error); } ,\n () => { event.sender.send('bsync-download-complete'); }\n );\n });\n }\n\n}"],"names":["Observable","fs.existsSync","fs.statSync","fs.createWriteStream","fs.unlink"],"mappings":"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"}
...\ No newline at end of file ...\ No newline at end of file
1 +// Karma configuration
2 +// Generated on Tue Jan 03 2017 13:04:16 GMT+0100 (CET)
3 +
4 +module.exports = function(config) {
5 + config.set({
6 +
7 + // base path that will be used to resolve all patterns (eg. files, exclude)
8 + basePath: '',
9 +
10 +
11 + // frameworks to use
12 + // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
13 + frameworks: ['jasmine'],
14 +
15 +
16 + // list of files / patterns to load in the browser
17 + files: [
18 + './node_modules/rxjs/bundles/Rx.min.js' ,
19 + './node_modules/pouchdb/dist/pouchdb.min.js' ,
20 + './.tmp/browser-test.js'
21 + ],
22 +
23 +
24 + // list of files to exclude
25 + exclude: [
26 + ],
27 +
28 +
29 + // preprocess matching files before serving them to the browser
30 + // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
31 + preprocessors: {
32 + },
33 +
34 +
35 + // test results reporter to use
36 + // possible values: 'dots', 'progress'
37 + // available reporters: https://npmjs.org/browse/keyword/karma-reporter
38 + reporters: ['progress'],
39 +
40 +
41 + // web server port
42 + port: 9876,
43 +
44 +
45 + // enable / disable colors in the output (reporters and logs)
46 + colors: true,
47 +
48 +
49 + // level of logging
50 + // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
51 + logLevel: config.LOG_INFO,
52 +
53 +
54 + // enable / disable watching file and executing tests whenever any file changes
55 + autoWatch: false,
56 +
57 +
58 + // start these browsers
59 + // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
60 + browsers: ['Chrome'],
61 +
62 +
63 + // Continuous Integration mode
64 + // if true, Karma captures browsers, runs the tests and exits
65 + singleRun: true,
66 +
67 + // Concurrency level
68 + // how many browser should be started simultaneous
69 + concurrency: Infinity
70 + })
71 +}
1 +{
2 + "name": "bsync-client",
3 + "version": "1.0.0",
4 + "description": "",
5 + "main": "dist/browser-build.js",
6 + "scripts": {
7 + "build": "npm run build:node && npm run build:browser",
8 + "build:node": "rollup -c ./rollup.config.node.js",
9 + "build:browser": "rollup --config ./rollup.config.browser.js",
10 + "pretest": "scripts/before-test.sh",
11 + "posttest": "scripts/after-test.sh",
12 + "test": "npm run test:node && npm run test:browser",
13 + "test:node": "rollup --config ./rollup.config.node-test.js && jasmine",
14 + "test:browser": "rollup --config ./rollup.config.browser-test.js && karma start",
15 + "test:cordova": "npm run build:browser && scripts/prepare-cordova-test.sh"
16 + },
17 + "author": "",
18 + "license": "ISC",
19 + "dependencies": {
20 + "rxjs": "^5.0.2"
21 + },
22 + "devDependencies": {
23 + "@types/jasmine": "^2.5.40",
24 + "cordova": "^6.4.0",
25 + "jasmine": "^2.5.2",
26 + "karma": "^1.3.0",
27 + "karma-chrome-launcher": "^2.0.0",
28 + "karma-jasmine": "^1.1.0",
29 + "pouchdb": "^6.1.0",
30 + "pouchdb-upsert": "^2.0.2",
31 + "rollup": "^0.39.2",
32 + "rollup-plugin-commonjs": "^7.0.0",
33 + "rollup-plugin-ignore": "^1.0.3",
34 + "rollup-plugin-node-builtins": "^2.0.0",
35 + "rollup-plugin-node-globals": "^1.1.0",
36 + "rollup-plugin-node-resolve": "^2.0.0",
37 + "rollup-plugin-typescript": "^0.8.1"
38 + }
39 +}
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
3 + id="bsync-client"
4 + version="1.0.0"
5 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
6 +
7 + <name>bsync client plugin</name>
8 + <description>Cordova plugin for syncing files referenced by pouchdb records</description>
9 + <author>Stefan Huber</author>
10 + <keywords>pouchdb, file sync</keywords>
11 + <license>Apache 2.0 License</license>
12 +
13 + <asset src="dist/browser-build.js" target="bsync.js"></asset>
14 +
15 + <dependency id="cordova-plugin-file-transfer" url="https://github.com/apache/cordova-plugin-file-transfer" commit="master" />
16 +
17 +</plugin>
...\ No newline at end of file ...\ No newline at end of file
1 +import typescript from 'rollup-plugin-typescript';
2 +import globals from 'rollup-plugin-node-globals';
3 +import builtins from 'rollup-plugin-node-builtins';
4 +
5 +export default {
6 + entry: './spec/browser-test.ts',
7 + dest: './.tmp/browser-test.js',
8 +
9 + format: 'umd',
10 +
11 + globals: {
12 + 'rxjs' : 'Rx'
13 + },
14 +
15 + plugins: [
16 + typescript() ,
17 + globals(),
18 + builtins()
19 + ]
20 +};
...\ No newline at end of file ...\ No newline at end of file
1 +import typescript from 'rollup-plugin-typescript';
2 +import builtins from 'rollup-plugin-node-builtins';
3 +import globals from 'rollup-plugin-node-globals';
4 +
5 +export default {
6 +
7 + moduleName : 'bsync',
8 +
9 + entry: './src/browser-main.ts',
10 + dest: './dist/browser-build.js',
11 +
12 + format: 'cjs',
13 +
14 + sourceMap: true ,
15 +
16 + plugins: [
17 + typescript(),
18 + // globals(),
19 + // builtins()
20 + ]
21 +};
...\ No newline at end of file ...\ No newline at end of file
1 +import typescript from 'rollup-plugin-typescript';
2 +
3 +export default {
4 + entry: './spec/node-test.ts',
5 + dest: './.tmp/node-test-build.spec.js',
6 +
7 + format: 'cjs',
8 +
9 + plugins: [
10 + typescript()
11 + ]
12 +};
...\ No newline at end of file ...\ No newline at end of file
1 +import typescript from 'rollup-plugin-typescript';
2 +
3 +export default {
4 + entry: './src/node-main.ts',
5 + dest: './dist/node-build.js',
6 +
7 + format: 'cjs',
8 +
9 + sourceMap: true ,
10 +
11 + plugins: [
12 + typescript()
13 + ]
14 +};
...\ No newline at end of file ...\ No newline at end of file
1 +#!/bin/bash
2 +
3 +curl -X DELETE http://admin:admin@127.0.0.1:5984/pouch_test_db
4 +rm -R ./.tmp
...\ No newline at end of file ...\ No newline at end of file
1 +#!/bin/bash
2 +
3 +curl -X DELETE http://admin:admin@127.0.0.1:5984/pouch_test_db
4 +curl -X PUT http://admin:admin@127.0.0.1:5984/pouch_test_db
1 +#!/bin/bash
2 +
3 +COMMAND=${1:-emulate}
4 +PLATFORM=${2:-android}
5 +
6 +echo "cordova $COMMAND $PLATFORM"
7 +
8 +cd ..
9 +rm -r ./bysnc-client-test-app
10 +./bsync-client/node_modules/.bin/cordova create bysnc-client-test-app
11 +cd ./bysnc-client-test-app
12 +
13 +../bsync-client/node_modules/.bin/cordova platform add $PLATFORM
14 +../bsync-client/node_modules/.bin/cordova plugin add ../bsync-client
15 +../bsync-client/node_modules/.bin/cordova plugin add ../bsync-client/spec/cordova
16 +../bsync-client/node_modules/.bin/cordova plugin add cordova-plugin-test-framework
17 +
18 +sed -i 's/index\.html/cdvtests\/index\.html/g' ./config.xml
19 +
20 +if [ $COMMAND == "run" ]; then
21 + ../bsync-client/node_modules/.bin/cordova run $PLATFORM
22 +else
23 + ../bsync-client/node_modules/.bin/cordova emulate $PLATFORM
24 +fi
1 +import PouchDB from 'pouchdb';
2 +import {FileReplicator} from '../src/file-replicator';
3 +import {ServiceLocator, ENV_UNKNOWN} from '../src/service-locator';
4 +import {TestFileHandler} from './file-handler/test-file-handler';
5 +
6 +ServiceLocator.addFileHandler(ENV_UNKNOWN, new TestFileHandler());
7 +
8 +import '../src/browser-main';
9 +
10 +declare var emit:any;
11 +
12 +const dbUrl = 'http://admin:admin@localhost:5984/pouch_test_db';
13 +
14 +const testDocs = [
15 + { 'type' : 'asset', 'source' : 'https://upload.wikimedia.org/wikipedia/commons/thumb/e/e1/FullMoon2010.jpg/292px-FullMoon2010.jpg' } ,
16 + { 'type' : 'asset', 'source' : 'https://upload.wikimedia.org/wikipedia/commons/thumb/2/2b/Jupiter_and_its_shrunken_Great_Red_Spot.jpg/260px-Jupiter_and_its_shrunken_Great_Red_Spot.jpg' } ,
17 + { 'type' : 'asset', 'source' : 'https://upload.wikimedia.org/wikipedia/commons/c/c7/Saturn_during_Equinox.jpg' }
18 +];
19 +
20 +describe("Integration tests with couchdb", () => {
21 +
22 + let index = 1;
23 + let remoteDb = new PouchDB(dbUrl);
24 + let localDb;
25 +
26 + beforeAll((done) => {
27 + ServiceLocator.getFileReplicator().retryTimeout = 100;
28 + remoteDb.bulkDocs(testDocs).then(() => {
29 + done();
30 + });
31 + });
32 +
33 + beforeEach(() => {
34 + localDb = new PouchDB('testdb-' + index);
35 + index++;
36 +
37 + localDb.put({
38 + _id : "_design/index_type",
39 + views : {
40 + type : {
41 + map : function(doc) {
42 + if (doc.type) { emit(doc.type); }
43 + }.toString()
44 + }
45 + }
46 + });
47 +
48 + });
49 +
50 + it("Should successfully download several files", (done) => {
51 + TestFileHandler.setErrorRate(0);
52 + ServiceLocator.getFileReplicator().init();
53 + let index = 0;
54 +
55 + localDb.replicate.from(dbUrl)
56 + .on('file-replicator-complete', event => {
57 + index++;
58 + })
59 + .on('complete', () => {
60 + expect(index).toEqual(3);
61 + done();
62 + });
63 + });
64 +
65 + it("Should trigger errors, but successfully download with retries", (done) => {
66 + TestFileHandler.setErrorRate(0.8);
67 + ServiceLocator.getFileReplicator().init();
68 + let errors = 0;
69 +
70 + localDb.replicate.from(dbUrl)
71 + .on('file-replicator-error', event => {
72 + errors++;
73 + })
74 + .on('complete', () => {
75 + expect(errors).toBeGreaterThanOrEqual(1);
76 + done();
77 + });
78 + });
79 +
80 +});
1 +exports.defineAutoTests = function() {
2 +
3 + console.log(bsync);
4 +
5 + describe("Cordova tests", () => {
6 + /*
7 + let downloader = new CordovaDownloader();
8 +
9 + it("should download sample image from https source and store with new name", (done) => {
10 +
11 + let source = "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e1/FullMoon2010.jpg/800px-FullMoon2010.jpg";
12 + let target = "cdvfile://full-moon.jpg";
13 + let lastProgress = 0;
14 +
15 + downloader.download(source, target)
16 + .subscribe(
17 + (progress: number) => {
18 + expect(progress).toBeGreaterThan(lastProgress);
19 + lastProgress = progress;
20 + } ,
21 + (error:any) => {} ,
22 + () => {
23 + expect(lastProgress).toEqual(1);
24 + // expect(fs.existsSync(target)).toBeTruthy();
25 + done();
26 + }
27 + );
28 +
29 + });
30 + */
31 +
32 + });
33 +
34 +
35 +};
...\ No newline at end of file ...\ No newline at end of file
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
3 + xmlns:android="http://schemas.android.com/apk/res/android"
4 + id="bsync-client-test"
5 + version="1.0.0"
6 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
7 +
8 + <name>bsync cordova test</name>
9 + <license>Apache 2.0 License</license>
10 +
11 + <js-module src="cordova-test.spec.js" name="tests">
12 + </js-module>
13 +
14 +</plugin>
...\ No newline at end of file ...\ No newline at end of file
1 +import { Observable, Subscriber } from 'rxjs';
2 +import { FileHandler } from '../../src/api/file-handler';
3 +
4 +export class TestFileHandler implements FileHandler {
5 +
6 + protected static errorRate:number = 0;
7 +
8 + static setErrorRate(rate:number) {
9 + TestFileHandler.errorRate = rate;
10 + }
11 +
12 + download(source:string, target:string) : Observable<number> {
13 + return Observable.create((subscriber:Subscriber<number>) => {
14 + let random = Math.random();
15 + let error:boolean = random < TestFileHandler.errorRate;
16 + let counter = 1;
17 +
18 + if (error) {
19 + subscriber.error("random error triggered");
20 + return;
21 + }
22 +
23 + let interval = setInterval(() => {
24 + if (counter < 4) {
25 + subscriber.next(counter * 25);
26 + } else {
27 + subscriber.complete();
28 + clearInterval(interval);
29 + }
30 +
31 + ++counter;
32 + }, 10);
33 + });
34 + }
35 +}
...\ No newline at end of file ...\ No newline at end of file
1 +export * from './test/file-handler/node-file-handler';
2 +export * from './test/file-replicator';
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "spec_dir": ".tmp",
3 + "spec_files": [
4 + "**/*[sS]pec.js"
5 + ],
6 + "helpers": [
7 + "helpers/**/*.js"
8 + ],
9 + "stopSpecOnExpectationFailure": false,
10 + "random": true
11 +}
1 +import { NodeFileHandler } from '../../../src/file-handler/node-file-handler';
2 +import * as http from 'http';
3 +import * as https from 'https';
4 +import * as fs from 'fs';
5 +
6 +describe("Node Downloader", () => {
7 +
8 + let nodeFileHandler = new NodeFileHandler();
9 +
10 + it("should retrieve right handler", () => {
11 +
12 + let httpHandler = nodeFileHandler.selectProtocol("http://someurl.com/image.jpg");
13 + expect(httpHandler).toEqual(http);
14 +
15 + let httpsHandler = nodeFileHandler.selectProtocol("https://someurl.com/image.jpg");
16 + expect(httpsHandler).toEqual(https);
17 +
18 + let nullHandler = nodeFileHandler.selectProtocol("notsupported://blub");
19 + expect(nullHandler).toBeNull();
20 +
21 + });
22 +
23 + it("should download sample image from https source and store with new name", (done) => {
24 +
25 + let source = "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e1/FullMoon2010.jpg/800px-FullMoon2010.jpg";
26 + let target = ".tmp/full-moon.jpg";
27 + let lastProgress = 0;
28 +
29 + nodeFileHandler.download(source, target)
30 + .subscribe(
31 + (progress: number) => {
32 + expect(progress).toBeGreaterThan(lastProgress);
33 + lastProgress = progress;
34 + } ,
35 + (error:any) => {} ,
36 + () => {
37 + expect(lastProgress).toEqual(1);
38 + expect(fs.existsSync(target)).toBeTruthy();
39 + done();
40 + }
41 + );
42 + });
43 +
44 + it('should not download if file with same name exists and bytesize > 0', (done) => {
45 + let source = "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e1/FullMoon2010.jpg/800px-FullMoon2010.jpg";
46 + let target = ".tmp/full-moon-sensless.jpg";
47 + let lastProgress = 0;
48 +
49 + let file = fs.createWriteStream(target, {'flags': 'a'});
50 + let dummyData = "some sensless information to fill bytes...";
51 +
52 + file.write(new Buffer(dummyData));
53 + file.end(() => {
54 +
55 + nodeFileHandler.download(source, target)
56 + .subscribe(
57 + () => { fail("progress should not be called"); } ,
58 + () => {} ,
59 + () => {
60 + expect(fs.existsSync(target)).toBeTruthy();
61 + done();
62 + }
63 + );
64 +
65 + });
66 + });
67 +
68 +});
...\ No newline at end of file ...\ No newline at end of file
1 +import { FileReplicator } from '../../src/file-replicator';
2 +
3 +
4 +describe("File Replicator", () => {
5 +
6 + let fileReplicator = new FileReplicator();
7 +
8 + let change = {
9 + docs : [
10 + { language: 'de', type : "asset", source : "http://someplace.com/icon.jpg" , target : "icon.jpg" } ,
11 + { language: 'de', type : "asset", source : "http://sampleuri.com/image.png" } ,
12 + { language: 'en', type : "asset", source : "https://secureasset.com/asset.mp3" , target : "music.mp3" }
13 + ]
14 + };
15 +
16 + beforeEach(() => {
17 + fileReplicator.init();
18 + });
19 +
20 + it("should contain several assets", () => {
21 + fileReplicator.pushChanges(change);
22 + expect(fileReplicator.files.length).toEqual(3);
23 + });
24 +
25 + it("should get correct asset names", () => {
26 + let files = fileReplicator.prepareFiles(change.docs);
27 +
28 + expect(files[0].target).toEqual("icon.jpg");
29 + expect(files[1].target).toEqual("bsync_707608502");
30 + expect(files[2].target).toEqual("music.mp3");
31 + });
32 +
33 + it("should only get items with language=de", () => {
34 + fileReplicator.itemValidator = (item:any) => {
35 + if (item && item.language === "de") {
36 + return true;
37 + }
38 + return false;
39 + };
40 +
41 + let files = fileReplicator.prepareFiles(change.docs);
42 +
43 + expect(files.length).toEqual(2);
44 + expect(files[0].target).toEqual("icon.jpg");
45 + expect(files[1].target).toEqual("bsync_707608502");
46 +
47 + fileReplicator.itemValidator = null;
48 + });
49 +
50 +});
...\ No newline at end of file ...\ No newline at end of file
1 +import { Observable } from 'rxjs/Observable';
2 +
3 +export interface FileHandler {
4 +
5 + /**
6 + * The donwload works as following:
7 + * - if the file already exists and is not empty: trigger complete
8 + * - if the download is in progress: trigger next (0-1 progress for percent of download)
9 + * - if the download enters any error condition, trigger error and an already downloaded part of the file
10 + */
11 + download(source:string, target:string) : Observable<number>;
12 +
13 +}
...\ No newline at end of file ...\ No newline at end of file
1 +export interface File {
2 + source :string;
3 + target :string;
4 +}
...\ No newline at end of file ...\ No newline at end of file
1 +import { EventEmitter } from 'events';
2 +import { ServiceLocator } from './service-locator';
3 +
4 +export * from './service-locator';
5 +
6 +export function loadBsyncPlugin (PouchDB) {
7 + let pouchReplicate = PouchDB.replicate;
8 +
9 + PouchDB.plugin((PouchDB) => {
10 + PouchDB.replicate = function() {
11 + let eventEmitter = new EventEmitter();
12 + let emitter = pouchReplicate.apply(this, arguments);
13 + let replicator = ServiceLocator.getFileReplicator();
14 + let db = arguments[1];
15 +
16 + replicator.once('final', event => {
17 + eventEmitter.emit('complete');
18 + eventEmitter.removeAllListeners();
19 + });
20 +
21 + replicator.on('error', event => {
22 + eventEmitter.emit('file-replicator-error', event);
23 + });
24 +
25 + replicator.on('complete', event => {
26 + eventEmitter.emit('file-replicator-complete', event);
27 + });
28 +
29 + replicator.on('progress', event => {
30 + eventEmitter.emit('file-replicator-progress', event);
31 + });
32 +
33 + emitter.once('change', info => {
34 + eventEmitter.emit('change', info);
35 + });
36 +
37 + emitter.once('complete', info => {
38 + db.query('index_type/type',{
39 + include_docs : true,
40 + key : replicator.itemValue
41 + }).then((res) => {
42 + let docs = { docs : [] };
43 + for (let r of res.rows) {
44 + docs.docs.push(r.doc);
45 + }
46 +
47 + replicator.pushChanges(docs);
48 + replicator.start();
49 + }).catch(error => {
50 + eventEmitter.emit('error', error);
51 + });
52 + });
53 +
54 + emitter.once('error', (error) => {
55 + eventEmitter.emit('error', error);
56 + });
57 +
58 + return eventEmitter;
59 + };
60 + });
61 +};
62 +
63 +if (typeof window !== 'undefined' && window['PouchDB']) {
64 + loadBsyncPlugin(window['PouchDB']);
65 +}
1 +export const CONFIG_ITEM_KEY = "itemKey";
2 +export const CONFIG_ITEM_VALUE = "itemValue";
3 +export const CONFIG_ITEM_SOURCE_ATTRIBUTE = "itemSourceAttribute";
4 +export const CONFIG_ITEM_TARGET_ATTRIBUTE = "itemTargetAttribute";
5 +export const CONFIG_ITEM_VALIDATOR = "itemValidator";
6 +export const CONFIG_RETRY_TIMEOUT = "retryTimeout";
7 +export const CONFIG_FILE_HANDLER = "fileHandler";
8 +
9 +export class Config {
10 +
11 + protected config:any = {};
12 +
13 + hasConfig(key:string) {
14 + if (this.config[key]) {
15 + return true;
16 + }
17 + return false;
18 + }
19 +
20 + getConfig(key:string) {
21 + return this.config[key];
22 + }
23 +
24 + setConfig(key:string, value:any) {
25 + this.config[key] = value;
26 + }
27 +
28 +}
...\ No newline at end of file ...\ No newline at end of file
1 +import { FileHandler } from '../api/file-handler';
2 +
3 +declare var Rx;
4 +
5 +export class CordovaDownloader implements FileHandler {
6 +
7 + download(source:string, target:string) : Rx.Observable<number> {
8 + return Rx.Observable.create((subscriber:Rx.Subscriber<number>) => {
9 +
10 + if (!window['FileTransfer']) {
11 + subscriber.error("Cordova FileTransfer object undefined");
12 + }
13 +
14 + let fileTransfer = new window['FileTransfer']();
15 +
16 + fileTransfer.onprogress = (progress:ProgressEvent) => {
17 + subscriber.next(progress.total / progress.loaded);
18 + };
19 +
20 + fileTransfer.download(
21 + source ,
22 + target ,
23 + (entry:any) => {
24 + subscriber.complete();
25 + } ,
26 + (error:any) => {
27 + subscriber.error(error);
28 + },
29 + true
30 + );
31 +
32 + });
33 + }
34 +
35 +}
...\ No newline at end of file ...\ No newline at end of file
1 +import { Observable, Subscriber } from 'rxjs';
2 +import { FileHandler } from '../api/file-handler';
3 +
4 +export class ElectronFileHandler implements FileHandler {
5 +
6 + constructor (private ipcRenderer:any) {
7 + }
8 +
9 + download(source:string, target:string) : Observable<number> {
10 + return Observable.create((subscriber:Subscriber<number>) => {
11 +
12 + this.ipcRenderer.once('bsync-download-complete', () => {
13 + this.ipcRenderer.removeAllListeners('bsync-download-progress');
14 + this.ipcRenderer.removeAllListeners('bsync-download-error');
15 + subscriber.complete();
16 + });
17 +
18 + this.ipcRenderer.on('bsync-download-progress', (progress:number) => {
19 + subscriber.next(progress);
20 + });
21 +
22 + this.ipcRenderer.once('bsync-download-error', (error:any) => {
23 + this.ipcRenderer.removeAllListeners('bsync-download-progress');
24 + this.ipcRenderer.removeAllListeners('bsync-download-complete');
25 + subscriber.error(error);
26 + });
27 +
28 + this.ipcRenderer.send('bsync-download', {
29 + source : source ,
30 + target : target
31 + });
32 + });
33 + }
34 +
35 +}
...\ No newline at end of file ...\ No newline at end of file
1 +import { Observable, Subscriber } from 'rxjs';
2 +import { FileHandler } from '../api/file-handler';
3 +import * as http from 'http';
4 +import * as https from 'https';
5 +import * as fs from 'fs';
6 +
7 +export class NodeFileHandler implements FileHandler {
8 +
9 + selectProtocol(url:string) : any {
10 + if (url.search(/^http:\/\//) === 0) {
11 + return http;
12 + } else if (url.search(/^https:\/\//) === 0) {
13 + return https;
14 + } else {
15 + return null;
16 + }
17 + }
18 +
19 + download(source:string, target:string) : Observable<number> {
20 +
21 + let handler = this.selectProtocol(source);
22 +
23 + return Observable.create((subscriber:Subscriber<number>) => {
24 +
25 + if (!handler) {
26 + subscriber.error("No handler for source: " + source);
27 + return;
28 + }
29 +
30 + // file already exists and is not empty
31 + if (fs.existsSync(target) && (fs.statSync(target)['size'] > 0)) {
32 + subscriber.complete();
33 + return;
34 + }
35 +
36 + let file = fs.createWriteStream(target, {'flags': 'a'});
37 +
38 + handler.get(source, (response) => {
39 + let size = response.headers['content-length']; // in bytes
40 + let prog = 0; // already downloaded
41 + let progCounts = 100; // how many progress events should be triggerd (1-100 %)
42 + let nextProg = (1/progCounts);
43 +
44 + response.on('data', (chunk) => {
45 + prog += chunk.length;
46 + file.write(chunk, 'binary');
47 +
48 + if ((prog / size) > nextProg) {
49 + subscriber.next(prog / size);
50 + nextProg += (1 / progCounts);
51 + }
52 + });
53 +
54 + response.on('end', () => {
55 + file.end();
56 + subscriber.complete();
57 + });
58 +
59 + }).on('error', (error) => {
60 + fs.unlink(target);
61 + subscriber.error("Error while downloading: " + error);
62 + });
63 +
64 + });
65 +
66 + }
67 +
68 +}
...\ No newline at end of file ...\ No newline at end of file
1 +import {FileHandler} from './api/file-handler';
2 +import {File} from './api/file';
3 +import {Util} from './util';
4 +import {EventEmitter} from 'events';
5 +
6 +export class FileReplicator extends EventEmitter {
7 +
8 + constructor() {
9 + super();
10 + }
11 +
12 + protected _files:Array<File> = [];
13 +
14 + protected _itemValidator: (item:any) => boolean = null;
15 + protected _fileHandler:FileHandler = null;
16 + protected _retryTimeout:number = 0;
17 +
18 + protected _itemKey = "type";
19 + protected _itemValue = "asset";
20 + protected _itemSourceAttribute = "source";
21 + protected _itemTargetAttribute = "target";
22 +
23 + get files(): Array<File> {
24 + return this._files;
25 + }
26 +
27 + set fileHandler (handler:FileHandler) {
28 + this._fileHandler = handler;
29 + }
30 +
31 + set retryTimeout (timeout:number) {
32 + this._retryTimeout = timeout;
33 + }
34 +
35 + set itemValidator(validator:(item:any) => boolean) {
36 + this._itemValidator = validator;
37 + }
38 +
39 + set itemKey(key:string) {
40 + this._itemKey = key;
41 + }
42 +
43 + set itemValue(value:string) {
44 + this._itemValue = value;
45 + }
46 +
47 + set itemSourceAttribute(sourceAttribute:string) {
48 + this._itemSourceAttribute = sourceAttribute;
49 + }
50 +
51 + set itemTargetAttribute(targetAttribute:string) {
52 + this._itemTargetAttribute = targetAttribute;
53 + }
54 +
55 + get itemKey() {
56 + return this._itemKey;
57 + }
58 +
59 + get itemValue() {
60 + return this._itemValue;
61 + }
62 +
63 + get itemSourceAttribute() {
64 + return this._itemSourceAttribute;
65 + }
66 +
67 + get itemTargetAttribute() {
68 + return this._itemTargetAttribute;
69 + }
70 +
71 + init(files: Array<File> = []) {
72 + this._files = files;
73 + }
74 +
75 + /**
76 + * change from pouchdb replicate
77 + */
78 + pushChanges(change:any) {
79 + let items:Array<any> = [];
80 +
81 + if (change && change.docs && change.docs.length > 0) {
82 + for (let item of change.docs) {
83 + if (item[this._itemKey] && item[this._itemKey] === this._itemValue) {
84 + items.push(item);
85 + }
86 + }
87 + }
88 +
89 + let files = this.prepareFiles(items);
90 +
91 + for (let file of files) {
92 + this._files.push(file);
93 + }
94 + }
95 +
96 + downloadFiles(files:Array<File>, fileHandler:FileHandler, index:number = 0) {
97 + if (index >= files.length) {
98 + return;
99 + }
100 +
101 + this.emit('start', { progress: 0, index : index, length : files.length });
102 +
103 + fileHandler
104 + .download(files[index].source, files[index].target)
105 + .subscribe(
106 + progress => {
107 + this.emit('progress', { progress : progress, index : index, length : files.length })
108 + } ,
109 + error => {
110 + this.emit('error', { progress : 0, index : index, length : files.length, error: error });
111 + } ,
112 + () => {
113 + this.emit('complete', { progress : 100 , index : index, length : files.length });
114 + this.downloadFiles(files, fileHandler, index+1);
115 + }
116 + );
117 + }
118 +
119 + prepareFiles(items: Array<any>) : Array<File> {
120 + let output = [];
121 +
122 + for (let item of items) {
123 + if (item[this._itemSourceAttribute] && (!this._itemValidator || this._itemValidator(item))) {
124 + let file = { source : item[this._itemSourceAttribute] , target : '' };
125 +
126 + if (item[this._itemTargetAttribute]) {
127 + file.target = item[this._itemTargetAttribute];
128 + } else {
129 + file.target = Util.getNameHash(file.source);
130 + }
131 +
132 + output.push(file);
133 + }
134 + }
135 +
136 + return output;
137 + }
138 +
139 + start() {
140 + this.on('complete', (event:any) => {
141 + if ((event.index + 1) >= event.length) {
142 + this.replicationFinalized(event.index);
143 + }
144 + });
145 +
146 + this.on('error', (event:any) => {
147 + this.replicationFinalized(event.index);
148 + });
149 +
150 + this.downloadFiles(this._files, this._fileHandler);
151 + }
152 +
153 + replicationFinalized(lastIndex:number) {
154 + if (lastIndex+1 >= this._files.length) { // all finished
155 + this._files = [];
156 + this.emit('final');
157 + } else if (this._retryTimeout > 0) { // restart after last success
158 + this._files.splice(0,lastIndex);
159 + setTimeout(() => {
160 + this.downloadFiles(this._files, this._fileHandler);
161 + }, this._retryTimeout);
162 + }
163 + }
164 +
165 +}
...\ No newline at end of file ...\ No newline at end of file
1 +import { NodeFileHandler } from './file-handler/node-file-handler';
2 +
3 +export default class Bsync {
4 +
5 + static configIpcMain(ipcMain: any, downloadDir:string) {
6 + let nodeFileHander = new NodeFileHandler();
7 +
8 + ipcMain.on('bsync-download', (event, args) => {
9 + nodeFileHander.download(args.source, downloadDir + args.target)
10 + .subscribe(
11 + (progress:number) => { event.sender.send('bsync-download-progress', progress); } ,
12 + (error:any) => { event.sender.send('bsync-download-error', error); } ,
13 + () => { event.sender.send('bsync-download-complete'); }
14 + );
15 + });
16 + }
17 +
18 +}
...\ No newline at end of file ...\ No newline at end of file
1 +import {FileHandler} from './api/file-handler';
2 +import {ElectronFileHandler} from './file-handler/electron-file-handler';
3 +import {FileReplicator} from './file-replicator';
4 +import {
5 + Config,
6 + CONFIG_RETRY_TIMEOUT,
7 + CONFIG_ITEM_KEY,
8 + CONFIG_ITEM_VALUE,
9 + CONFIG_ITEM_TARGET_ATTRIBUTE,
10 + CONFIG_ITEM_SOURCE_ATTRIBUTE
11 +} from './config';
12 +
13 +export const ENV_ELECTRON = "electron";
14 +export const ENV_CORDOVA = "cordova";
15 +export const ENV_UNKNOWN = "unknown";
16 +
17 +export class ServiceLocator {
18 +
19 + protected static fileHandlers:any = {};
20 + protected static fileReplicator: FileReplicator;
21 + protected static config: Config;
22 +
23 + static addFileHandler(environment:string, fileHandler:FileHandler) {
24 + ServiceLocator.fileHandlers[environment] = fileHandler;
25 + }
26 +
27 + static getConfig() : Config {
28 + if (!ServiceLocator.config) {
29 + ServiceLocator.config = new Config();
30 + }
31 +
32 + return ServiceLocator.config;
33 + }
34 +
35 + static getEnvironment() {
36 + if (typeof window['require'] === 'function' && window['require']('electron')) {
37 + return ENV_ELECTRON;
38 + }
39 + if (typeof window['FileTransfer'] === 'function') {
40 + return ENV_CORDOVA;
41 + }
42 +
43 + return ENV_UNKNOWN;
44 + }
45 +
46 + static getFileHandler() : FileHandler {
47 + let environment = ServiceLocator.getEnvironment();
48 +
49 + if (ServiceLocator.fileHandlers[environment]) {
50 + return ServiceLocator.fileHandlers[environment];
51 + }
52 +
53 + if (environment === ENV_ELECTRON) {
54 + return new ElectronFileHandler(window['require']('electron').ipcRenderer);
55 + }
56 +
57 + return null;
58 + }
59 +
60 + static getFileReplicator() : FileReplicator {
61 + if (!ServiceLocator.fileReplicator) {
62 +
63 + ServiceLocator.fileReplicator = new FileReplicator();
64 +
65 + ServiceLocator.fileReplicator.fileHandler = ServiceLocator.getFileHandler();
66 +
67 + if (ServiceLocator.getConfig().hasConfig(CONFIG_RETRY_TIMEOUT)) {
68 + ServiceLocator.fileReplicator.retryTimeout = ServiceLocator.getConfig().getConfig(CONFIG_RETRY_TIMEOUT);
69 + }
70 + if (ServiceLocator.getConfig().hasConfig(CONFIG_ITEM_KEY)) {
71 + ServiceLocator.fileReplicator.itemKey = ServiceLocator.getConfig().getConfig(CONFIG_ITEM_KEY);
72 + }
73 + if (ServiceLocator.getConfig().hasConfig(CONFIG_ITEM_VALUE)) {
74 + ServiceLocator.fileReplicator.itemValue = ServiceLocator.getConfig().getConfig(CONFIG_ITEM_VALUE);
75 + }
76 + if (ServiceLocator.getConfig().hasConfig(CONFIG_ITEM_SOURCE_ATTRIBUTE)) {
77 + ServiceLocator.fileReplicator.itemSourceAttribute = ServiceLocator.getConfig().getConfig(CONFIG_ITEM_SOURCE_ATTRIBUTE);
78 + }
79 + if (ServiceLocator.getConfig().hasConfig(CONFIG_ITEM_TARGET_ATTRIBUTE)) {
80 + ServiceLocator.fileReplicator.itemTargetAttribute = ServiceLocator.getConfig().getConfig(CONFIG_ITEM_TARGET_ATTRIBUTE);
81 + }
82 + }
83 +
84 + return ServiceLocator.fileReplicator;
85 + }
86 +
87 +}
...\ No newline at end of file ...\ No newline at end of file
1 +export class Util {
2 +
3 + static getNameHash(path:string) {
4 + for(var r=0,i=0;i<path.length;i++) {
5 + r=(r<<5)-r+path.charCodeAt(i),r&=r;
6 + }
7 + return "bsync_" + Math.abs(r);
8 + }
9 +
10 +}
...\ No newline at end of file ...\ No newline at end of file