UNPKG

67.6 kBJavaScriptView Raw
1/*!
2 metadata-pouchdb v2.0.22-beta.1, built:2020-01-19
3 © 2014-2019 Evgeniy Malyarov and the Oknosoft team http://www.oknosoft.ru
4 metadata.js may be freely distributed under the MIT
5 To obtain commercial license and technical support, contact info@oknosoft.ru
6 */
7
8
9'use strict';
10
11function sort_fn(a, b) {
12 if (a.date < b.date){
13 return -1;
14 }
15 else if (a.date > b.date){
16 return 1;
17 }
18 else{
19 return 0;
20 }
21}
22class RamIndexer {
23 static waitError() {
24 const err = new Error('Индекс прочитн не полностью, повторите запрос позже');
25 err.status = 403;
26 throw err;
27 }
28 static truth(fld, cond) {
29 const blank = '00000000-0000-0000-0000-000000000000';
30 if(cond === true || (cond && cond.hasOwnProperty('$ne') && !cond.$ne)) {
31 return function (doc) {
32 return doc[fld];
33 };
34 }
35 else if(cond === false || (cond && cond.hasOwnProperty('$ne') && cond.$ne && typeof cond.$ne === 'boolean')) {
36 return function (doc) {
37 return !doc[fld];
38 };
39 }
40 else if(cond && cond.hasOwnProperty('filled')) {
41 return function (doc) {
42 return doc[fld] && doc[fld] !== blank;
43 };
44 }
45 else if(cond && cond.hasOwnProperty('nfilled')) {
46 return function (doc) {
47 return !doc[fld] || doc[fld] === blank;
48 };
49 }
50 else if(cond && cond.hasOwnProperty('$ne')) {
51 return function (doc) {
52 return doc[fld] !== cond.$ne;
53 };
54 }
55 else if(cond && cond.hasOwnProperty('$in')) {
56 const acond = typeof cond.$in === 'string' ? cond.$in.split(',').map((v) => v.trim()) : cond.$in;
57 return function (doc) {
58 return acond.includes(doc[fld]);
59 };
60 }
61 else if(cond && cond.hasOwnProperty('$nin')) {
62 const acond = typeof cond.$nin === 'string' ? cond.$nin.split(',').map((v) => v.trim()) : cond.$nin;
63 return function (doc) {
64 return !acond.includes(doc[fld]);
65 };
66 }
67 else {
68 return function (doc) {
69 return doc[fld] === cond;
70 };
71 }
72 }
73 constructor({fields, search_fields, mgr}) {
74 this._fields = fields;
75 this._search_fields = search_fields;
76 this._mgrs = Array.isArray(mgr) ? mgr : [mgr];
77 this._count = 0;
78 this._ready = false;
79 this._listeners = new Map();
80 this._area = this._mgrs.length > 1;
81 this.by_date = {};
82 }
83 sort() {
84 for(const date in this.by_date) {
85 this.by_date[date].sort(sort_fn);
86 }
87 this._ready = true;
88 }
89 put(indoc, force) {
90 const doc = {};
91 if(this._area) {
92 doc._area = indoc._area;
93 }
94 this._fields.forEach((fld) => {
95 if(indoc.hasOwnProperty(fld)) {
96 doc[fld] = indoc[fld];
97 }
98 });
99 const date = doc.date.substr(0, 7);
100 const arr = this.by_date[date];
101 if(arr) {
102 if(force || !arr.some((row) => {
103 if(row._id === doc._id) {
104 Object.assign(row, doc);
105 return true;
106 }
107 })) {
108 arr.push(doc);
109 !force && arr.sort(sort_fn);
110 }
111 }
112 else {
113 this.by_date[date] = [doc];
114 }
115 }
116 get_range(from, till, step, desc) {
117 if(!from || !till) {
118 return [];
119 }
120 if(desc) {
121 if(step) {
122 let [year, month] = till.split('-');
123 month = parseInt(month, 10) - step;
124 while (month < 1) {
125 year = parseInt(year, 10) - 1;
126 month += 12;
127 }
128 till = `${year}-${month.pad(2)}`;
129 }
130 if(till < from) {
131 return null;
132 }
133 let res = this.by_date[till];
134 if(!res) {
135 res = [];
136 }
137 return res;
138 }
139 else {
140 if(step) {
141 let [year, month] = from.split('-');
142 month = parseInt(month, 10) + step;
143 while (month > 12) {
144 year = parseInt(year, 10) + 1;
145 month -= 12;
146 }
147 from = `${year}-${month.pad(2)}`;
148 }
149 if(from > till) {
150 return null;
151 }
152 let res = this.by_date[from];
153 if(!res) {
154 res = [];
155 }
156 return res;
157 }
158 }
159 find({selector, sort, ref, limit, skip = 0}, auth) {
160 if(!this._ready) {
161 RamIndexer.waitError();
162 }
163 let dfrom, dtill, from, till, search;
164 for(const row of selector.$and) {
165 const fld = Object.keys(row)[0];
166 const cond = Object.keys(row[fld])[0];
167 if(fld === 'date') {
168 if(cond === '$lt' || cond === '$lte') {
169 dtill = row[fld][cond];
170 till = dtill.substr(0,7);
171 }
172 else if(cond === '$gt' || cond === '$gte') {
173 dfrom = row[fld][cond];
174 from = dfrom.substr(0,7);
175 }
176 }
177 else if(fld === 'search') {
178 search = row[fld][cond] ? row[fld][cond].toLowerCase().split(' ') : [];
179 }
180 }
181 if(sort && sort.length && sort[0][Object.keys(sort[0])[0]] === 'desc' || sort === 'desc') {
182 sort = 'desc';
183 }
184 else {
185 sort = 'asc';
186 }
187 const {_search_fields} = this;
188 const {utils} = $p;
189 let part,
190 step = 0,
191 flag = skip === 0 && utils.is_guid(ref),
192 scroll = null,
193 count = 0;
194 const docs = [];
195 function add(doc) {
196 count++;
197 if(flag && doc._id.endsWith(ref)) {
198 scroll = count - 1;
199 flag = false;
200 }
201 if(skip > 0) {
202 return skip--;
203 }
204 if(limit > 0) {
205 limit--;
206 docs.push(doc);
207 }
208 }
209 function check(doc) {
210 if(doc.date < dfrom || doc.date > dtill) {
211 return;
212 }
213 let ok = true;
214 for(const word of search) {
215 if(!word) {
216 continue;
217 }
218 if(!_search_fields.some((fld) => {
219 const val = doc[fld];
220 return val && typeof val === 'string' && val.toLowerCase().includes(word);
221 })){
222 ok = false;
223 break;
224 }
225 }
226 ok && add(doc);
227 }
228 while((part = this.get_range(from, till, step, sort === 'desc'))) {
229 step += 1;
230 if(sort === 'desc') {
231 for(let i = part.length - 1; i >= 0; i--){
232 check(part[i]);
233 }
234 }
235 else {
236 for(let i = 0; i < part.length; i++){
237 check(part[i]);
238 }
239 }
240 }
241 return {docs, scroll, flag, count};
242 }
243 init(bookmark, _mgr) {
244 if(!_mgr) {
245 return this._mgrs.reduce((sum, _mgr) => sum.then(() => this.init(bookmark, _mgr)), Promise.resolve())
246 .then(() => {
247 this.sort();
248 });
249 }
250 if(!bookmark) {
251 const listener = (change) => {
252 if(!change) {
253 return;
254 }
255 if(this._area) {
256 change._area = _mgr.cachable;
257 }
258 this.put(change);
259 };
260 this._listeners.set(_mgr, listener);
261 _mgr.on('change', listener);
262 }
263 return _mgr.pouch_db.find({
264 selector: {
265 class_name: _mgr.class_name,
266 },
267 fields: this._fields,
268 bookmark,
269 limit: 10000,
270 })
271 .then(({bookmark, docs}) => {
272 this._count += docs.length;
273 for(const doc of docs) {
274 if(this._area) {
275 doc._area = _mgr.cachable;
276 }
277 this.put(doc, true);
278 }
279 _mgr.adapter.emit('indexer_page', {indexer: this, bookmark: bookmark || '', _mgr});
280 return docs.length === 10000 && this.init(bookmark, _mgr);
281 });
282 }
283 reset(mgrs) {
284 for(const date in this.by_date) {
285 this.by_date[date].length = 0;
286 }
287 for(const [_mgr, listener] of this._listeners) {
288 _mgr.off('change', listener);
289 }
290 this._listeners.clear();
291 this._mgrs.length = 0;
292 mgrs && this._mgrs.push.apply(this._mgrs, mgrs);
293 this._area = this._mgrs.length > 1;
294 }
295}
296
297var proto = ({classes}) => {
298 const {DataManager, DataObj, DocObj, TaskObj, BusinessProcessObj} = classes;
299 classes.RamIndexer = RamIndexer;
300 DataObj.prototype.new_number_doc = function new_number_doc(prefix) {
301 if (!this._metadata().code_length) {
302 return Promise.resolve(this);
303 }
304 const {organization, _manager} = this;
305 const {current_user, utils} = _manager._owner.$p;
306 if(this.date === utils.blank.date) {
307 this.date = new Date();
308 }
309 const year = (this.date instanceof Date) ? this.date.getFullYear() : 0;
310 if (!prefix) {
311 prefix = ((current_user && current_user.prefix) || '') + ((organization && organization.prefix) || '');
312 }
313 let part = '',
314 code_length = this._metadata().code_length - prefix.length;
315 if (_manager.cachable == 'ram' || _manager.cachable == 'doc_ram') {
316 return Promise.resolve(this.new_cat_id(prefix));
317 }
318 return _manager.pouch_db.query('doc/number_doc',
319 {
320 limit: 1,
321 include_docs: false,
322 startkey: [_manager.class_name, year, prefix + '\ufff0'],
323 endkey: [_manager.class_name, year, prefix],
324 descending: true,
325 })
326 .then((res) => {
327 if(res.rows.length) {
328 const num0 = res.rows[0].key[2];
329 for (let i = num0.length - 1; i >= prefix.length; i--) {
330 if(isNaN(parseInt(num0[i]))) {
331 break;
332 }
333 part = num0[i] + part;
334 }
335 part = (parseInt(part || 0) + 1).toFixed(0);
336 }
337 else {
338 part = '1';
339 }
340 while (part.length < code_length) {
341 part = '0' + part;
342 }
343 if (this instanceof DocObj || this instanceof TaskObj || this instanceof BusinessProcessObj){
344 this.number_doc = prefix + part;
345 }
346 else{
347 this.id = prefix + part;
348 }
349 return this;
350 });
351 };
352 DataObj.prototype.new_cat_id = function new_cat_id(prefix) {
353 const {organization, _manager} = this;
354 const {current_user, wsql} = _manager._owner.$p;
355 if (!prefix)
356 prefix = ((current_user && current_user.prefix) || '') +
357 (organization && organization.prefix ? organization.prefix : (wsql.get_user_param('zone') + '-'));
358 let code_length = this._metadata().code_length - prefix.length,
359 field = (this instanceof DocObj || this instanceof TaskObj || this instanceof BusinessProcessObj) ? 'number_doc' : 'id',
360 part = '',
361 res = wsql.alasql('select top 1 ' + field + ' as id from ? where ' + field + ' like "' + prefix + '%" order by ' + field + ' desc', [_manager.alatable]);
362 if (res.length) {
363 const num0 = res[0].id || '';
364 for (let i = num0.length - 1; i > 0; i--) {
365 if (isNaN(parseInt(num0[i])))
366 break;
367 part = num0[i] + part;
368 }
369 part = (parseInt(part || 0) + 1).toFixed(0);
370 } else {
371 part = '1';
372 }
373 while (part.length < code_length){
374 part = '0' + part;
375 }
376 this[field] = prefix + part;
377 return this;
378 };
379 Object.defineProperties(DataManager.prototype, {
380 pouch_db: {
381 get () {
382 const cachable = this.cachable.replace('_ram', '').replace('_doc', '');
383 const {adapter} = this;
384 if(cachable.indexOf('remote') != -1) {
385 return adapter.remote[cachable.replace('_remote', '')];
386 }
387 else {
388 return adapter.local[cachable] || adapter.remote[cachable];
389 }
390 }
391 },
392 });
393};
394
395let PouchDB;
396if(typeof process !== 'undefined' && process.versions && process.versions.node) {
397 PouchDB = require('pouchdb-core')
398 .plugin(require('pouchdb-adapter-http'))
399 .plugin(require('pouchdb-replication'))
400 .plugin(require('pouchdb-mapreduce'))
401 .plugin(require('pouchdb-find'))
402 .plugin(require('pouchdb-adapter-memory'));
403}
404else if(typeof window !== 'undefined' && window.PouchDB) {
405 PouchDB = window.PouchDB;
406}
407else {
408 PouchDB = require('pouchdb-core').default
409 .plugin(require('pouchdb-adapter-http').default)
410 .plugin(require('pouchdb-replication').default)
411 .plugin(require('pouchdb-mapreduce').default)
412 .plugin(require('pouchdb-find').default)
413 .plugin(require('pouchdb-adapter-idb').default);
414 if(typeof window !== 'undefined') {
415 window.PouchDB = PouchDB;
416 }
417}
418var PouchDB$1 = PouchDB;
419
420function adapter({AbstracrAdapter}) {
421 const fieldsToDelete = '_id,search,timestamp'.split(',');
422 return class AdapterPouch extends AbstracrAdapter {
423 constructor($p) {
424 super($p);
425 this.props = {
426 _data_loaded: false,
427 _doc_ram_loading: false,
428 _doc_ram_loaded: false,
429 _auth: null,
430 _suffix: '',
431 _user: '',
432 _push_only: false,
433 branch: null,
434 };
435 this.local = {_loading: false, sync: {}};
436 this.remote = {};
437 this.fetch = this.fetch.bind(this);
438 }
439 init(wsql, job_prm) {
440 const {props, local, remote, fetch, $p: {md}} = this;
441 Object.assign(props, {
442 path: wsql.get_user_param('couch_path', 'string') || job_prm.couch_path || '',
443 zone: wsql.get_user_param('zone', 'number'),
444 prefix: job_prm.local_storage_prefix,
445 direct: wsql.get_user_param('zone', 'number') == job_prm.zone_demo ? false :
446 (job_prm.hasOwnProperty('couch_direct') ? job_prm.couch_direct : wsql.get_user_param('couch_direct', 'boolean')),
447 user_node: job_prm.user_node,
448 noreplicate: job_prm.noreplicate,
449 autologin: job_prm.autologin || [],
450 });
451 if(props.path && props.path.indexOf('http') != 0 && typeof location != 'undefined') {
452 props.path = `${location.protocol}//${location.host}${props.path}`;
453 }
454 if(job_prm.use_meta === false) {
455 props.use_meta = false;
456 }
457 if(job_prm.use_ram === false) {
458 props.use_ram = false;
459 }
460 if(props.user_node && props.user_node.suffix) {
461 props._suffix = props.user_node.suffix;
462 }
463 const opts = {auto_compaction: true, revs_limit: 3, owner: this, fetch};
464 const bases = md.bases();
465 if(props.use_meta !== false) {
466 local.meta = new PouchDB$1(props.prefix + 'meta', opts);
467 if(props.path) {
468 remote.meta = new PouchDB$1(props.path + 'meta', {skip_setup: true, owner: this, fetch});
469 setTimeout(() => this.run_sync('meta'));
470 }
471 }
472 const pbases = ['doc', 'user'];
473 if(props.use_ram !== false) {
474 pbases.push('ram');
475 }
476 for (const name of pbases) {
477 if(bases.indexOf(name) != -1) {
478 Object.defineProperty(local, name, {
479 get() {
480 const dynamic_doc = wsql.get_user_param('dynamic_doc');
481 if(dynamic_doc && name === 'doc' && props.direct) {
482 return remote[dynamic_doc];
483 }
484 else {
485 return local[`__${name}`] || remote[name];
486 }
487 }
488 });
489 if(job_prm.couch_memory && job_prm.couch_memory.includes(name)) {
490 local[`__${name}`] = new PouchDB$1(props.prefix + props.zone + '_' + name, Object.assign({adapter: 'memory'}, opts));
491 }
492 else if(props.user_node || (props.direct && name != 'ram' && name != 'user')) {
493 local[`__${name}`] = null;
494 }
495 else {
496 local[`__${name}`] = new PouchDB$1(props.prefix + props.zone + '_' + name, opts);
497 }
498 }
499 }
500 this.after_init( props.user_node ? bases : (props.autologin.length ? props.autologin : ['ram']));
501 }
502 after_init(bases, auth) {
503 const {props, remote, fetch, $p: {md, wsql}} = this;
504 const opts = {skip_setup: true, adapter: 'http', owner: this, fetch};
505 if(auth) {
506 opts.auth = auth;
507 }
508 else if(props.user_node) {
509 opts.auth = props.user_node;
510 }
511 (bases || md.bases()).forEach((name) => {
512 if((!auth && remote[name]) ||
513 name.match(/(e1cib|github|user)/) ||
514 (name === 'ram' && props.use_ram === false) ||
515 (name === 'pgsql' && wsql.alasql.utils.isNode)) {
516 return;
517 }
518 remote[name] = new PouchDB$1(this.dbpath(name), opts);
519 });
520 }
521 after_log_in() {
522 const {props, local, remote, $p: {md, wsql}} = this;
523 const run_sync = [];
524 md.bases().forEach((dbid) => {
525 if(dbid !== 'meta' &&
526 local[dbid] && remote[dbid] && local[dbid] != remote[dbid] &&
527 (dbid !== 'doc' || !wsql.get_user_param('dynamic_doc'))
528 ) {
529 if(props.noreplicate && props.noreplicate.includes(dbid)) {
530 return;
531 }
532 run_sync.push(this.run_sync(dbid));
533 }
534 });
535 return Promise.all(run_sync)
536 .then(() => {
537 if(props.use_ram === false) ;
538 else if(local._loading) {
539 return new Promise((resolve) => {
540 this.once('pouch_data_loaded', resolve);
541 });
542 }
543 else if(!props.user_node) {
544 return this.call_data_loaded();
545 }
546 });
547 }
548 log_in(username, password) {
549 const {props, remote, $p} = this;
550 const {job_prm, wsql, aes, md} = $p;
551 if(username == undefined && password == undefined) {
552 if(job_prm.guests && job_prm.guests.length) {
553 username = job_prm.guests[0].username;
554 password = aes.Ctr.decrypt(job_prm.guests[0].password);
555 }
556 else {
557 const err = new Error('empty login or password');
558 this.emit('user_log_fault', err);
559 return Promise.reject(err);
560 }
561 }
562 else if(!username || !password){
563 const err = new Error('empty login or password');
564 this.emit('user_log_fault', err);
565 return Promise.reject(err);
566 }
567 if(props._auth) {
568 if(props._auth.username == username) {
569 return Promise.resolve();
570 }
571 else {
572 const err = new Error('need logout first');
573 this.emit('user_log_fault', err);
574 return Promise.reject(err);
575 }
576 }
577 const bases = md.bases();
578 let try_auth = (props.user_node || !remote.ram) ?
579 Promise.resolve(true) :
580 remote.ram.login(username, password)
581 .then((user) => {
582 if(user.ref && typeof user.roles === 'string') {
583 if(user.zones && user.zones.length && !user.zones.includes(props.zone)) {
584 props.zone = user.zones[0];
585 wsql.set_user_param('zone', props.zone);
586 }
587 this.emit('authenticated', user);
588 props._suffix = user.suffix || '';
589 props._user = user.ref;
590 props._push_only = Boolean(user.push_only);
591 if(user.direct && !props.direct && props.zone != job_prm.zone_demo) {
592 props.direct = true;
593 wsql.set_user_param('couch_direct', true);
594 }
595 if(user.su) {
596 username = user.su;
597 }
598 }
599 else {
600 const {roles} = user;
601 const suffix = /^suffix:/;
602 const ref = /^ref:/;
603 roles.forEach((role) => {
604 if(suffix.test(role)) {
605 props._suffix = role.substr(7);
606 }
607 else if(ref.test(role)) {
608 props._user = role.substr(4);
609 }
610 else if(role === 'direct' && !props.direct && props.zone != job_prm.zone_demo) {
611 props.direct = true;
612 wsql.set_user_param('couch_direct', true);
613 }
614 else if(role === 'push_only' && !props._push_only) {
615 props._push_only = true;
616 }
617 });
618 }
619 if(props._push_only && props.direct) {
620 props.direct = false;
621 wsql.set_user_param('couch_direct', false);
622 }
623 if(props._suffix) {
624 while (props._suffix.length < 4) {
625 props._suffix = '0' + props._suffix;
626 }
627 }
628 return true;
629 })
630 .catch((err) => {
631 if(props.direct) {
632 throw err;
633 }
634 return new Promise((resolve, reject) => {
635 let count = 0;
636 function props_by_user() {
637 setTimeout(() => {
638 const {current_user} = $p;
639 if(current_user) {
640 if(current_user.push_only) {
641 props._push_only = true;
642 }
643 if(current_user.suffix) {
644 props._suffix = current_user.suffix;
645 while (props._suffix.length < 4) {
646 props._suffix = '0' + props._suffix;
647 }
648 }
649 resolve();
650 }
651 else {
652 if(count > 4) {
653 return reject();
654 }
655 count++;
656 props_by_user();
657 }
658 }, 100 + count * 500);
659 }
660 props_by_user();
661 });
662 });
663 if(!props.user_node) {
664 try_auth = try_auth
665 .then((ram_logged_in) => {
666 ram_logged_in && this.after_init(bases, {username, password});
667 return ram_logged_in;
668 })
669 .then((ram_logged_in) => {
670 let postlogin = Promise.resolve(ram_logged_in);
671 if(!props.user_node) {
672 bases.forEach((dbid) => {
673 if(dbid !== 'meta' && dbid !== 'ram' && remote[dbid]) {
674 postlogin = postlogin
675 .then((ram_logged_in) => ram_logged_in && remote[dbid].info());
676 }
677 });
678 }
679 return postlogin;
680 });
681 }
682 return try_auth.then((info) => {
683 props._auth = {username};
684 if(wsql.get_user_param('user_name') != username) {
685 wsql.set_user_param('user_name', username);
686 }
687 if(info) {
688 if(wsql.get_user_param('enable_save_pwd')) {
689 if(aes.Ctr.decrypt(wsql.get_user_param('user_pwd')) != password) {
690 wsql.set_user_param('user_pwd', aes.Ctr.encrypt(password));
691 }
692 }
693 else if(wsql.get_user_param('user_pwd') != '') {
694 wsql.set_user_param('user_pwd', '');
695 }
696 this.emit('user_log_in', username);
697 }
698 else {
699 this.emit('user_log_stop', username);
700 }
701 return this.emit_promise('on_log_in').then(() => info);
702 })
703 .then((info) => {
704 if(props._data_loaded && !props._doc_ram_loading) {
705 if(props._doc_ram_loaded) {
706 this.emit('pouch_doc_ram_loaded');
707 }
708 else {
709 this.load_doc_ram();
710 }
711 }
712 return info && this.after_log_in();
713 })
714 .catch(err => {
715 this.emit('user_log_fault', err);
716 });
717 }
718 log_out() {
719 const {props, local, remote, fetch, authorized, $p: {md}} = this;
720 if(authorized) {
721 for (const name in local.sync) {
722 if(name != 'meta' && props.autologin.indexOf(name) === -1) {
723 try {
724 local.sync[name].removeAllListeners();
725 local.sync[name].cancel();
726 local.sync[name] = null;
727 }
728 catch (err) {
729 }
730 }
731 }
732 props._auth = null;
733 }
734 return Promise.all(md.bases().map((name) => {
735 if(name != 'meta' && remote[name]) {
736 let res = remote[name].logout && remote[name].logout();
737 if(name != 'ram') {
738 const dbpath = AdapterPouch.prototype.dbpath.call(this, name);
739 if(remote[name].name !== dbpath) {
740 const sub = remote[name].close()
741 .then(() => {
742 remote[name].removeAllListeners();
743 if(props.autologin.indexOf(name) === -1) {
744 remote[name] = null;
745 }
746 else {
747 remote[name] = new PouchDB$1(dbpath, {skip_setup: true, adapter: 'http', owner: this, fetch});
748 }
749 });
750 res = res ? res.then(() => sub) : sub;
751 }
752 }
753 return res;
754 }
755 }))
756 .then(() => {
757 props._user = '';
758 this.emit('user_log_out');
759 });
760 }
761 auth_prefix() {
762 switch (this.props._auth_provider) {
763 case 'google':
764 return 'Google ';
765 case 'ldap':
766 return 'LDAP ';
767 case 'github':
768 return 'Github ';
769 case 'vkontakte':
770 return 'Vkontakte ';
771 case 'facebook':
772 return 'Facebook ';
773 default:
774 return 'Basic ';
775 }
776 }
777 load_data(db) {
778 const {local, $p: {job_prm}} = this;
779 const options = {
780 limit: 700,
781 include_docs: true,
782 };
783 const _page = {
784 total_rows: 0,
785 limit: options.limit,
786 page: 0,
787 start: Date.now(),
788 };
789 if(job_prm.second_instance) {
790 return Promise.reject(new Error('second_instance'));
791 }
792 if(!db) {
793 db = local.ram;
794 }
795 return new Promise((resolve, reject) => {
796 let index;
797 const processPage = (err, response) => {
798 if(response) {
799 _page.page++;
800 _page.total_rows = response.total_rows;
801 this.emit('pouch_data_page', Object.assign({}, _page));
802 if(this.load_changes(response, options)) {
803 fetchNextPage();
804 }
805 else {
806 local._loading = false;
807 this.call_data_loaded(_page);
808 resolve();
809 }
810 }
811 else if(err) {
812 reject(err);
813 this.emit('pouch_data_error', 'ram', err);
814 }
815 };
816 const fetchNextPage = () => {
817 if(index){
818 db.query('server/load_order', options, processPage);
819 }
820 else {
821 db.allDocs(options, processPage);
822 }
823 };
824 db.get('_design/server')
825 .catch((err) => {
826 if(err.status === 404) {
827 return {views: {}}
828 }
829 else {
830 reject(err);
831 }
832 })
833 .then((design) => {
834 if(design) {
835 const {views} = design;
836 if(views.load_order){
837 index = true;
838 }
839 return (Object.keys(views).length ? this.rebuild_indexes('ram') : Promise.resolve())
840 .then(() => db.info());
841 }
842 })
843 .then((info) => {
844 if(info) {
845 if(info.doc_count >= (job_prm.pouch_ram_doc_count || 10)) {
846 this.emit('pouch_load_start', Object.assign(_page, {local_rows: info.doc_count}));
847 local._loading = true;
848 fetchNextPage();
849 }
850 else {
851 this.emit('pouch_no_data', info);
852 resolve();
853 }
854 }
855 });
856 });
857 }
858 dbpath(name) {
859 const {props: {path, zone, _suffix}, $p: {wsql, job_prm}} = this;
860 if(name == 'meta') {
861 return path + 'meta';
862 }
863 else if(name == 'ram') {
864 return path + zone + '_ram';
865 }
866 else if(name === 'pgsql') {
867 return (job_prm.pg_path.startsWith('/') && !wsql.alasql.utils.isNode ? location.origin + job_prm.pg_path : job_prm.pg_path) + zone;
868 }
869 else {
870 return path + zone + '_' + name + (_suffix ? '_' + _suffix : '');
871 }
872 }
873 db(_mgr) {
874 const dbid = _mgr.cachable.replace('_remote', '').replace('_ram', '').replace('_doc', '');
875 const {props, local, remote} = this;
876 if(dbid.indexOf('remote') != -1 || dbid === 'pgsql' || (props.noreplicate && props.noreplicate.includes(dbid))) {
877 return remote[dbid.replace('_remote', '')];
878 }
879 else {
880 return local[dbid] || remote[dbid] || local.user;
881 }
882 }
883 back_off (delay) {
884 if (!delay) {
885 return 1000 + Math.floor(Math.random() * 2000);
886 }
887 else if (delay >= 200000) {
888 return 200000;
889 }
890 return delay * 3;
891 }
892 rerun_sync(id, options, sync_events) {
893 const {local, remote} = this;
894 const sync = local.sync[id];
895 const rerun = () => {
896 if(sync) {
897 sync.cancel();
898 sync.removeAllListeners();
899 }
900 setTimeout(() => {
901 local.sync[id] = sync_events(local[id].replicate.from(remote[id], options));
902 this.rerun_sync(id, options, sync_events);
903 }, 300000 + Math.floor(Math.random() * 60000));
904 };
905 if(sync) {
906 sync.listeners('complete').forEach((fn) => {
907 if(fn.toString().includes('(info)')) {
908 sync.removeListener('complete', fn);
909 }
910 });
911 sync.on('complete', rerun);
912 sync.on('error', rerun);
913 }
914 else {
915 rerun();
916 }
917 }
918 run_sync(id) {
919 const {local, remote, $p: {wsql, job_prm, record_log}, props} = this;
920 if(local.sync[id]) {
921 return Promise.resolve(id);
922 }
923 const {_push_only, _user} = props;
924 const db_local = local[id];
925 const db_remote = remote[id];
926 let linfo, _page;
927 return db_local.info()
928 .then((info) => {
929 linfo = info;
930 return db_remote.info();
931 })
932 .then((rinfo) => {
933 if(id == 'ram') {
934 return db_remote.get('data_version')
935 .then((v) => {
936 if(v.version != wsql.get_user_param('couch_ram_data_version')) {
937 if(wsql.get_user_param('couch_ram_data_version')) {
938 rinfo = this.reset_local_data();
939 }
940 wsql.set_user_param('couch_ram_data_version', v.version);
941 }
942 return rinfo;
943 })
944 .catch(record_log)
945 .then(() => rinfo);
946 }
947 return rinfo;
948 })
949 .then((rinfo) => {
950 if(!rinfo) {
951 return;
952 }
953 if(!_push_only && rinfo.data_size > (job_prm.data_size_sync_limit || 2e8)) {
954 this.emit('pouch_sync_error', id, {data_size: rinfo.data_size});
955 props.direct = true;
956 wsql.set_user_param('couch_direct', true);
957 return;
958 }
959 if(id == 'ram' && linfo.doc_count < (job_prm.pouch_ram_doc_count || 10)) {
960 _page = {
961 total_rows: rinfo.doc_count,
962 local_rows: linfo.doc_count,
963 docs_written: 0,
964 limit: 300,
965 page: 0,
966 start: Date.now(),
967 };
968 this.emit('pouch_load_start', _page);
969 }
970 return new Promise((resolve) => {
971 const options = {
972 batch_size: 200,
973 batches_limit: 3,
974 heartbeat: 20000,
975 retry: true,
976 };
977 if(job_prm.pouch_filter && job_prm.pouch_filter[id]) {
978 options.filter = job_prm.pouch_filter[id];
979 }
980 else if(id == 'meta') {
981 options.filter = 'auth/meta';
982 }
983 const final_sync = (options) => {
984 options.back_off_function = this.back_off;
985 if(id == 'ram' || id == 'meta' || props.zone == job_prm.zone_demo) {
986 options.live = !job_prm.crazy_ram;
987 if(options.live) {
988 local.sync[id] = sync_events(db_local.replicate.from(db_remote, options));
989 }
990 else {
991 this.rerun_sync(id, options, sync_events);
992 }
993 }
994 else if(_push_only) {
995 options.live = true;
996 if(options.filter) {
997 delete options.filter;
998 delete options.query_params;
999 }
1000 local.sync[id] = sync_events(db_local.replicate.to(db_remote, Object.assign({}, options, {batch_size: 50})));
1001 }
1002 else {
1003 options.live = true;
1004 local.sync[id] = sync_events(db_local.sync(db_remote, options));
1005 }
1006 };
1007 const sync_events = (sync, options) => {
1008 sync.on('change', (change) => {
1009 if(change.pending > 10) {
1010 change.db = id;
1011 this.emit_async('repl_state', change);
1012 }
1013 change.update_only = id !== 'ram';
1014 this.load_changes(change);
1015 this.emit('pouch_sync_data', id, change);
1016 })
1017 .on('denied', (info) => {
1018 this.emit('pouch_sync_denied', id, info);
1019 })
1020 .on('error', (err) => {
1021 this.emit('pouch_sync_error', id, err);
1022 })
1023 .on('complete', (info) => {
1024 sync.cancel();
1025 sync.removeAllListeners();
1026 if(options) {
1027 info.db = id;
1028 this.emit_async('repl_state', info);
1029 final_sync(options);
1030 this.rebuild_indexes(id)
1031 .then(() => resolve(id));
1032 }
1033 });
1034 if(id == 'ram') {
1035 sync
1036 .on('paused', (info) => this.emit('pouch_sync_paused', id, info))
1037 .on('active', (info) => this.emit('pouch_sync_resumed', id, info));
1038 }
1039 return sync;
1040 };
1041 if(_push_only && !options.filter && id !== 'ram' && id !== 'meta') {
1042 options.filter = 'auth/push_only';
1043 options.query_params = {user: _user};
1044 }
1045 (job_prm.templates ? this.from_files(db_local, db_remote, options) : this.from_dump(db_local, db_remote, options))
1046 .then((synced) => {
1047 if(synced) {
1048 final_sync(options);
1049 if(typeof synced === 'number') {
1050 this.rebuild_indexes(id)
1051 .then(() => this.load_data());
1052 }
1053 }
1054 else {
1055 sync_events(db_local.replicate.from(db_remote, options), options);
1056 }
1057 });
1058 });
1059 });
1060 }
1061 from_dump(local, remote, opts = {}) {
1062 const {utils, job_prm} = this.$p;
1063 if(job_prm.crazy_ram && local === this.local.ram) {
1064 return Promise.resolve(false);
1065 }
1066 return local.get('_local/dumped')
1067 .then(() => true)
1068 .catch(() => remote.get('_local/dump'))
1069 .then(doc => {
1070 if(doc === true) {
1071 return doc;
1072 }
1073 const byteCharacters = atob(doc.dump);
1074 const byteNumbers = new Array(byteCharacters.length);
1075 for (let i = 0; i < byteCharacters.length; i++) {
1076 byteNumbers[i] = byteCharacters.charCodeAt(i);
1077 }
1078 const blob = new Blob([new Uint8Array(byteNumbers)], {type: 'application/zip'});
1079 return utils.blob_as_text(blob, 'array');
1080 })
1081 .then((uarray) => {
1082 if(uarray === true) {
1083 return uarray;
1084 }
1085 return ('JSZip' in window ? Promise.resolve() : utils.load_script('https://cdn.jsdelivr.net/jszip/2/jszip.min.js', 'script'))
1086 .then(() => {
1087 const zip = new JSZip(uarray);
1088 return zip.files.dump.asText();
1089 });
1090 })
1091 .then((text) => {
1092 if(text === true) {
1093 return text;
1094 }
1095 const opt = {
1096 proxy: remote.name,
1097 checkpoints: 'target',
1098 emit: (docs) => {
1099 this.emit('pouch_dumped', {db: local, docs});
1100 if(local.name.indexOf('ram') !== -1) {
1101 this.emit('pouch_data_page', {
1102 total_rows: docs.length,
1103 local_rows: 3,
1104 docs_written: 3,
1105 limit: 300,
1106 page: 0,
1107 start: Date.now(),
1108 });
1109 }
1110 }
1111 };
1112 if(remote.__opts.auth) {
1113 opt.auth = remote.__opts.auth;
1114 }
1115 if(opts.filter) {
1116 opt.filter = opts.filter;
1117 }
1118 if(opts.query_params) {
1119 opt.query_params = opts.query_params;
1120 }
1121 if(opts.selector) {
1122 opt.selector = opts.selector;
1123 }
1124 return (local.load ? Promise.resolve() : utils.load_script('/dist/pouchdb.load.js', 'script'))
1125 .then(() => {
1126 return local.load(text, opt);
1127 })
1128 .then(() => local.put({_id: '_local/dumped'}))
1129 .then(() => -1);
1130 })
1131 .catch((err) => {
1132 err.status !== 404 && console.log(err);
1133 return false;
1134 });
1135 }
1136 from_files(local, remote, opts = {}) {
1137 const li = local.name.lastIndexOf('_');
1138 const id = local.name.substr(li + 1);
1139 const {job_prm} = this.$p;
1140 if(job_prm.crazy_ram && local === this.local.ram) {
1141 return Promise.resolve(false);
1142 }
1143 return fetch(`/${id}/00000.json`)
1144 .then((res) => res.json())
1145 .then((info) => {
1146 return local.get('_local/stamp')
1147 .then((doc) => {
1148 if(doc.stamp === info.stamp) {
1149 return true;
1150 }
1151 info._rev = doc._rev;
1152 return info;
1153 })
1154 .catch(() => {
1155 return info;
1156 });
1157 })
1158 .then((info) => {
1159 if(info === true) {
1160 return info;
1161 }
1162 if(info) {
1163 return (local.load ? Promise.resolve() : this.$p.utils.load_script('/dist/pouchdb.load.js', 'script'))
1164 .then(() => info);
1165 }
1166 })
1167 .then((info) => {
1168 if(info === true) {
1169 return info;
1170 }
1171 if(info) {
1172 const {origin} = location;
1173 let series = Promise.resolve();
1174 const msg = {db: id, ok: true, docs_read: 0, pending: info.doc_count, start_time: new Date().toISOString()};
1175 this.emit_async('repl_state', msg);
1176 const opt = {
1177 proxy: remote.name,
1178 checkpoints: 'target',
1179 emit: (docs) => {
1180 this.emit('pouch_dumped', {db: local, docs});
1181 }
1182 };
1183 if(remote.__opts.auth) {
1184 opt.auth = remote.__opts.auth;
1185 }
1186 if(opts.filter) {
1187 opt.filter = opts.filter;
1188 }
1189 if(opts.query_params) {
1190 opt.query_params = opts.query_params;
1191 }
1192 if(opts.selector) {
1193 opt.selector = opts.selector;
1194 }
1195 for(let i = 1; i <= info.files; i++) {
1196 series = series.then(() => {
1197 return local.load(`${origin}/${id}/${i.pad(5)}.json`, opt);
1198 })
1199 .then((step) => {
1200 msg.docs_read = (info.doc_count * i / info.files).round();
1201 msg.pending = info.doc_count - msg.docs_read;
1202 this.emit_async('repl_state', msg);
1203 });
1204 }
1205 return series
1206 .then(() => {
1207 info._id = '_local/stamp';
1208 return local.put(info);
1209 })
1210 .then(() => -1);
1211 }
1212 })
1213 .catch(() => {
1214 return false;
1215 });
1216 }
1217 rebuild_indexes(id, silent) {
1218 const {local, remote} = this;
1219 const msg = {db: id, ok: true, docs_read: 0, pending: 0, start_time: new Date().toISOString()};
1220 let promises = Promise.resolve();
1221 return local[id] === remote[id] ?
1222 Promise.resolve() :
1223 local[id].allDocs({
1224 include_docs: true,
1225 startkey: '_design/',
1226 endkey : '_design/\u0fff',
1227 limit: 1000,
1228 })
1229 .then(({rows}) => {
1230 for(const {doc} of rows) {
1231 if(doc._id.indexOf('/server') !== -1 && id !== 'ram') {
1232 continue;
1233 }
1234 if(doc.views) {
1235 for(const name in doc.views) {
1236 const view = doc.views[name];
1237 const index = doc._id.replace('_design/', '') + '/' + name;
1238 if(doc.language === 'javascript') {
1239 promises = promises.then(() => {
1240 if(silent) {
1241 this.emit('rebuild_indexes', {id, index, start: true});
1242 }
1243 else {
1244 msg.index = index;
1245 this.emit('repl_state', msg);
1246 }
1247 return local[id].query(index, {limit: 1}).catch(() => null);
1248 });
1249 }
1250 else {
1251 const selector = {
1252 limit: 1,
1253 fields: ['_id'],
1254 selector: {},
1255 use_index: index.split('/'),
1256 };
1257 for(const fld of view.options.def.fields) {
1258 selector.selector[fld] = '';
1259 }
1260 promises = promises.then(() => {
1261 if(silent) {
1262 this.emit('rebuild_indexes', {id, index, start: true});
1263 }
1264 else {
1265 msg.index = index;
1266 this.emit('repl_state', msg);
1267 }
1268 return local[id].find(selector).catch(() => null);
1269 });
1270 }
1271 }
1272 }
1273 }
1274 return promises.then(() => {
1275 msg.index = '';
1276 msg.end_time = new Date().toISOString();
1277 this.emit('repl_state', msg);
1278 this.emit('rebuild_indexes', {id, start: false, finish: true});
1279 });
1280 });
1281 }
1282 call_data_loaded(page) {
1283 const {local, props} = this;
1284 if(!props._data_loaded) {
1285 props._data_loaded = true;
1286 if(!page) {
1287 page = local.sync._page || {};
1288 }
1289 if(!local.sync._page) {
1290 local.sync._page = page;
1291 }
1292 Promise.resolve().then(() => {
1293 this.emit(page.note = 'pouch_data_loaded', page);
1294 this.authorized && this.load_doc_ram();
1295 });
1296 }
1297 else if(!props._doc_ram_loaded && !props._doc_ram_loading && this.authorized) {
1298 this.load_doc_ram();
1299 }
1300 }
1301 reset_local_data() {
1302 const {local, remote} = this;
1303 const do_reload = () => {
1304 setTimeout(() => typeof location != 'undefined' && location.reload(true), 1000);
1305 };
1306 return this.log_out()
1307 .then(() => {
1308 return local.templates && local.templates.adapter === 'idb' && local.templates.destroy()
1309 })
1310 .then(() => {
1311 return remote.ram != local.ram && local.ram.destroy()
1312 })
1313 .then(() => {
1314 return remote.doc != local.doc && local.doc.destroy()
1315 })
1316 .then(do_reload)
1317 .catch(do_reload);
1318 }
1319 load_obj(tObj, attr) {
1320 const db = (attr && attr.db) || this.db(tObj._manager);
1321 if(!db) {
1322 return Promise.resolve(tObj);
1323 }
1324 return db.get(tObj._manager.class_name + '|' + tObj.ref)
1325 .then((res) => {
1326 for(const fld of fieldsToDelete) {
1327 delete res[fld];
1328 }
1329 tObj._data._loading = true;
1330 tObj._mixin(res);
1331 tObj._obj._rev = res._rev;
1332 })
1333 .catch((err) => {
1334 if(err.status != 404) {
1335 throw err;
1336 }
1337 })
1338 .then(() => {
1339 return tObj;
1340 });
1341 }
1342 save_obj(tObj, attr) {
1343 const {_manager, _obj, _data, ref, class_name} = tObj;
1344 const db = attr.db || this.db(_manager);
1345 if(!_data || (_data._saving && !_data._modified) || !db) {
1346 return Promise.resolve(tObj);
1347 }
1348 if(_data._saving && _data._modified) {
1349 _data._saving++;
1350 if(_data._saving > 10) {
1351 return Promise.reject(new Error(`Циклическая перезапись`));
1352 }
1353 return new Promise((resolve) => {
1354 setTimeout(() => resolve(this.save_obj(tObj, attr)), 200);
1355 });
1356 }
1357 _data._saving = 1;
1358 const tmp = Object.assign({_id: `${class_name}|${ref}`, class_name}, _obj);
1359 const {utils, wsql} = this.$p;
1360 if(utils.is_doc_obj(tObj) || _manager.build_search) {
1361 if(_manager.build_search) {
1362 _manager.build_search(tmp, tObj);
1363 }
1364 else {
1365 tmp.search = ((_obj.number_doc || '') + (_obj.note ? ' ' + _obj.note : '')).toLowerCase();
1366 }
1367 }
1368 tmp.timestamp = {
1369 user: this.authorized || wsql.get_user_param('user_name'),
1370 moment: utils.moment().format('YYYY-MM-DDTHH:mm:ss ZZ'),
1371 };
1372 if(attr.attachments) {
1373 tmp._attachments = attr.attachments;
1374 }
1375 if(_manager.metadata().grouping === 'array') {
1376 delete tmp._id;
1377 delete tmp.class_name;
1378 const index = Math.floor(utils.crc32(tmp.ref) / 268435455);
1379 const _id = `${class_name}|${index.pad()}`;
1380 return db.get(_id)
1381 .catch((err) => {
1382 if(err.status !== 404) throw err;
1383 return {_id, class_name, rows: []};
1384 })
1385 .then((doc) => {
1386 let finded;
1387 for(const row of doc.rows) {
1388 if(row.ref === tmp.ref) {
1389 Object.assign(row, tmp);
1390 finded = true;
1391 break;
1392 }
1393 }
1394 if(!finded) {
1395 doc.rows.push(tmp);
1396 }
1397 return db.put(doc);
1398 })
1399 .then(() => {
1400 tObj.is_new() && tObj._set_loaded(tObj.ref);
1401 return tObj;
1402 });
1403 }
1404 else {
1405 delete tmp.ref;
1406 return (tObj.is_new() ? Promise.resolve(true) : db.get(tmp._id))
1407 .then((res) => {
1408 if(typeof res === 'object') {
1409 if(tmp._rev !== res._rev && _manager.metadata().check_rev !== false) {
1410 const {timestamp} = res;
1411 const err = new Error(`Объект ${timestamp && typeof timestamp.user === 'string' ?
1412 `изменил ${timestamp.user}<br/>${timestamp.moment}` : 'изменён другим пользователем'}`);
1413 err._rev = true;
1414 throw err;
1415 }
1416 tmp._rev = res._rev;
1417 for (let att in res._attachments) {
1418 if(!tmp._attachments) {
1419 tmp._attachments = {};
1420 }
1421 if(!tmp._attachments[att]) {
1422 tmp._attachments[att] = res._attachments[att];
1423 }
1424 }
1425 }
1426 return res;
1427 })
1428 .catch((err) => {
1429 if(err.status !== 404) throw err;
1430 })
1431 .then(() => db.put(tmp))
1432 .then((res) => {
1433 if(res) {
1434 tObj.is_new() && tObj._set_loaded(tObj.ref);
1435 if(tmp._attachments) {
1436 if(!tObj._attachments) {
1437 tObj._attachments = {};
1438 }
1439 for (let att in tmp._attachments) {
1440 if(!tObj._attachments[att] || !tmp._attachments[att].stub) {
1441 tObj._attachments[att] = tmp._attachments[att];
1442 }
1443 }
1444 }
1445 _obj._rev = res.rev;
1446 return tObj;
1447 }
1448 });
1449 }
1450 }
1451 get_tree(_mgr, attr) {
1452 return this.find_rows(_mgr, {
1453 is_folder: true,
1454 _raw: true,
1455 _top: attr.count || 300,
1456 _skip: attr.start || 0
1457 })
1458 .then((rows) => {
1459 rows.sort((a, b) => {
1460 const {guid} = this.$p.utils.blank;
1461 if(a.parent == guid && b.parent != guid) {
1462 return -1;
1463 }
1464 if(b.parent == guid && a.parent != guid) {
1465 return 1;
1466 }
1467 if(a.name < b.name) {
1468 return -1;
1469 }
1470 if(a.name > b.name) {
1471 return 1;
1472 }
1473 return 0;
1474 });
1475 return rows.map((row) => ({
1476 ref: row.ref,
1477 parent: row.parent,
1478 presentation: row.name
1479 }));
1480 })
1481 .then((ares) => this.$p.iface.data_to_tree.call(_mgr, ares, attr));
1482 }
1483 get_selection(_mgr, attr) {
1484 const {classes} = this.$p;
1485 const cmd = attr.metadata || _mgr.metadata();
1486 const flds = ['ref', '_deleted'];
1487 const selection = {
1488 _raw: true,
1489 _total_count: true,
1490 _top: attr.count || 30,
1491 _skip: attr.start || 0,
1492 };
1493 const ares = [];
1494 if(cmd.form && cmd.form.selection) {
1495 cmd.form.selection.fields.forEach((fld) => flds.push(fld));
1496 }
1497 else if(_mgr instanceof classes.DocManager) {
1498 flds.push('posted');
1499 flds.push('date');
1500 flds.push('number_doc');
1501 }
1502 else if(_mgr instanceof classes.TaskManager) {
1503 flds.push('name as presentation');
1504 flds.push('date');
1505 flds.push('number_doc');
1506 flds.push('completed');
1507 }
1508 else if(_mgr instanceof classes.BusinessProcessManager) {
1509 flds.push('date');
1510 flds.push('number_doc');
1511 flds.push('started');
1512 flds.push('finished');
1513 }
1514 else {
1515 if(cmd.hierarchical && cmd.group_hierarchy) {
1516 flds.push('is_folder');
1517 }
1518 else {
1519 flds.push('0 as is_folder');
1520 }
1521 if(cmd.main_presentation_name) {
1522 flds.push('name as presentation');
1523 }
1524 else {
1525 if(cmd.code_length) {
1526 flds.push('id as presentation');
1527 }
1528 else {
1529 flds.push('... as presentation');
1530 }
1531 }
1532 if(cmd.has_owners) {
1533 flds.push('owner');
1534 }
1535 if(cmd.code_length) {
1536 flds.push('id');
1537 }
1538 }
1539 if(_mgr.metadata('date') && (attr.date_from || attr.date_till)) {
1540 if(!attr.date_from) {
1541 attr.date_from = new Date('2017-01-01');
1542 }
1543 if(!attr.date_till) {
1544 attr.date_till = $p.utils.date_add_day(new Date(), 1);
1545 }
1546 selection.date = {between: [attr.date_from, attr.date_till]};
1547 }
1548 if(cmd.hierarchical && attr.parent) {
1549 selection.parent = attr.parent;
1550 }
1551 if(attr.selection) {
1552 if(Array.isArray(attr.selection)) {
1553 attr.selection.forEach((asel) => {
1554 for (const fld in asel) {
1555 if(fld[0] != '_' || fld == '_view' || fld == '_key') {
1556 selection[fld] = asel[fld];
1557 }
1558 }
1559 });
1560 }
1561 else {
1562 for (const fld in attr.selection) {
1563 if(fld[0] != '_' || fld == '_view' || fld == '_key') {
1564 selection[fld] = attr.selection[fld];
1565 }
1566 }
1567 }
1568 }
1569 if(selection._key && selection._key._drop_date && selection.date) {
1570 delete selection.date;
1571 }
1572 if(attr.filter && (!selection._key || !selection._key._search)) {
1573 if(cmd.input_by_string.length == 1) {
1574 selection[cmd.input_by_string] = {like: attr.filter};
1575 }
1576 else {
1577 selection.or = [];
1578 cmd.input_by_string.forEach((ifld) => {
1579 const flt = {};
1580 flt[ifld] = {like: attr.filter};
1581 selection.or.push(flt);
1582 });
1583 }
1584 }
1585 if(selection._key && selection._key._order_by) {
1586 selection._key._order_by = attr.direction;
1587 }
1588 return this.find_rows(_mgr, selection)
1589 .then((rows) => {
1590 if(rows.hasOwnProperty('_total_count') && rows.hasOwnProperty('rows')) {
1591 attr._total_count = rows._total_count;
1592 rows = rows.rows;
1593 }
1594 rows.forEach((doc) => {
1595 const o = {};
1596 flds.forEach((fld) => {
1597 let fldsyn;
1598 if(fld == 'ref') {
1599 o[fld] = doc[fld];
1600 return;
1601 }
1602 else if(fld.indexOf(' as ') != -1) {
1603 fldsyn = fld.split(' as ')[1];
1604 fld = fld.split(' as ')[0].split('.');
1605 fld = fld[fld.length - 1];
1606 }
1607 else {
1608 fldsyn = fld;
1609 }
1610 const mf = _mgr.metadata(fld);
1611 if(mf) {
1612 if(mf.type.date_part) {
1613 o[fldsyn] = $p.moment(doc[fld]).format($p.moment._masks[mf.type.date_part]);
1614 }
1615 else if(mf.type.is_ref) {
1616 if(!doc[fld] || doc[fld] == $p.utils.blank.guid) {
1617 o[fldsyn] = '';
1618 }
1619 else {
1620 const mgr = _mgr.value_mgr(o, fld, mf.type, false, doc[fld]);
1621 if(mgr) {
1622 o[fldsyn] = mgr.get(doc[fld]).presentation;
1623 }
1624 else {
1625 o[fldsyn] = '';
1626 }
1627 }
1628 }
1629 else if(typeof doc[fld] === 'number' && mf.type.fraction) {
1630 o[fldsyn] = doc[fld].toFixed(mf.type.fraction);
1631 }
1632 else {
1633 o[fldsyn] = doc[fld];
1634 }
1635 }
1636 });
1637 ares.push(o);
1638 });
1639 return $p.iface.data_to_grid.call(_mgr, ares, attr);
1640 })
1641 .catch($p.record_log);
1642 }
1643 load_array(_mgr, refs, with_attachments, db) {
1644 if(!refs || !refs.length) {
1645 return Promise.resolve(false);
1646 }
1647 if(!db && _mgr) {
1648 db = this.db(_mgr);
1649 }
1650 const options = {
1651 limit: refs.length + 1,
1652 include_docs: true,
1653 keys: _mgr ? refs.map((v) => _mgr.class_name + '|' + v) : refs,
1654 };
1655 if(with_attachments) {
1656 options.attachments = true;
1657 options.binary = true;
1658 }
1659 return db.allDocs(options).then((result) => this.load_changes(result, {}));
1660 }
1661 load_view(_mgr, _view, options) {
1662 return new Promise((resolve, reject) => {
1663 const db = this.db(_mgr);
1664 if(!options) {
1665 options = {
1666 limit: 1000,
1667 include_docs: true,
1668 startkey: _mgr.class_name + '|',
1669 endkey: _mgr.class_name + '|\ufff0',
1670 };
1671 }
1672 function process_docs(err, result) {
1673 if(result) {
1674 if(result.rows.length) {
1675 options.startkey = result.rows[result.rows.length - 1].key;
1676 options.skip = 1;
1677 _mgr.load_array(result.rows.map(({doc}) => {
1678 doc.ref = doc._id.split('|')[1];
1679 delete doc._id;
1680 return doc;
1681 }), true);
1682 if(result.rows.length < options.limit) {
1683 resolve();
1684 }
1685 else {
1686 db.query(_view, options, process_docs);
1687 }
1688 }
1689 else {
1690 resolve();
1691 }
1692 }
1693 else if(err && err.status !== 404) {
1694 reject(err);
1695 }
1696 }
1697 db.query(_view, options, process_docs);
1698 });
1699 }
1700 load_doc_ram() {
1701 const {local, props, $p: {md}} = this;
1702 if(!local.doc){
1703 return;
1704 }
1705 const res = [];
1706 const {_m} = md;
1707 this.emit('pouch_doc_ram_start');
1708 props._doc_ram_loading = true;
1709 ['cat', 'cch', 'ireg'].forEach((kind) => {
1710 for (const name in _m[kind]) {
1711 (_m[kind][name].cachable === 'doc_ram' || _m[kind][name].cachable === 'templates_ram') && res.push(kind + '.' + name);
1712 }
1713 });
1714 return res.reduce((acc, name) => {
1715 return acc.then(() => {
1716 const opt = {
1717 include_docs: true,
1718 startkey: name + '|',
1719 endkey: name + '|\ufff0',
1720 limit: 10000,
1721 };
1722 const page = local.sync._page || {};
1723 const meta = md.get(name);
1724 this.emit('pouch_data_page', Object.assign(page, {synonym: meta.synonym}));
1725 return local[meta.cachable.replace(/_ram$/, '')].allDocs(opt).then((res) => {
1726 this.load_changes(res, opt);
1727 });
1728 });
1729 }, Promise.resolve())
1730 .catch((err) => {
1731 props._doc_ram_loading = false;
1732 this.emit('pouch_sync_error', 'doc', err);
1733 return {docs: []};
1734 })
1735 .then(() => {
1736 props._doc_ram_loading = false;
1737 props._doc_ram_loaded = true;
1738 this.emit('pouch_doc_ram_loaded');
1739 });
1740 }
1741 find_rows(_mgr, selection, db) {
1742 if(!db) {
1743 db = this.db(_mgr);
1744 }
1745 if(!db) {
1746 return Promise.resolve([]);
1747 }
1748 const err_handler = this.emit.bind(this, 'pouch_sync_error', _mgr.cachable);
1749 if(selection && selection._mango) {
1750 const sel = {};
1751 for(const fld in selection) {
1752 if(fld[0] !== '_') {
1753 sel[fld] = selection[fld];
1754 }
1755 }
1756 return db.find(sel)
1757 .then(({docs}) => {
1758 if(!docs) {
1759 docs = [];
1760 }
1761 for (const doc of docs) {
1762 doc.ref = doc._id.split('|')[1];
1763 if(!selection._raw){
1764 delete doc._id;
1765 delete doc.class_name;
1766 }
1767 }
1768 return selection._raw ? docs : _mgr.load_array(docs);
1769 })
1770 .catch((err) => {
1771 err_handler(err);
1772 return [];
1773 });
1774 }
1775 const {utils} = this.$p;
1776 const res = [];
1777 const options = {
1778 limit: 100,
1779 include_docs: true,
1780 startkey: _mgr.class_name + '|',
1781 endkey: _mgr.class_name + '|\ufff0',
1782 };
1783 let doc, _raw, _view, _total_count, top, calc_count, top_count = 0, skip = 0, skip_count = 0;
1784 if(selection) {
1785 if(selection._top) {
1786 top = selection._top;
1787 delete selection._top;
1788 }
1789 else {
1790 top = 300;
1791 }
1792 if(selection._raw) {
1793 _raw = selection._raw;
1794 delete selection._raw;
1795 }
1796 if(selection._total_count) {
1797 _total_count = selection._total_count;
1798 delete selection._total_count;
1799 }
1800 if(selection._view) {
1801 _view = selection._view;
1802 delete selection._view;
1803 }
1804 if(selection._key) {
1805 if(selection._key._order_by == 'des') {
1806 options.startkey = selection._key.endkey || selection._key + '\ufff0';
1807 options.endkey = selection._key.startkey || selection._key;
1808 options.descending = true;
1809 }
1810 else {
1811 options.startkey = selection._key.startkey || selection._key;
1812 options.endkey = selection._key.endkey || selection._key + '\ufff0';
1813 }
1814 }
1815 if(typeof selection._skip == 'number') {
1816 skip = selection._skip;
1817 delete selection._skip;
1818 }
1819 if(selection._attachments) {
1820 options.attachments = true;
1821 options.binary = true;
1822 delete selection._attachments;
1823 }
1824 }
1825 if(_total_count) {
1826 calc_count = true;
1827 _total_count = 0;
1828 if(Object.keys(selection).length <= 1) {
1829 if(selection._key && selection._key.hasOwnProperty('_search')) {
1830 options.include_docs = false;
1831 options.limit = 100000;
1832 return db.query(_view, options)
1833 .then((result) => {
1834 result.rows.forEach((row) => {
1835 if(!selection._key._search || row.key[row.key.length - 1].toLowerCase().indexOf(selection._key._search) != -1) {
1836 _total_count++;
1837 if(skip) {
1838 skip_count++;
1839 if(skip_count < skip) {
1840 return;
1841 }
1842 }
1843 if(top) {
1844 top_count++;
1845 if(top_count > top) {
1846 return;
1847 }
1848 }
1849 res.push(row.id);
1850 }
1851 });
1852 delete options.startkey;
1853 delete options.endkey;
1854 if(options.descending) {
1855 delete options.descending;
1856 }
1857 options.keys = res;
1858 options.include_docs = true;
1859 return db.allDocs(options);
1860 })
1861 .catch((err) => {
1862 err_handler(err);
1863 return {rows: []};
1864 })
1865 .then((result) => {
1866 return {
1867 rows: result.rows.map(({doc}) => {
1868 doc.ref = doc._id.split('|')[1];
1869 if(!_raw) {
1870 delete doc._id;
1871 }
1872 return doc;
1873 }),
1874 _total_count: _total_count,
1875 };
1876 });
1877 }
1878 }
1879 }
1880 return new Promise((resolve, reject) => {
1881 function process_docs(result) {
1882 if(result && result.rows.length) {
1883 options.startkey = result.rows[result.rows.length - 1].key;
1884 options.skip = 1;
1885 result.rows.forEach((rev) => {
1886 doc = rev.doc;
1887 let key = doc._id.split('|');
1888 doc.ref = key[1];
1889 if(!_raw) {
1890 delete doc._id;
1891 }
1892 if(!utils._selection.call(_mgr, doc, selection)) {
1893 return;
1894 }
1895 if(calc_count) {
1896 _total_count++;
1897 }
1898 if(skip) {
1899 skip_count++;
1900 if(skip_count < skip) {
1901 return;
1902 }
1903 }
1904 if(top) {
1905 top_count++;
1906 if(top_count > top) {
1907 return;
1908 }
1909 }
1910 res.push(doc);
1911 });
1912 if((result.rows.length < options.limit) || top && top_count > top && !calc_count) {
1913 resolve(_raw ? res : _mgr.load_array(res));
1914 }
1915 else {
1916 fetch_next_page();
1917 }
1918 }
1919 else {
1920 if(calc_count) {
1921 resolve({
1922 rows: _raw ? res : _mgr.load_array(res),
1923 _total_count: _total_count,
1924 });
1925 }
1926 else {
1927 resolve(_raw ? res : _mgr.load_array(res));
1928 }
1929 }
1930 }
1931 function fetch_next_page() {
1932 (_view ? db.query(_view, options) : db.allDocs(options))
1933 .catch((err) => {
1934 err_handler(err);
1935 reject(err);
1936 })
1937 .then(process_docs);
1938 }
1939 fetch_next_page();
1940 });
1941 }
1942 save_attachment(_mgr, ref, att_id, attachment, type) {
1943 if(!type) {
1944 type = {type: 'text/plain'};
1945 }
1946 if(!(attachment instanceof Blob) && type.indexOf('text') == -1) {
1947 attachment = new Blob([attachment], {type: type});
1948 }
1949 let _rev,
1950 db = this.db(_mgr);
1951 ref = _mgr.class_name + '|' + this.$p.utils.fix_guid(ref);
1952 return db.get(ref)
1953 .then((res) => {
1954 if(res) {
1955 _rev = res._rev;
1956 }
1957 })
1958 .catch((err) => {
1959 if(err.status != 404) {
1960 throw err;
1961 }
1962 })
1963 .then(() => {
1964 return db.putAttachment(ref, att_id, _rev, attachment, type);
1965 });
1966 }
1967 get_attachment(_mgr, ref, att_id) {
1968 return this.db(_mgr).getAttachment(_mgr.class_name + '|' + this.$p.utils.fix_guid(ref), att_id);
1969 }
1970 delete_attachment(_mgr, ref, att_id) {
1971 let _rev,
1972 db = this.db(_mgr);
1973 ref = _mgr.class_name + '|' + this.$p.utils.fix_guid(ref);
1974 return db.get(ref)
1975 .then((res) => {
1976 if(res) {
1977 _rev = res._rev;
1978 }
1979 })
1980 .catch((err) => {
1981 if(err.status != 404) {
1982 throw err;
1983 }
1984 })
1985 .then(() => {
1986 return db.removeAttachment(ref, att_id, _rev);
1987 });
1988 }
1989 load_changes(changes, options) {
1990 let docs, doc, res = {}, cn, key, {$p} = this;
1991 if(!options) {
1992 if(changes.direction) {
1993 if(changes.direction != 'pull') {
1994 return;
1995 }
1996 docs = changes.change.docs;
1997 }
1998 else {
1999 docs = changes.docs;
2000 }
2001 }
2002 else {
2003 docs = changes.rows;
2004 }
2005 if(docs.length > 0) {
2006 if(options) {
2007 options.startkey = docs[docs.length - 1].key;
2008 options.skip = 1;
2009 }
2010 docs.forEach((rev) => {
2011 doc = options ? rev.doc : rev;
2012 if(!doc) {
2013 if((rev.value && rev.value.deleted)) {
2014 doc = {
2015 _id: rev.id,
2016 _deleted: true,
2017 };
2018 }
2019 else if(rev.error) {
2020 return;
2021 }
2022 }
2023 key = doc._id.split('|');
2024 if(key[0] === 'system') {
2025 return !options && this.emit('system', key[1], doc);
2026 }
2027 cn = key[0].split('.');
2028 doc.ref = key[1];
2029 delete doc._id;
2030 if(!res[cn[0]]) {
2031 res[cn[0]] = {};
2032 }
2033 if(!res[cn[0]][cn[1]]) {
2034 res[cn[0]][cn[1]] = [];
2035 }
2036 res[cn[0]][cn[1]].push(doc);
2037 });
2038 for (let mgr in res) {
2039 for (cn in res[mgr]) {
2040 if($p[mgr] && $p[mgr][cn]) {
2041 $p[mgr][cn].load_array(res[mgr][cn], changes.update_only ? 'update_only' : true);
2042 }
2043 }
2044 }
2045 return true;
2046 }
2047 return false;
2048 }
2049 attach_refresher(regex, timout = 500000) {
2050 if(this.props._refresher) {
2051 clearInterval(this.props._refresher);
2052 }
2053 setInterval(() => {
2054 if(this.authorized && this.remote.ram && this.remote.ram.adapter == 'http') {
2055 this.remote.ram.info()
2056 .then(response => {
2057 })
2058 .catch(err => {
2059 });
2060 }
2061 }, timout);
2062 }
2063 backup_database(do_zip) {
2064 }
2065 restore_database(do_zip) {
2066 }
2067 get authorized() {
2068 const {_auth} = this.props;
2069 return _auth && _auth.username;
2070 }
2071 fetch(url, opts) {
2072 const {authorized, remote, props} = this;
2073 if(!opts.headers.get('Authorization')) {
2074 if(authorized) {
2075 for(const name in remote) {
2076 const db = remote[name];
2077 const {auth} = db.__opts;
2078 if(auth) {
2079 const {Authorization} = db.getBasicAuthHeaders({prefix: this.auth_prefix(), ...auth});
2080 opts.headers.set('Authorization', Authorization);
2081 break;
2082 }
2083 }
2084 }
2085 }
2086 if(props.branch) {
2087 opts.headers.set('branch', props.branch.valueOf());
2088 }
2089 return PouchDB$1.fetch(url, opts);
2090 }
2091 };
2092}
2093var adapter$1 = (constructor) => {
2094 const {classes} = constructor;
2095 classes.PouchDB = PouchDB$1;
2096 classes.AdapterPouch = adapter(classes);
2097};
2098
2099const plugin = {
2100 proto(constructor) {
2101 proto(constructor);
2102 adapter$1(constructor);
2103 },
2104 constructor(){
2105 const {AdapterPouch} = this.classes;
2106 this.adapters.pouch = new AdapterPouch(this);
2107 }
2108};
2109
2110module.exports = plugin;
2111//# sourceMappingURL=index.js.map