<commit_before>import {createEvent, ShimEvent, ProxyPolyfill, IDBVersionChangeEvent} from './Event.js';
import {findError, createDOMException, DOMException} from './DOMException.js';
import {IDBOpenDBRequest, IDBRequest} from './IDBRequest.js';
import * as util from './util.js';
import Key from './Key.js';
import IDBTransaction from './IDBTransaction.js';
import IDBDatabase from './IDBDatabase.js';
import CFG from './CFG.js';

let sysdb;

/**
 * Craetes the sysDB to keep track of version numbers for databases
 **/
function createSysDB (success, failure) {
    function sysDbCreateError (...args /* tx, err */) {
        const err = findError(args);
        CFG.DEBUG && console.log('Error in sysdb transaction - when creating dbVersions', err);
        failure(err);
    }

    if (sysdb) {
        success();
    } else {
        sysdb = CFG.win.openDatabase('__sysdb__.sqlite', 1, 'System Database', CFG.DEFAULT_DB_SIZE);
        sysdb.transaction(function (tx) {
            tx.executeSql('CREATE TABLE IF NOT EXISTS dbVersions (name VARCHAR(255), version INT);', [], success, sysDbCreateError);
        }, sysDbCreateError);
    }
}

/**
 * IDBFactory Class
 * https://w3c.github.io/IndexedDB/#idl-def-IDBFactory
 * @constructor
 */
function IDBFactory () {
    this.modules = {DOMException, Event: typeof Event !== 'undefined' ? Event : ShimEvent, ShimEvent, ProxyPolyfill, IDBFactory};
}

/**
 * The IndexedDB Method to create a new database and return the DB
 * @param {string} name
 * @param {number} version
 */
IDBFactory.prototype.open = function (name, version) {
    const req = new IDBOpenDBRequest();
    let calledDbCreateError = false;

    if (arguments.length === 0) {
        throw new TypeError('Database name is required');
    } else if (arguments.length >= 2) {
        version = Number(version);
        if (isNaN(version) || !isFinite(version) ||
            version >= 0x20000000000000 || // 2 ** 53
            version < 1) { // The spec only mentions version==0 as throwing, but W3C tests fail with these
            throw new TypeError('Invalid database version: ' + version);
        }
    }
    name = String(name); // cast to a string

    function dbCreateError (...args /* tx, err */) {
        if (calledDbCreateError) {
            return;
        }
        const err = findError(args);
        calledDbCreateError = true;
        const evt = createEvent('error', args);
        req.__readyState = 'done';
        req.__error = err || DOMException;
        req.dispatchEvent(evt);
    }

    function openDB (oldVersion) {
        const db = CFG.win.openDatabase(util.escapeDatabaseName(name), 1, name, CFG.DEFAULT_DB_SIZE);
        req.__readyState = 'done';
        if (version === undefined) {
            version = oldVersion || 1;
        }
        if (oldVersion > version) {
            const err = createDOMException('VersionError', 'An attempt was made to open a database using a lower version than the existing version.', version);
            dbCreateError(err);
            return;
        }

        db.transaction(function (tx) {
            tx.executeSql('CREATE TABLE IF NOT EXISTS __sys__ (name VARCHAR(255), keyPath VARCHAR(255), autoInc BOOLEAN, indexList BLOB, currNum INTEGER)', [], function () {
                tx.executeSql('SELECT * FROM __sys__', [], function (tx, data) {
                    req.__result = new IDBDatabase(db, name, version, data);
                    if (oldVersion < version) {
                        // DB Upgrade in progress
                        sysdb.transaction(function (systx) {
                            systx.executeSql('UPDATE dbVersions SET version = ? WHERE name = ?', [version, name], function () {
                                const e = new IDBVersionChangeEvent('upgradeneeded', {oldVersion, newVersion: version});
                                req.__transaction = req.result.__versionTransaction = new IDBTransaction(req.result, req.result.objectStoreNames, 'versionchange');
                                req.transaction.__addToTransactionQueue(function onupgradeneeded (tx, args, success) {
                                    req.dispatchEvent(e);
                                    success();
                                });
                                req.transaction.on__beforecomplete = function () {
                                    req.result.__versionTransaction = null;
                                };
                                req.transaction.on__complete = function () {
                                    if (req.__result.__closed) {
                                        return;
                                    }
                                    req.__transaction = null;
                                    const e = createEvent('success');
                                    req.dispatchEvent(e);
                                };
                            }, dbCreateError);
                        }, dbCreateError);
                    } else {
                        const e = createEvent('success');
                        req.dispatchEvent(e);
                    }
                }, dbCreateError);
            }, dbCreateError);
        }, dbCreateError);
    }

    createSysDB(function () {
        sysdb.transaction(function (tx) {
            tx.executeSql('SELECT * FROM dbVersions WHERE name = ?', [name], function (tx, data) {
                if (data.rows.length === 0) {
                    // Database with this name does not exist
                    tx.executeSql('INSERT INTO dbVersions VALUES (?,?)', [name, version || 1], function () {
                        openDB(0);
                    }, dbCreateError);
                } else {
                    openDB(data.rows.item(0).version);
                }
            }, dbCreateError);
        }, dbCreateError);
    }, dbCreateError);

    return req;
};

/**
 * Deletes a database
 * @param {string} name
 * @returns {IDBOpenDBRequest}
 */
IDBFactory.prototype.deleteDatabase = function (name) {
    const req = new IDBOpenDBRequest();
    let calledDBError = false;
    let version = null;

    if (arguments.length === 0) {
        throw new TypeError('Database name is required');
    }
    name = String(name); // cast to a string

    function dbError (...args /* tx, err */) {
        if (calledDBError) {
            return;
        }
        const err = findError(args);
        req.__readyState = 'done';
        req.__error = err || DOMException;
        const e = createEvent('error', args);
        req.dispatchEvent(e);
        calledDBError = true;
    }

    function deleteFromDbVersions () {
        sysdb.transaction(function (systx) {
            systx.executeSql('DELETE FROM dbVersions WHERE name = ? ', [name], function () {
                req.__result = undefined;
                const e = new IDBVersionChangeEvent('success', {oldVersion: version, newVersion: null});
                req.dispatchEvent(e);
            }, dbError);
        }, dbError);
    }

    createSysDB(function () {
        sysdb.transaction(function (systx) {
            systx.executeSql('SELECT * FROM dbVersions WHERE name = ?', [name], function (tx, data) {
                if (data.rows.length === 0) {
                    req.__result = undefined;
                    const e = new IDBVersionChangeEvent('success', {oldVersion: version, newVersion: null});
                    req.dispatchEvent(e);
                    return;
                }
                version = data.rows.item(0).version;
                const db = CFG.win.openDatabase(util.escapeDatabaseName(name), 1, name, CFG.DEFAULT_DB_SIZE);
                db.transaction(function (tx) {
                    tx.executeSql('SELECT * FROM __sys__', [], function (tx, data) {
                        const tables = data.rows;
                        (function deleteTables (i) {
                            if (i >= tables.length) {
                                // If all tables are deleted, delete the housekeeping tables
                                tx.executeSql('DROP TABLE IF EXISTS __sys__', [], function () {
                                    // Finally, delete the record for this DB from sysdb
                                    deleteFromDbVersions();
                                }, dbError);
                            } else {
                                // Delete all tables in this database, maintained in the sys table
                                tx.executeSql('DROP TABLE ' + util.escapeStore(tables.item(i).name), [], function () {
                                    deleteTables(i + 1);
                                }, function () {
                                    deleteTables(i + 1);
                                });
                            }
                        }(0));
                    }, function (e) {
                        // __sysdb table does not exist, but that does not mean delete did not happen
                        deleteFromDbVersions();
                    });
                });
            }, dbError);
        }, dbError);
    }, dbError);

    return req;
};

/**
 * Compares two keys
 * @param key1
 * @param key2
 * @returns {number}
 */
function cmp (key1, key2) {
    if (arguments.length < 2) {
        throw new TypeError('You must provide two keys to be compared');
    }

    Key.convertValueToKey(key1);
    Key.convertValueToKey(key2);
    const encodedKey1 = Key.encode(key1);
    const encodedKey2 = Key.encode(key2);
    const result = encodedKey1 > encodedKey2 ? 1 : encodedKey1 === encodedKey2 ? 0 : -1;

    if (CFG.DEBUG) {
        // verify that the keys encoded correctly
        let decodedKey1 = Key.decode(encodedKey1);
        let decodedKey2 = Key.decode(encodedKey2);
        if (typeof key1 === 'object') {
            key1 = JSON.stringify(key1);
            decodedKey1 = JSON.stringify(decodedKey1);
        }
        if (typeof key2 === 'object') {
            key2 = JSON.stringify(key2);
            decodedKey2 = JSON.stringify(decodedKey2);
        }

        // encoding/decoding mismatches are usually due to a loss of floating-point precision
        if (decodedKey1 !== key1) {
            console.warn(key1 + ' was incorrectly encoded as ' + decodedKey1);
        }
        if (decodedKey2 !== key2) {
            console.warn(key2 + ' was incorrectly encoded as ' + decodedKey2);
        }
    }

    return result;
}

IDBFactory.prototype.cmp = cmp;

/**
* NON-STANDARD!! (Also may return outdated information)
* @link https://www.w3.org/Bugs/Public/show_bug.cgi?id=16137
* @link http://lists.w3.org/Archives/Public/public-webapps/2011JulSep/1537.html
*/
IDBFactory.prototype.webkitGetDatabaseNames = function () {
    let calledDbCreateError = false;
    function dbGetDatabaseNamesError (...args /* tx, err */) {
        if (calledDbCreateError) {
            return;
        }
        const err = findError(args);
        calledDbCreateError = true;
        const evt = createEvent('error', args);
        req.__readyState = 'done';
        req.__error = err || DOMException;
        req.dispatchEvent(evt);
    }
    const req = new IDBRequest();
    createSysDB(function () {
        sysdb.transaction(function (tx) {
            tx.executeSql('SELECT name FROM dbVersions', [], function (tx, data) {
                const dbNames = new util.StringList();
                for (let i = 0; i < data.rows.length; i++) {
                    dbNames.push(data.rows.item(i).name);
                }
            }, dbGetDatabaseNamesError);
        }, dbGetDatabaseNamesError);
    }, dbGetDatabaseNamesError);
    const e = createEvent('success');
    req.dispatchEvent(e);
    return req;
};

IDBFactory.prototype.toString = function () {
    return '[object IDBFactory]';
};

const shimIndexedDB = new IDBFactory();
export {IDBFactory, cmp, shimIndexedDB};
<commit_msg>Clarify code comment<commit_after>import {createEvent, ShimEvent, ProxyPolyfill, IDBVersionChangeEvent} from './Event.js';
import {findError, createDOMException, DOMException} from './DOMException.js';
import {IDBOpenDBRequest, IDBRequest} from './IDBRequest.js';
import * as util from './util.js';
import Key from './Key.js';
import IDBTransaction from './IDBTransaction.js';
import IDBDatabase from './IDBDatabase.js';
import CFG from './CFG.js';

let sysdb;

/**
 * Craetes the sysDB to keep track of version numbers for databases
 **/
function createSysDB (success, failure) {
    function sysDbCreateError (...args /* tx, err */) {
        const err = findError(args);
        CFG.DEBUG && console.log('Error in sysdb transaction - when creating dbVersions', err);
        failure(err);
    }

    if (sysdb) {
        success();
    } else {
        sysdb = CFG.win.openDatabase('__sysdb__.sqlite', 1, 'System Database', CFG.DEFAULT_DB_SIZE);
        sysdb.transaction(function (tx) {
            tx.executeSql('CREATE TABLE IF NOT EXISTS dbVersions (name VARCHAR(255), version INT);', [], success, sysDbCreateError);
        }, sysDbCreateError);
    }
}

/**
 * IDBFactory Class
 * https://w3c.github.io/IndexedDB/#idl-def-IDBFactory
 * @constructor
 */
function IDBFactory () {
    this.modules = {DOMException, Event: typeof Event !== 'undefined' ? Event : ShimEvent, ShimEvent, ProxyPolyfill, IDBFactory};
}

/**
 * The IndexedDB Method to create a new database and return the DB
 * @param {string} name
 * @param {number} version
 */
IDBFactory.prototype.open = function (name, version) {
    const req = new IDBOpenDBRequest();
    let calledDbCreateError = false;

    if (arguments.length === 0) {
        throw new TypeError('Database name is required');
    } else if (arguments.length >= 2) {
        version = Number(version);
        if (isNaN(version) || !isFinite(version) ||
            version >= 0x20000000000000 || // 2 ** 53
            version < 1) { // The spec only mentions version==0 as throwing, but W3C tests fail with these
            throw new TypeError('Invalid database version: ' + version);
        }
    }
    name = String(name); // cast to a string

    function dbCreateError (...args /* tx, err */) {
        if (calledDbCreateError) {
            return;
        }
        const err = findError(args);
        calledDbCreateError = true;
        const evt = createEvent('error', args);
        req.__readyState = 'done';
        req.__error = err || DOMException;
        req.dispatchEvent(evt);
    }

    function openDB (oldVersion) {
        const db = CFG.win.openDatabase(util.escapeDatabaseName(name), 1, name, CFG.DEFAULT_DB_SIZE);
        req.__readyState = 'done';
        if (version === undefined) {
            version = oldVersion || 1;
        }
        if (oldVersion > version) {
            const err = createDOMException('VersionError', 'An attempt was made to open a database using a lower version than the existing version.', version);
            dbCreateError(err);
            return;
        }

        db.transaction(function (tx) {
            tx.executeSql('CREATE TABLE IF NOT EXISTS __sys__ (name VARCHAR(255), keyPath VARCHAR(255), autoInc BOOLEAN, indexList BLOB, currNum INTEGER)', [], function () {
                tx.executeSql('SELECT * FROM __sys__', [], function (tx, data) {
                    req.__result = new IDBDatabase(db, name, version, data);
                    if (oldVersion < version) {
                        // DB Upgrade in progress
                        sysdb.transaction(function (systx) {
                            systx.executeSql('UPDATE dbVersions SET version = ? WHERE name = ?', [version, name], function () {
                                const e = new IDBVersionChangeEvent('upgradeneeded', {oldVersion, newVersion: version});
                                req.__transaction = req.result.__versionTransaction = new IDBTransaction(req.result, req.result.objectStoreNames, 'versionchange');
                                req.transaction.__addToTransactionQueue(function onupgradeneeded (tx, args, success) {
                                    req.dispatchEvent(e);
                                    success();
                                });
                                req.transaction.on__beforecomplete = function () {
                                    req.result.__versionTransaction = null;
                                };
                                req.transaction.on__complete = function () {
                                    if (req.__result.__closed) {
                                        return;
                                    }
                                    req.__transaction = null;
                                    const e = createEvent('success');
                                    req.dispatchEvent(e);
                                };
                            }, dbCreateError);
                        }, dbCreateError);
                    } else {
                        const e = createEvent('success');
                        req.dispatchEvent(e);
                    }
                }, dbCreateError);
            }, dbCreateError);
        }, dbCreateError);
    }

    createSysDB(function () {
        sysdb.transaction(function (tx) {
            tx.executeSql('SELECT * FROM dbVersions WHERE name = ?', [name], function (tx, data) {
                if (data.rows.length === 0) {
                    // Database with this name does not exist
                    tx.executeSql('INSERT INTO dbVersions VALUES (?,?)', [name, version || 1], function () {
                        openDB(0);
                    }, dbCreateError);
                } else {
                    openDB(data.rows.item(0).version);
                }
            }, dbCreateError);
        }, dbCreateError);
    }, dbCreateError);

    return req;
};

/**
 * Deletes a database
 * @param {string} name
 * @returns {IDBOpenDBRequest}
 */
IDBFactory.prototype.deleteDatabase = function (name) {
    const req = new IDBOpenDBRequest();
    let calledDBError = false;
    let version = null;

    if (arguments.length === 0) {
        throw new TypeError('Database name is required');
    }
    name = String(name); // cast to a string

    function dbError (...args /* tx, err */) {
        if (calledDBError) {
            return;
        }
        const err = findError(args);
        req.__readyState = 'done';
        req.__error = err || DOMException;
        const e = createEvent('error', args);
        req.dispatchEvent(e);
        calledDBError = true;
    }

    function deleteFromDbVersions () {
        sysdb.transaction(function (systx) {
            systx.executeSql('DELETE FROM dbVersions WHERE name = ? ', [name], function () {
                req.__result = undefined;
                const e = new IDBVersionChangeEvent('success', {oldVersion: version, newVersion: null});
                req.dispatchEvent(e);
            }, dbError);
        }, dbError);
    }

    createSysDB(function () {
        sysdb.transaction(function (systx) {
            systx.executeSql('SELECT * FROM dbVersions WHERE name = ?', [name], function (tx, data) {
                if (data.rows.length === 0) {
                    req.__result = undefined;
                    const e = new IDBVersionChangeEvent('success', {oldVersion: version, newVersion: null});
                    req.dispatchEvent(e);
                    return;
                }
                version = data.rows.item(0).version;
                const db = CFG.win.openDatabase(util.escapeDatabaseName(name), 1, name, CFG.DEFAULT_DB_SIZE);
                db.transaction(function (tx) {
                    tx.executeSql('SELECT * FROM __sys__', [], function (tx, data) {
                        const tables = data.rows;
                        (function deleteTables (i) {
                            if (i >= tables.length) {
                                // If all tables are deleted, delete the housekeeping tables
                                tx.executeSql('DROP TABLE IF EXISTS __sys__', [], function () {
                                    // Finally, delete the record for this DB from sysdb
                                    deleteFromDbVersions();
                                }, dbError);
                            } else {
                                // Delete all tables in this database, maintained in the sys table
                                tx.executeSql('DROP TABLE ' + util.escapeStore(tables.item(i).name), [], function () {
                                    deleteTables(i + 1);
                                }, function () {
                                    deleteTables(i + 1);
                                });
                            }
                        }(0));
                    }, function (e) {
                        // __sysdb table does not exist, but that does not mean delete did not happen
                        deleteFromDbVersions();
                    });
                });
            }, dbError);
        }, dbError);
    }, dbError);

    return req;
};

/**
 * Compares two keys
 * @param key1
 * @param key2
 * @returns {number}
 */
function cmp (key1, key2) {
    if (arguments.length < 2) {
        throw new TypeError('You must provide two keys to be compared');
    }

    Key.convertValueToKey(key1);
    Key.convertValueToKey(key2);
    const encodedKey1 = Key.encode(key1);
    const encodedKey2 = Key.encode(key2);
    const result = encodedKey1 > encodedKey2 ? 1 : encodedKey1 === encodedKey2 ? 0 : -1;

    if (CFG.DEBUG) {
        // verify that the keys encoded correctly
        let decodedKey1 = Key.decode(encodedKey1);
        let decodedKey2 = Key.decode(encodedKey2);
        if (typeof key1 === 'object') {
            key1 = JSON.stringify(key1);
            decodedKey1 = JSON.stringify(decodedKey1);
        }
        if (typeof key2 === 'object') {
            key2 = JSON.stringify(key2);
            decodedKey2 = JSON.stringify(decodedKey2);
        }

        // encoding/decoding mismatches are usually due to a loss of floating-point precision
        if (decodedKey1 !== key1) {
            console.warn(key1 + ' was incorrectly encoded as ' + decodedKey1);
        }
        if (decodedKey2 !== key2) {
            console.warn(key2 + ' was incorrectly encoded as ' + decodedKey2);
        }
    }

    return result;
}

IDBFactory.prototype.cmp = cmp;

/**
* NON-STANDARD!! (Also may return outdated information if a database has since been deleted)
* @link https://www.w3.org/Bugs/Public/show_bug.cgi?id=16137
* @link http://lists.w3.org/Archives/Public/public-webapps/2011JulSep/1537.html
*/
IDBFactory.prototype.webkitGetDatabaseNames = function () {
    let calledDbCreateError = false;
    function dbGetDatabaseNamesError (...args /* tx, err */) {
        if (calledDbCreateError) {
            return;
        }
        const err = findError(args);
        calledDbCreateError = true;
        const evt = createEvent('error', args);
        req.__readyState = 'done';
        req.__error = err || DOMException;
        req.dispatchEvent(evt);
    }
    const req = new IDBRequest();
    createSysDB(function () {
        sysdb.transaction(function (tx) {
            tx.executeSql('SELECT name FROM dbVersions', [], function (tx, data) {
                const dbNames = new util.StringList();
                for (let i = 0; i < data.rows.length; i++) {
                    dbNames.push(data.rows.item(i).name);
                }
            }, dbGetDatabaseNamesError);
        }, dbGetDatabaseNamesError);
    }, dbGetDatabaseNamesError);
    const e = createEvent('success');
    req.dispatchEvent(e);
    return req;
};

IDBFactory.prototype.toString = function () {
    return '[object IDBFactory]';
};

const shimIndexedDB = new IDBFactory();
export {IDBFactory, cmp, shimIndexedDB};
<|endoftext|>


<commit_before>var
  Promise = require('bluebird'),
  os = require('os'),
  _ = require('lodash'),
  BadRequestError = require('kuzzle-common-objects').Errors.badRequestError,
  ResponseObject = require('kuzzle-common-objects').Models.responseObject;

/**
 * @param {Kuzzle} kuzzle
 * @constructor
 */
function ReadController (kuzzle) {
  var engine = kuzzle.services.list.storageEngine;

  this.search = function readSearch (requestObject) {
    var modifiedRequestObject = null;

    return new Promise((resolve, reject) => {
      if (requestObject.index && typeof requestObject.index === 'string' && requestObject.index.split(',').length > 1) {
        return reject(new BadRequestError('Search on multiple indexes is not available.'));
      }

      resolve({});
    })
      .then(() => {
        return kuzzle.pluginsManager.trigger('data:beforeSearch', requestObject);
      })
      .then(newRequestObject => {
        modifiedRequestObject = newRequestObject;
        return engine.search(modifiedRequestObject);
      })
      .then(response => kuzzle.pluginsManager.trigger('data:afterSearch', new ResponseObject(modifiedRequestObject, response)));
  };

  this.get = function readGet (requestObject) {
    var modifiedRequestObject = null;

    return kuzzle.pluginsManager.trigger('data:beforeGet', requestObject)
      .then(newRequestObject => {
        modifiedRequestObject = newRequestObject;
        return engine.get(modifiedRequestObject);
      })
      .then(response => kuzzle.pluginsManager.trigger('data:afterGet', new ResponseObject(modifiedRequestObject, response)));
  };

  this.count = function readCount (requestObject) {
    var modifiedRequestObject = null;

    return kuzzle.pluginsManager.trigger('data:beforeCount', requestObject)
      .then(newRequestObject => {
        modifiedRequestObject = newRequestObject;
        return engine.count(modifiedRequestObject);
      })
      .then(response => kuzzle.pluginsManager.trigger('data:afterCount', new ResponseObject(requestObject, response)));
  };

  this.listCollections = function readListCollections (requestObject) {
    var
      type = requestObject.data.body.type || 'all',
      realtimeCollections = [],
      modifiedRequestObject = null;

    if (['all', 'stored', 'realtime'].indexOf(type) === -1) {
      return Promise.reject(
        new ResponseObject(requestObject, new BadRequestError('listCollections: unrecognized type argument: "' + type + '"'))
      );
    }

    return kuzzle.pluginsManager.trigger('data:beforeListCollections', requestObject)
      .then(newRequestObject => {
        modifiedRequestObject = newRequestObject;

        if (type === 'stored') {
          return engine.listCollections(modifiedRequestObject)
            .then(response => {
              response.type = type;
              response.collections = formatCollections(response);
              return kuzzle.pluginsManager.trigger('data:afterListCollections', new ResponseObject(modifiedRequestObject, paginateCollections(requestObject, response)));
            });
        }

        realtimeCollections = kuzzle.hotelClerk.getRealtimeCollections()
          .filter(collection => collection.index === requestObject.index)
          .map(collection => collection.name);

        if (type === 'realtime') {
          let realtimeResponse = {type, collections: {realtime: realtimeCollections}};

          realtimeResponse.collections = formatCollections(realtimeResponse);

          return kuzzle.pluginsManager.trigger('data:afterListCollections', new ResponseObject(modifiedRequestObject, paginateCollections(requestObject, realtimeResponse)));
        }

        return engine.listCollections(modifiedRequestObject)
          .then(response => {
            response.type = type;
            response.collections.realtime = realtimeCollections;
            response.collections = formatCollections(response);
            response = paginateCollections(modifiedRequestObject, response);

            return kuzzle.pluginsManager.trigger('data:afterListCollections', new ResponseObject(modifiedRequestObject, response));
          });
      });
  };

  this.now = function readNow (requestObject) {
    return kuzzle.pluginsManager.trigger('data:beforeNow', requestObject)
      .then(newRequestObject => kuzzle.pluginsManager.trigger('data:afterNow', new ResponseObject(newRequestObject, {now: Date.now()})));
  };

  this.listIndexes = function readListIndexes (requestObject) {
    var modifiedRequestObject = null;

    return kuzzle.pluginsManager.trigger('data:beforeListIndexes', requestObject)
      .then(newRequestObject => {
        modifiedRequestObject = newRequestObject;
        return engine.listIndexes(modifiedRequestObject);
      })
      .then(response => kuzzle.pluginsManager.trigger('data:afterListIndexes', new ResponseObject(modifiedRequestObject, response)));
  };

  this.serverInfo = function readServerInfo (requestObject) {
    var
      serviceName,
      response = {
        kuzzle: {
          version: kuzzle.config.version,
          api: {
            version: kuzzle.config.apiVersion,
            routes: {}
          },
          nodeVersion: process.version,
          memoryUsed: process.memoryUsage().rss,
          uptime: process.uptime() + 's',
          plugins: {},
          system: {
            memory: {
              total: os.totalmem(),
              free: os.freemem()
            },
            cpus: os.cpus()
          }
        },
        services: {}
      };

    Object.keys(kuzzle.funnel.controllers).forEach(controller => {
      var actionList = {};

      Object.keys(kuzzle.funnel.controllers[controller]).forEach(action => {
        var routeDescription = {};

        if (typeof kuzzle.funnel.controllers[controller][action] === 'function') {
          actionList[action] = {name: action};

          // resolve associated http route for each actions
          Object.keys(kuzzle.config.httpRoutes).forEach((key) => {
            var route = kuzzle.config.httpRoutes[key];

            if ((route.controller === controller || controller === 'memoryStorage' && route.controller === 'ms') && route.action === action) {
              routeDescription = route;
              return false;
            }
          });

          if (routeDescription.url) {
            actionList[action].route = '/api/' + kuzzle.config.apiVersion + routeDescription.url;
            actionList[action].method = routeDescription.verb;
          }
        }
      });

      if (Object.keys(actionList).length > 0) {
        response.kuzzle.api.routes[controller] = actionList;
      }
    });

    Object.keys(kuzzle.pluginsManager.plugins).forEach(plugin => {
      var
        pluginInfo = kuzzle.pluginsManager.plugins[plugin],
        p = {
          name: pluginInfo.name,
          version: pluginInfo.version,
          activated: pluginInfo.activated,
          hooks: [],
          pipes: [],
          controllers: [],
          routes: []
        };

      if (pluginInfo.object.hasOwnProperty('hooks')) {
        p.hooks = _.uniq(Object.keys(pluginInfo.object.hooks));
      }

      if (pluginInfo.object.hasOwnProperty('pipes')) {
        p.pipes = _.uniq(Object.keys(pluginInfo.object.pipes));
      }

      if (pluginInfo.object.hasOwnProperty('controllers')) {
        p.controllers = _.uniq(Object.keys(pluginInfo.object.controllers));
        p.controllers = p.controllers.map((item) => pluginInfo.name + '/' + item);
      }

      if (pluginInfo.object.hasOwnProperty('routes')) {
        p.routes = _.uniq(pluginInfo.object.routes);
      }

      response.kuzzle.plugins[plugin] = p;
    });

    return Object.keys(kuzzle.services.list).reduce((previous, current) => {
      return previous.then(serviceInfo => {
        if (serviceInfo) {
          response.services[serviceName] = serviceInfo;
        }

        serviceName = current;

        if (kuzzle.services.list[current].getInfos) {
          return kuzzle.services.list[current].getInfos();
        }

        return Promise.resolve();
      });
    }, Promise.resolve())
    .then(serviceInfo => {
      if (serviceInfo) {
        response.services[serviceName] = serviceInfo;
      }

      return kuzzle.pluginsManager.trigger('data:afterServerInfo', new ResponseObject(requestObject, {serverInfo: response}));
    });
  };
}

module.exports = ReadController;

/**
 * Uses from and size to paginate response results
 * If type is "all", stored collections are prioritary
 *
 * @param {RequestObject} requestObject
 * @param {*} response
 * @returns {*}
 */
function paginateCollections (requestObject, response) {
  if (requestObject.data.body.from || requestObject.data.body.size) {
    response.from = Number.parseInt(requestObject.data.body.from) || 0;

    if (requestObject.data.body.size) {
      response.size = Number.parseInt(requestObject.data.body.size);

      response.collections = response.collections.slice(response.from, response.from + response.size);
    }
    else {
      response.collections = response.collections.slice(response.from);
    }
  }

  return response;
}

function formatCollections (response) {
  var
    collections = [];
  if (response.collections.realtime) {
    response.collections.realtime.forEach(item => {
      collections.push({name: item, type: 'realtime'});
    });
  }

  if (response.collections.stored) {
    response.collections.stored.forEach(item => {
      collections.push({name: item, type: 'stored'});
    });
  }

  return collections.sort((a, b) => {
    if (a.name > b.name) {
      return 1;
    }
    else if (a.name < b.name) {
      return -1;
    }

    return 0;
  });
}
<commit_msg>Use strict with let<commit_after>var
  Promise = require('bluebird'),
  os = require('os'),
  _ = require('lodash'),
  BadRequestError = require('kuzzle-common-objects').Errors.badRequestError,
  ResponseObject = require('kuzzle-common-objects').Models.responseObject;

/**
 * @param {Kuzzle} kuzzle
 * @constructor
 */
function ReadController (kuzzle) {
  var engine = kuzzle.services.list.storageEngine;

  this.search = function readSearch (requestObject) {
    var modifiedRequestObject = null;

    return new Promise((resolve, reject) => {
      if (requestObject.index && typeof requestObject.index === 'string' && requestObject.index.split(',').length > 1) {
        return reject(new BadRequestError('Search on multiple indexes is not available.'));
      }

      resolve({});
    })
      .then(() => {
        return kuzzle.pluginsManager.trigger('data:beforeSearch', requestObject);
      })
      .then(newRequestObject => {
        modifiedRequestObject = newRequestObject;
        return engine.search(modifiedRequestObject);
      })
      .then(response => kuzzle.pluginsManager.trigger('data:afterSearch', new ResponseObject(modifiedRequestObject, response)));
  };

  this.get = function readGet (requestObject) {
    var modifiedRequestObject = null;

    return kuzzle.pluginsManager.trigger('data:beforeGet', requestObject)
      .then(newRequestObject => {
        modifiedRequestObject = newRequestObject;
        return engine.get(modifiedRequestObject);
      })
      .then(response => kuzzle.pluginsManager.trigger('data:afterGet', new ResponseObject(modifiedRequestObject, response)));
  };

  this.count = function readCount (requestObject) {
    var modifiedRequestObject = null;

    return kuzzle.pluginsManager.trigger('data:beforeCount', requestObject)
      .then(newRequestObject => {
        modifiedRequestObject = newRequestObject;
        return engine.count(modifiedRequestObject);
      })
      .then(response => kuzzle.pluginsManager.trigger('data:afterCount', new ResponseObject(requestObject, response)));
  };

  this.listCollections = function readListCollections (requestObject) {
    var
      type = requestObject.data.body.type || 'all',
      realtimeCollections = [],
      modifiedRequestObject = null;

    if (['all', 'stored', 'realtime'].indexOf(type) === -1) {
      return Promise.reject(
        new ResponseObject(requestObject, new BadRequestError('listCollections: unrecognized type argument: "' + type + '"'))
      );
    }

    return kuzzle.pluginsManager.trigger('data:beforeListCollections', requestObject)
      .then(newRequestObject => {
        'use strict';
        modifiedRequestObject = newRequestObject;

        if (type === 'stored') {
          return engine.listCollections(modifiedRequestObject)
            .then(response => {
              response.type = type;
              response.collections = formatCollections(response);
              return kuzzle.pluginsManager.trigger('data:afterListCollections', new ResponseObject(modifiedRequestObject, paginateCollections(requestObject, response)));
            });
        }

        realtimeCollections = kuzzle.hotelClerk.getRealtimeCollections()
          .filter(collection => collection.index === requestObject.index)
          .map(collection => collection.name);

        if (type === 'realtime') {
          let realtimeResponse = {type, collections: {realtime: realtimeCollections}};

          realtimeResponse.collections = formatCollections(realtimeResponse);

          return kuzzle.pluginsManager.trigger('data:afterListCollections', new ResponseObject(modifiedRequestObject, paginateCollections(requestObject, realtimeResponse)));
        }

        return engine.listCollections(modifiedRequestObject)
          .then(response => {
            response.type = type;
            response.collections.realtime = realtimeCollections;
            response.collections = formatCollections(response);
            response = paginateCollections(modifiedRequestObject, response);

            return kuzzle.pluginsManager.trigger('data:afterListCollections', new ResponseObject(modifiedRequestObject, response));
          });
      });
  };

  this.now = function readNow (requestObject) {
    return kuzzle.pluginsManager.trigger('data:beforeNow', requestObject)
      .then(newRequestObject => kuzzle.pluginsManager.trigger('data:afterNow', new ResponseObject(newRequestObject, {now: Date.now()})));
  };

  this.listIndexes = function readListIndexes (requestObject) {
    var modifiedRequestObject = null;

    return kuzzle.pluginsManager.trigger('data:beforeListIndexes', requestObject)
      .then(newRequestObject => {
        modifiedRequestObject = newRequestObject;
        return engine.listIndexes(modifiedRequestObject);
      })
      .then(response => kuzzle.pluginsManager.trigger('data:afterListIndexes', new ResponseObject(modifiedRequestObject, response)));
  };

  this.serverInfo = function readServerInfo (requestObject) {
    var
      serviceName,
      response = {
        kuzzle: {
          version: kuzzle.config.version,
          api: {
            version: kuzzle.config.apiVersion,
            routes: {}
          },
          nodeVersion: process.version,
          memoryUsed: process.memoryUsage().rss,
          uptime: process.uptime() + 's',
          plugins: {},
          system: {
            memory: {
              total: os.totalmem(),
              free: os.freemem()
            },
            cpus: os.cpus()
          }
        },
        services: {}
      };

    Object.keys(kuzzle.funnel.controllers).forEach(controller => {
      var actionList = {};

      Object.keys(kuzzle.funnel.controllers[controller]).forEach(action => {
        var routeDescription = {};

        if (typeof kuzzle.funnel.controllers[controller][action] === 'function') {
          actionList[action] = {name: action};

          // resolve associated http route for each actions
          Object.keys(kuzzle.config.httpRoutes).forEach((key) => {
            var route = kuzzle.config.httpRoutes[key];

            if ((route.controller === controller || controller === 'memoryStorage' && route.controller === 'ms') && route.action === action) {
              routeDescription = route;
              return false;
            }
          });

          if (routeDescription.url) {
            actionList[action].route = '/api/' + kuzzle.config.apiVersion + routeDescription.url;
            actionList[action].method = routeDescription.verb;
          }
        }
      });

      if (Object.keys(actionList).length > 0) {
        response.kuzzle.api.routes[controller] = actionList;
      }
    });

    Object.keys(kuzzle.pluginsManager.plugins).forEach(plugin => {
      var
        pluginInfo = kuzzle.pluginsManager.plugins[plugin],
        p = {
          name: pluginInfo.name,
          version: pluginInfo.version,
          activated: pluginInfo.activated,
          hooks: [],
          pipes: [],
          controllers: [],
          routes: []
        };

      if (pluginInfo.object.hasOwnProperty('hooks')) {
        p.hooks = _.uniq(Object.keys(pluginInfo.object.hooks));
      }

      if (pluginInfo.object.hasOwnProperty('pipes')) {
        p.pipes = _.uniq(Object.keys(pluginInfo.object.pipes));
      }

      if (pluginInfo.object.hasOwnProperty('controllers')) {
        p.controllers = _.uniq(Object.keys(pluginInfo.object.controllers));
        p.controllers = p.controllers.map((item) => pluginInfo.name + '/' + item);
      }

      if (pluginInfo.object.hasOwnProperty('routes')) {
        p.routes = _.uniq(pluginInfo.object.routes);
      }

      response.kuzzle.plugins[plugin] = p;
    });

    return Object.keys(kuzzle.services.list).reduce((previous, current) => {
      return previous.then(serviceInfo => {
        if (serviceInfo) {
          response.services[serviceName] = serviceInfo;
        }

        serviceName = current;

        if (kuzzle.services.list[current].getInfos) {
          return kuzzle.services.list[current].getInfos();
        }

        return Promise.resolve();
      });
    }, Promise.resolve())
    .then(serviceInfo => {
      if (serviceInfo) {
        response.services[serviceName] = serviceInfo;
      }

      return kuzzle.pluginsManager.trigger('data:afterServerInfo', new ResponseObject(requestObject, {serverInfo: response}));
    });
  };
}

module.exports = ReadController;

/**
 * Uses from and size to paginate response results
 * If type is "all", stored collections are prioritary
 *
 * @param {RequestObject} requestObject
 * @param {*} response
 * @returns {*}
 */
function paginateCollections (requestObject, response) {
  if (requestObject.data.body.from || requestObject.data.body.size) {
    response.from = Number.parseInt(requestObject.data.body.from) || 0;

    if (requestObject.data.body.size) {
      response.size = Number.parseInt(requestObject.data.body.size);

      response.collections = response.collections.slice(response.from, response.from + response.size);
    }
    else {
      response.collections = response.collections.slice(response.from);
    }
  }

  return response;
}

function formatCollections (response) {
  var
    collections = [];
  if (response.collections.realtime) {
    response.collections.realtime.forEach(item => {
      collections.push({name: item, type: 'realtime'});
    });
  }

  if (response.collections.stored) {
    response.collections.stored.forEach(item => {
      collections.push({name: item, type: 'stored'});
    });
  }

  return collections.sort((a, b) => {
    if (a.name > b.name) {
      return 1;
    }
    else if (a.name < b.name) {
      return -1;
    }

    return 0;
  });
}
<|endoftext|>


<commit_before>//i18n
//
function _t(s) {
  return s;
}

var i18n = {
  // format('hello, {0}', 'rambo') -> "hello, rambo"
  format: function (str) {
    for(var i = 1; i < arguments.length; ++i) {
      var attrs = arguments[i];
      for(var attr in attrs) {
        str = str.replace(RegExp('\\{' + attr + '\\}', 'g'), attrs[attr]);
      }
    }
    return str;
  }
};

cdb.config.setUrlVersion = function(modelName, method, v) {
  cdb.config.set(modelName + '_' + method + '_url_version', v || 'v1');
}

cdb.config.urlVersion = function(modelName, method, defaultVersion) {
  method = method || ''
  var version = cdb.config.get(modelName + "_" + method + '_url_version')
  return version || defaultVersion || 'v1';
}

// return prefixUrl for all the queries to the rest api
cdb.config.prefixUrl = function() {
  return this.get('url_prefix') || '';
};

// if the prefixUrl is like http://host.com/u/o returns
// /u/o part
cdb.config.prefixUrlPathname = function() {
  var prefix = this.prefixUrl();
  if (prefix !== '') {
    try {
      var url = new URL(this.prefixUrl()).pathname;
      // remove trailing slash
      return url.replace(/\/$/, '');
    } catch(e) {
      // not an url
    }
  }
  return prefix;
};

(function() {

  // helper functions needed frmo backbone (they are not exported)
  var getValue = function(object, prop, method) {
    if (!(object && object[prop])) return null;
    return _.isFunction(object[prop]) ? object[prop](method) : object[prop];
  };

  // Throw an error when a URL is needed, and none is supplied.
  var urlError = function() {
    throw new Error('A "url" property or function must be specified');
  };

  // backbone.sync replacement to control url prefix
  Backbone.originalSync = Backbone.sync;
  Backbone.sync = function(method, model, options) {
    var url = options.url || getValue(model, 'url', method) || urlError();
    // prefix if http is not present
    var absoluteUrl = url.indexOf('http') === 0 || url.indexOf("//") === 0;
    if (!absoluteUrl) {
      options.url = cdb.config.prefixUrl() + url;
    } else {
      options.url = url;
    }
    if (method !== 'read') {
      // remove everything related
      if (model.surrogateKeys) {
        Backbone.cachedSync.invalidateSurrogateKeys(getValue(model, 'surrogateKeys'));
      }
    }
    return Backbone.originalSync(method, model, options);
  };

  // this method returns a cached version of backbone sync
  // take a look at https://github.com/teambox/backbone.memoized_sync/blob/master/backbone.memoized_sync.js
  // this is the same concept but implemented as a wrapper for ``Backbone.sync``
  // usage:
  // initialize: function() {
  //    this.sync = Backbone.cachedSync(this.user_name);
  // }
  Backbone.cachedSync = function(namespace, sync) {

    if (!namespace) {
      throw new Error("cachedSync needs a namespace as argument");
    }

    var surrogateKey = namespace;
    var session = window.user_data && window.user_data.username;
    // no user session, no cache
    // there should be a session to have cache so we avoid
    // cache collision for someone with more than one account
    if (session) {
      namespace += "-" + session;
    } else {
      return Backbone.sync;
    }

    var namespaceKey = "cdb-cache/" + namespace;

    // saves all the localstore references to the namespace
    // inside localstore. It allows to remove all the references
    // at a time
    var index = {

      // return a list of references for the namespace
      _keys: function() {
        return JSON.parse(localStorage.getItem(namespaceKey) || '{}');
      },

      // add a new reference for the namespace
      add: function(key) {
        var keys = this._keys();
        keys[key] = +new Date();
        localStorage.setItem(namespaceKey, JSON.stringify(keys));
      },

      // remove all the references for the namespace
      invalidate: function() {
        var keys = this._keys();
        _.each(keys, function(v, k) {
          localStorage.removeItem(k);
        });
        localStorage.removeItem(namespaceKey);
      }

    }

    // localstore-like cache wrapper
    var cache = {

      setItem: function(key, value) {
        localStorage.setItem(key, value);
        index.add(key);
        return this;
      },

      // this is async in case the data needs to be compressed
      getItem: function(key, callback) {
        var val = localStorage.getItem(key);
        _.defer(function() {
          callback(val);
        });
      },

      removeItem: function(key) {
        localStorage.removeItem(key);
        index.invalidate();
      }
    }

    var cached = function(method, model, options) {
      var url = options.url || getValue(model, 'url') || urlError();
      var key = namespaceKey + "/" + url;

      if (method === 'read') {
        var success = options.success;
        var cachedValue = null;

        options.success = function(resp, status, xhr) {
           // if cached value is ok
           if (cachedValue && xhr.responseText === cachedValue) {
             return;
           }
           cache.setItem(key, xhr.responseText);
           success(resp, status, xhr);
        }

        cache.getItem(key, function(val) {
          cachedValue = val;
          if (val) {
            success(JSON.parse(val), "success");
          }
        });
      } else {
        cache.removeItem(key);
      }
      return (sync || Backbone.sync)(method, model, options);
    }

    // create a public function to invalidate all the namespace
    // items
    cached.invalidate = function() {
      index.invalidate();
    }

    // for testing and debugging porpuposes
    cached.cache = cache;

    // have a global namespace -> sync function in order to avoid invalidation
    Backbone.cachedSync.surrogateKeys[surrogateKey] = cached;

    return cached;
  }

  Backbone.cachedSync.surrogateKeys = {};

  Backbone.cachedSync.invalidateSurrogateKeys = function(keys) {
    _.each(keys, function(k) {
      var s = Backbone.cachedSync.surrogateKeys[k];
      if (s) {
        s.invalidate();
      } else {
        cdb.log.debug("surrogate key not found: " + k);
      }
    });
  }

})();

Backbone.syncAbort = function() {
  var self = arguments[1];
  if (self._xhr) {
    self._xhr.abort();
  }
  self._xhr = Backbone.sync.apply(this, arguments);
  self._xhr.always(function() { self._xhr = null; });
  return self._xhr;
};

Backbone.delayedSaveSync = function(sync, delay) {
  var dsync = _.debounce(sync, delay);
  return function(method, model, options) {
    if (method === 'create' || method === 'update') {
      return dsync(method, model, options);
    } else {
      return sync(method, model, options);
    }
  }
}

Backbone.saveAbort = function() {
  var self = this;
  if (this._saving && this._xhr) {
    this._xhr.abort();
  }
  this._saving = true;
  var xhr = Backbone.Model.prototype.save.apply(this, arguments);
  this._xhr = xhr;
  xhr.always(function() { self._saving = false; });
  return xhr;
};

cdb.admin = {};
cdb.common = {};
cdb.admin.dashboard = {};
cdb.forms = {};
cdb.open = {};
cdb.dashboard = {};

// TODO: this routing code should be removed
function applyPatchNewVisualizationUrl() {

  // collections only needs read method
  cdb.config.setUrlVersion('visualizations', 'read', 'v1_1')

  // models can have: read, update, delete and create
  cdb.config.setUrlVersion('visualization', 'read', 'v1_1')
  //cdb.config.setUrlVersion('visualization', 'update', 'v1_1')
  cdb.config.setUrlVersion('like', 'read', 'v1_1')
  cdb.config.setUrlVersion('vizjson', 'read', 'v2_1')
  //cdb.config.setUrlVersion('overlays', 'read', 'v1_1')
}

function applyPatchNewTableUrls() {
  cdb.config.setUrlVersion('table', 'read', 'v1_1');
  cdb.config.setUrlVersion('record', 'read', 'v1_1');
  cdb.config.setUrlVersion('column', 'read', 'v1_1');
}

function applyPatchNewImportUrl() {
  cdb.config.setUrlVersion('import', 'read', 'v1_1');s
}<commit_msg>Fix typo<commit_after>//i18n
//
function _t(s) {
  return s;
}

var i18n = {
  // format('hello, {0}', 'rambo') -> "hello, rambo"
  format: function (str) {
    for(var i = 1; i < arguments.length; ++i) {
      var attrs = arguments[i];
      for(var attr in attrs) {
        str = str.replace(RegExp('\\{' + attr + '\\}', 'g'), attrs[attr]);
      }
    }
    return str;
  }
};

cdb.config.setUrlVersion = function(modelName, method, v) {
  cdb.config.set(modelName + '_' + method + '_url_version', v || 'v1');
}

cdb.config.urlVersion = function(modelName, method, defaultVersion) {
  method = method || ''
  var version = cdb.config.get(modelName + "_" + method + '_url_version')
  return version || defaultVersion || 'v1';
}

// return prefixUrl for all the queries to the rest api
cdb.config.prefixUrl = function() {
  return this.get('url_prefix') || '';
};

// if the prefixUrl is like http://host.com/u/o returns
// /u/o part
cdb.config.prefixUrlPathname = function() {
  var prefix = this.prefixUrl();
  if (prefix !== '') {
    try {
      var url = new URL(this.prefixUrl()).pathname;
      // remove trailing slash
      return url.replace(/\/$/, '');
    } catch(e) {
      // not an url
    }
  }
  return prefix;
};

(function() {

  // helper functions needed frmo backbone (they are not exported)
  var getValue = function(object, prop, method) {
    if (!(object && object[prop])) return null;
    return _.isFunction(object[prop]) ? object[prop](method) : object[prop];
  };

  // Throw an error when a URL is needed, and none is supplied.
  var urlError = function() {
    throw new Error('A "url" property or function must be specified');
  };

  // backbone.sync replacement to control url prefix
  Backbone.originalSync = Backbone.sync;
  Backbone.sync = function(method, model, options) {
    var url = options.url || getValue(model, 'url', method) || urlError();
    // prefix if http is not present
    var absoluteUrl = url.indexOf('http') === 0 || url.indexOf("//") === 0;
    if (!absoluteUrl) {
      options.url = cdb.config.prefixUrl() + url;
    } else {
      options.url = url;
    }
    if (method !== 'read') {
      // remove everything related
      if (model.surrogateKeys) {
        Backbone.cachedSync.invalidateSurrogateKeys(getValue(model, 'surrogateKeys'));
      }
    }
    return Backbone.originalSync(method, model, options);
  };

  // this method returns a cached version of backbone sync
  // take a look at https://github.com/teambox/backbone.memoized_sync/blob/master/backbone.memoized_sync.js
  // this is the same concept but implemented as a wrapper for ``Backbone.sync``
  // usage:
  // initialize: function() {
  //    this.sync = Backbone.cachedSync(this.user_name);
  // }
  Backbone.cachedSync = function(namespace, sync) {

    if (!namespace) {
      throw new Error("cachedSync needs a namespace as argument");
    }

    var surrogateKey = namespace;
    var session = window.user_data && window.user_data.username;
    // no user session, no cache
    // there should be a session to have cache so we avoid
    // cache collision for someone with more than one account
    if (session) {
      namespace += "-" + session;
    } else {
      return Backbone.sync;
    }

    var namespaceKey = "cdb-cache/" + namespace;

    // saves all the localstore references to the namespace
    // inside localstore. It allows to remove all the references
    // at a time
    var index = {

      // return a list of references for the namespace
      _keys: function() {
        return JSON.parse(localStorage.getItem(namespaceKey) || '{}');
      },

      // add a new reference for the namespace
      add: function(key) {
        var keys = this._keys();
        keys[key] = +new Date();
        localStorage.setItem(namespaceKey, JSON.stringify(keys));
      },

      // remove all the references for the namespace
      invalidate: function() {
        var keys = this._keys();
        _.each(keys, function(v, k) {
          localStorage.removeItem(k);
        });
        localStorage.removeItem(namespaceKey);
      }

    }

    // localstore-like cache wrapper
    var cache = {

      setItem: function(key, value) {
        localStorage.setItem(key, value);
        index.add(key);
        return this;
      },

      // this is async in case the data needs to be compressed
      getItem: function(key, callback) {
        var val = localStorage.getItem(key);
        _.defer(function() {
          callback(val);
        });
      },

      removeItem: function(key) {
        localStorage.removeItem(key);
        index.invalidate();
      }
    }

    var cached = function(method, model, options) {
      var url = options.url || getValue(model, 'url') || urlError();
      var key = namespaceKey + "/" + url;

      if (method === 'read') {
        var success = options.success;
        var cachedValue = null;

        options.success = function(resp, status, xhr) {
           // if cached value is ok
           if (cachedValue && xhr.responseText === cachedValue) {
             return;
           }
           cache.setItem(key, xhr.responseText);
           success(resp, status, xhr);
        }

        cache.getItem(key, function(val) {
          cachedValue = val;
          if (val) {
            success(JSON.parse(val), "success");
          }
        });
      } else {
        cache.removeItem(key);
      }
      return (sync || Backbone.sync)(method, model, options);
    }

    // create a public function to invalidate all the namespace
    // items
    cached.invalidate = function() {
      index.invalidate();
    }

    // for testing and debugging porpuposes
    cached.cache = cache;

    // have a global namespace -> sync function in order to avoid invalidation
    Backbone.cachedSync.surrogateKeys[surrogateKey] = cached;

    return cached;
  }

  Backbone.cachedSync.surrogateKeys = {};

  Backbone.cachedSync.invalidateSurrogateKeys = function(keys) {
    _.each(keys, function(k) {
      var s = Backbone.cachedSync.surrogateKeys[k];
      if (s) {
        s.invalidate();
      } else {
        cdb.log.debug("surrogate key not found: " + k);
      }
    });
  }

})();

Backbone.syncAbort = function() {
  var self = arguments[1];
  if (self._xhr) {
    self._xhr.abort();
  }
  self._xhr = Backbone.sync.apply(this, arguments);
  self._xhr.always(function() { self._xhr = null; });
  return self._xhr;
};

Backbone.delayedSaveSync = function(sync, delay) {
  var dsync = _.debounce(sync, delay);
  return function(method, model, options) {
    if (method === 'create' || method === 'update') {
      return dsync(method, model, options);
    } else {
      return sync(method, model, options);
    }
  }
}

Backbone.saveAbort = function() {
  var self = this;
  if (this._saving && this._xhr) {
    this._xhr.abort();
  }
  this._saving = true;
  var xhr = Backbone.Model.prototype.save.apply(this, arguments);
  this._xhr = xhr;
  xhr.always(function() { self._saving = false; });
  return xhr;
};

cdb.admin = {};
cdb.common = {};
cdb.admin.dashboard = {};
cdb.forms = {};
cdb.open = {};
cdb.dashboard = {};

// TODO: this routing code should be removed
function applyPatchNewVisualizationUrl() {

  // collections only needs read method
  cdb.config.setUrlVersion('visualizations', 'read', 'v1_1')

  // models can have: read, update, delete and create
  cdb.config.setUrlVersion('visualization', 'read', 'v1_1')
  //cdb.config.setUrlVersion('visualization', 'update', 'v1_1')
  cdb.config.setUrlVersion('like', 'read', 'v1_1')
  cdb.config.setUrlVersion('vizjson', 'read', 'v2_1')
  //cdb.config.setUrlVersion('overlays', 'read', 'v1_1')
}

function applyPatchNewTableUrls() {
  cdb.config.setUrlVersion('table', 'read', 'v1_1');
  cdb.config.setUrlVersion('record', 'read', 'v1_1');
  cdb.config.setUrlVersion('column', 'read', 'v1_1');
}

function applyPatchNewImportUrl() {
  cdb.config.setUrlVersion('import', 'read', 'v1_1');
}
<|endoftext|>


<commit_before>import animate;
import ui.View;
import ui.ImageView;
import ui.resource.Image as Image;

var Chance = require('chance');
var chance = new Chance();

// our images
var img_bee = new Image({
        url: "resources/images/bee.png"
    }),
    img_beehive = new Image({
        url: "resources/images/beehive.png"
    }),
    img_honeycomb = new Image({
        url: "resources/images/honeycomb.png"
    }),
    img_jar = new Image({
        url: "resources/images/honeyjar.png"
    });
var TileBee,TileBeehive,TileHoneycomb,TileJar;

exports = Class(ui.View, function(supr) {
    this.init = function(opts) {
        opts = merge(opts, {
            width: 100,
            height: 100
        });

        supr(this, 'init', [opts]);

       TileBee = new ui.ImageView({
       	superview: this,
       	width:this.width,
       	height: this.height,
       	image: img_bee
       });
       TileBeehive = new ui.ImageView({
       	superview: this,
       	width:this.width,
       	height: this.height,
       	image: img_beehive
       });
       TileHoneycomb = new ui.ImageView({
       	superview: this,
       	width:this.width,
       	height: this.height,
       	image: img_honeycomb
       });
       TileJar = new ui.ImageView({
       	superview: this,
       	width:this.width,
       	height: this.height,
       	image: img_jar
       });
    };

    this.build = function() {};
});

// Our levels
var level = {
    x: 250,
    y: 113,
    col: 9,
    rows: 9,
    tile_W: 40,
    tile_H: 40,
    tiles: [],
    selectedTile: {
        selected: false,
        col: 0,
        row: 0
    }
};

function InitalizeBoard() {
	// initialize the 2D Level Array
    for (var i = 0; i < level.col; i++) {
        level.tiles[i] = [];
        for (var j = 0; j < level.rows; j++) {
            //Define a tile type and shift type
            level.tiles[i][j] = {
                type: 0,
                shift: 0
            };
        }
    }
}

function CreateLevel() {
    var done = false;

    // keep generating levels unitl it is correct
    while (!done) {
        for (var i = 0; i < level.col; i++) {
            for (var j = 0; j < level.rows; j++) {
                level.tiles[i][j].type = GetRandomTile();
            }
        }

        ResolveClusters();
        FindMoves();

        if (moves.length > 0) {
            done = true;
        }
    }
}

function GetRandomTile() {
    var result = chance.integer({
        min: 0,
        max: 3
    });

    switch (result) {
        case 0: // 1st tile type
            return;
        case 1: // 2nd tile type
            return;
        case 2: //3rd tile type
            return;
        case 3: //4th tile type
            return;
    }
}

/* Removes Matches from the game
 * screen and insert new tiles
 */
function ResolveClusters() {
    FindClusters();

    while (clusters.length > 0) {
        RemoveClusters();
        ShiftTiles();
        FindClusters();
    }
}

//find clusters
var clusters = [];

function FindClusters() {
    // Reset clusters
    clusters = [];

    FindHorizontalClusters();
    FindVerticalClusters();
}

function FindHorizontalClusters() {
    // Find horizontal clusters
    for (var j = 0; j < level.rows; j++) {
        // Start with a single tile, cluster of 1
        var matchlength = 1;
        for (var i = 0; i < level.col; i++) {
            var checkcluster = false;

            if (i == level.columns - 1) {
                // Last tile
                checkcluster = true;
            } else {
                // Check the type of the next tile
                if (level.tiles[i][j].type == level.tiles[i + 1][j].type &&
                    level.tiles[i][j].type != -1) {
                    // Same type as the previous tile, increase matchlength
                    matchlength += 1;
                } else {
                    // Different type
                    checkcluster = true;
                }
            }

            // Check if there was a cluster
            if (checkcluster) {
                if (matchlength >= 3) {
                    // Found a horizontal cluster
                    clusters.push({
                        column: i + 1 - matchlength,
                        row: j,
                        length: matchlength,
                        horizontal: true
                    });
                }

                matchlength = 1;
            }
        }
    }
}

function FindVerticalClusters() {
    // Find vertical clusters
    for (var i = 0; i < level.col; i++) {
        // Start with a single tile, cluster of 1
        var matchlength = 1;
        for (var j = 0; j < level.rows; j++) {
            var checkcluster = false;

            if (j == level.rows - 1) {
                // Last tile
                checkcluster = true;
            } else {
                // Check the type of the next tile
                if (level.tiles[i][j].type == level.tiles[i][j + 1].type &&
                    level.tiles[i][j].type != -1) {
                    // Same type as the previous tile, increase matchlength
                    matchlength += 1;
                } else {
                    // Different type
                    checkcluster = true;
                }
            }

            // Check if there was a cluster
            if (checkcluster) {
                if (matchlength >= 3) {
                    // Found a vertical cluster
                    clusters.push({
                        column: i,
                        row: j + 1 - matchlength,
                        length: matchlength,
                        horizontal: false
                    });
                }

                matchlength = 1;
            }
        }
    }
}

// Available moves
var moves = [];

function Swap(x1, y1, x2, y2) {
    var typeswap = level.tiles[x1][y1].type;
    level.tiles[x1][y1].type = level.tiles[x2][y2].type;
    level.tiles[x2][y2].type = typeswap;
}

function FindMoves() {
    // Reset moves
    moves = [];

    CheckHorizontalSwaps();
    CheckVerticalSwaps();

    // Reset clusters
    clusters = [];
}

function CheckHorizontalSwaps() {
    // Check horizontal swaps
    for (var j = 0; j < level.rows; j++) {
        for (var i = 0; i < level.col - 1; i++) {
            // Swap, find clusters and swap back
            Swap(i, j, i + 1, j);
            FindClusters();
            Swap(i, j, i + 1, j);

            // Check if the swap made a cluster
            if (clusters.length > 0) {
                // Found a move
                moves.push({
                    column1: i,
                    row1: j,
                    column2: i + 1,
                    row2: j
                });
            }
        }
    }
}

function CheckVerticalSwaps() {
    // Check vertical swaps
    for (var i = 0; i < level.col; i++) {
        for (var j = 0; j < level.rows - 1; j++) {
            // Swap, find clusters and swap back
            Swap(i, j, i, j + 1);
            FindClusters();
            Swap(i, j, i, j + 1);

            // Check if the swap made a cluster
            if (clusters.length > 0) {
                // Found a move
                moves.push({
                    column1: i,
                    row1: j,
                    column2: i,
                    row2: j + 1
                });
            }
        }
    }
}

// Loop over the cluster tiles and execute a function
function LoopClusters(func) {
    for (var i = 0; i < clusters.length; i++) {
        //  { column, row, length, horizontal }
        var cluster = clusters[i];
        var coffset = 0;
        var roffset = 0;
        for (var j = 0; j < cluster.length; j++) {
            func(i, cluster.column + coffset, cluster.row + roffset, cluster);

            if (cluster.horizontal) {
                coffset++;
            } else {
                roffset++;
            }
        }
    }
}

// remove clusters
function RemoveClusters() {
    // Change the type of the tiles to -1, indicating a removed tile
    LoopClusters(function(index, column, row, cluster) {
        level.tiles[column][row].type = -1;
    });

    for (var i = 0; i < level.col; i++) {
        var shift = 0;
        for (var j = level.rows - 1; j >= 0; j--) {
            if (level.tiles[i][j].type === -1) {
                shift++;
                level.tiles[i][j].shift = 0;
            } else {
                level.tiles[i][j].shift = shift;
            }
        }
    }
}

// Replace removed tiles
function ShiftTiles() {
    for (var i = 0; i < level.col; i++) {
        for (var j = level.rows - 1; j >= 0; j--) {
            // Loop from bottom to top
            if (level.tiles[i][j].type == -1) {
                // Insert new random tile
                level.tiles[i][j].type = GetRandomTile();
            } else {
                // Swap tile to shift it
                var shift = level.tiles[i][j].shift;
                if (shift > 0) {
                    Swap(i, j, i, j + shift);
                }
            }

            // Reset shift
            level.tiles[i][j].shift = 0;
        }
    }
}<commit_msg>Update Match3Core.js<commit_after>import animate;
import ui.View;
import ui.ImageView;
import ui.resource.Image as Image;

import src.chance;
//var Chance = require('chance');
var chance = new Chance();

// our images
var img_bee = new Image({
        url: "resources/images/bee.png"
    }),
    img_beehive = new Image({
        url: "resources/images/beehive.png"
    }),
    img_honeycomb = new Image({
        url: "resources/images/honeycomb.png"
    }),
    img_jar = new Image({
        url: "resources/images/honeyjar.png"
    });
var TileBee,TileBeehive,TileHoneycomb,TileJar;

exports = Class(ui.View, function(supr) {
    this.init = function(opts) {
        opts = merge(opts, {
            width: 100,
            height: 100
        });

        supr(this, 'init', [opts]);

       TileBee = new ui.ImageView({
       	superview: this,
       	width:this.width,
       	height: this.height,
       	image: img_bee
       });
       TileBeehive = new ui.ImageView({
       	superview: this,
       	width:this.width,
       	height: this.height,
       	image: img_beehive
       });
       TileHoneycomb = new ui.ImageView({
       	superview: this,
       	width:this.width,
       	height: this.height,
       	image: img_honeycomb
       });
       TileJar = new ui.ImageView({
       	superview: this,
       	width:this.width,
       	height: this.height,
       	image: img_jar
       });
    };

    this.build = function() {};
});

// Our levels
var level = {
    x: 250,
    y: 113,
    col: 9,
    rows: 9,
    tile_W: 40,
    tile_H: 40,
    tiles: [],
    selectedTile: {
        selected: false,
        col: 0,
        row: 0
    }
};

function InitalizeBoard() {
	// initialize the 2D Level Array
    for (var i = 0; i < level.col; i++) {
        level.tiles[i] = [];
        for (var j = 0; j < level.rows; j++) {
            //Define a tile type and shift type
            level.tiles[i][j] = {
                type: 0,
                shift: 0
            };
        }
    }
}

function CreateLevel() {
    var done = false;

    // keep generating levels unitl it is correct
    while (!done) {
        for (var i = 0; i < level.col; i++) {
            for (var j = 0; j < level.rows; j++) {
                level.tiles[i][j].type = GetRandomTile();
            }
        }

        ResolveClusters();
        FindMoves();

        if (moves.length > 0) {
            done = true;
        }
    }
}

function GetRandomTile() {
    var result = chance.integer({
        min: 0,
        max: 3
    });

    switch (result) {
        case 0: // 1st tile type
            return;
        case 1: // 2nd tile type
            return;
        case 2: //3rd tile type
            return;
        case 3: //4th tile type
            return;
    }
}

/* Removes Matches from the game
 * screen and insert new tiles
 */
function ResolveClusters() {
    FindClusters();

    while (clusters.length > 0) {
        RemoveClusters();
        ShiftTiles();
        FindClusters();
    }
}

//find clusters
var clusters = [];

function FindClusters() {
    // Reset clusters
    clusters = [];

    FindHorizontalClusters();
    FindVerticalClusters();
}

function FindHorizontalClusters() {
    // Find horizontal clusters
    for (var j = 0; j < level.rows; j++) {
        // Start with a single tile, cluster of 1
        var matchlength = 1;
        for (var i = 0; i < level.col; i++) {
            var checkcluster = false;

            if (i == level.columns - 1) {
                // Last tile
                checkcluster = true;
            } else {
                // Check the type of the next tile
                if (level.tiles[i][j].type == level.tiles[i + 1][j].type &&
                    level.tiles[i][j].type != -1) {
                    // Same type as the previous tile, increase matchlength
                    matchlength += 1;
                } else {
                    // Different type
                    checkcluster = true;
                }
            }

            // Check if there was a cluster
            if (checkcluster) {
                if (matchlength >= 3) {
                    // Found a horizontal cluster
                    clusters.push({
                        column: i + 1 - matchlength,
                        row: j,
                        length: matchlength,
                        horizontal: true
                    });
                }

                matchlength = 1;
            }
        }
    }
}

function FindVerticalClusters() {
    // Find vertical clusters
    for (var i = 0; i < level.col; i++) {
        // Start with a single tile, cluster of 1
        var matchlength = 1;
        for (var j = 0; j < level.rows; j++) {
            var checkcluster = false;

            if (j == level.rows - 1) {
                // Last tile
                checkcluster = true;
            } else {
                // Check the type of the next tile
                if (level.tiles[i][j].type == level.tiles[i][j + 1].type &&
                    level.tiles[i][j].type != -1) {
                    // Same type as the previous tile, increase matchlength
                    matchlength += 1;
                } else {
                    // Different type
                    checkcluster = true;
                }
            }

            // Check if there was a cluster
            if (checkcluster) {
                if (matchlength >= 3) {
                    // Found a vertical cluster
                    clusters.push({
                        column: i,
                        row: j + 1 - matchlength,
                        length: matchlength,
                        horizontal: false
                    });
                }

                matchlength = 1;
            }
        }
    }
}

// Available moves
var moves = [];

function Swap(x1, y1, x2, y2) {
    var typeswap = level.tiles[x1][y1].type;
    level.tiles[x1][y1].type = level.tiles[x2][y2].type;
    level.tiles[x2][y2].type = typeswap;
}

function FindMoves() {
    // Reset moves
    moves = [];

    CheckHorizontalSwaps();
    CheckVerticalSwaps();

    // Reset clusters
    clusters = [];
}

function CheckHorizontalSwaps() {
    // Check horizontal swaps
    for (var j = 0; j < level.rows; j++) {
        for (var i = 0; i < level.col - 1; i++) {
            // Swap, find clusters and swap back
            Swap(i, j, i + 1, j);
            FindClusters();
            Swap(i, j, i + 1, j);

            // Check if the swap made a cluster
            if (clusters.length > 0) {
                // Found a move
                moves.push({
                    column1: i,
                    row1: j,
                    column2: i + 1,
                    row2: j
                });
            }
        }
    }
}

function CheckVerticalSwaps() {
    // Check vertical swaps
    for (var i = 0; i < level.col; i++) {
        for (var j = 0; j < level.rows - 1; j++) {
            // Swap, find clusters and swap back
            Swap(i, j, i, j + 1);
            FindClusters();
            Swap(i, j, i, j + 1);

            // Check if the swap made a cluster
            if (clusters.length > 0) {
                // Found a move
                moves.push({
                    column1: i,
                    row1: j,
                    column2: i,
                    row2: j + 1
                });
            }
        }
    }
}

// Loop over the cluster tiles and execute a function
function LoopClusters(func) {
    for (var i = 0; i < clusters.length; i++) {
        //  { column, row, length, horizontal }
        var cluster = clusters[i];
        var coffset = 0;
        var roffset = 0;
        for (var j = 0; j < cluster.length; j++) {
            func(i, cluster.column + coffset, cluster.row + roffset, cluster);

            if (cluster.horizontal) {
                coffset++;
            } else {
                roffset++;
            }
        }
    }
}

// remove clusters
function RemoveClusters() {
    // Change the type of the tiles to -1, indicating a removed tile
    LoopClusters(function(index, column, row, cluster) {
        level.tiles[column][row].type = -1;
    });

    for (var i = 0; i < level.col; i++) {
        var shift = 0;
        for (var j = level.rows - 1; j >= 0; j--) {
            if (level.tiles[i][j].type === -1) {
                shift++;
                level.tiles[i][j].shift = 0;
            } else {
                level.tiles[i][j].shift = shift;
            }
        }
    }
}

// Replace removed tiles
function ShiftTiles() {
    for (var i = 0; i < level.col; i++) {
        for (var j = level.rows - 1; j >= 0; j--) {
            // Loop from bottom to top
            if (level.tiles[i][j].type == -1) {
                // Insert new random tile
                level.tiles[i][j].type = GetRandomTile();
            } else {
                // Swap tile to shift it
                var shift = level.tiles[i][j].shift;
                if (shift > 0) {
                    Swap(i, j, i, j + shift);
                }
            }

            // Reset shift
            level.tiles[i][j].shift = 0;
        }
    }
}
<|endoftext|>


<commit_before>import { operations, types } from './constants';
import RefStore from './RefStore';
import Events from './Events';

export default class RefManager {
  constructor(db) {
    this.db = db;
    this.events = new Events();
    this.references = new RefStore();
  }

  add(rawConfig) {
    const config = RefStore.parseRefConfig(rawConfig);

    this.references.add(config);

    if (config.syncOn.includes(operations.update)) {
      this.onUpdate(config);
    }

    if (config.syncOn.includes(operations.remove)) {
      this.onRemove(config);
    }

    return this;
  }

  async sync({ collection, references, data, cursor }) {
    if (cursor) {
      while (await cursor.hasNext()) {
        const next = await cursor.next();
        await this.sync({ collection, references, data: next });
      }

      return Promise.resolve();
    }

    const docs = Array.isArray(data) ? data : [data];
    const refsConfig = this.references.get(collection);
    const refPropsList = Array.isArray(references) ? references : Object.keys(refsConfig);
    const refsIds = RefStore.getRefsIds({ docs, refPropsList });
    const refsList = await RefStore.getRefsList({ db: this.db, refsIds, refsConfig });
    const refsMap = refPropsList.reduce((acc, refPropr, index) => ({
      ...acc,
      [refPropr]: refsList[index],
    }), {});

    const bulk = this.db.collection(collection).initializeUnorderedBulkOp();
    let hasOperations = false;
    docs.forEach((doc) => {
      const updatePayload = RefStore.getDocUpdatePayload({ doc, refsConfig, refsMap });
      const hasUpdates = Object.keys(updatePayload).length;
      if (hasUpdates) {
        hasOperations = true;
        bulk.find({ _id: doc._id }).updateOne({
          $set: updatePayload,
        });
      }
    });

    if (hasOperations) {
      return bulk.execute();
    }

    return Promise.resolve();
  }

  async syncAll({ collections }) {
    if (!collections) {
      collections = Array.from(this.references.getCollections()); // eslint-disable-line
    }

    return Promise.all(
      collections.map(
        (collection) => this.sync({ collection, cursor: this.db.collection(collection).find() })
      )
    );
  }

  onUpdate({ source, destination, refProperty, extractor, type, ns }) {
    this.events.on(operations.update, destination, async ({ query }) => {
      const refDocs = await this.db.collection(destination).find(query).toArray();
      const bulk = this.db.collection(source).initializeUnorderedBulkOp();

      refDocs.forEach((doc) => {
        const updateNS = type === types.one ? ns : `${ns}.${doc._id}`;

        bulk.find({
          [refProperty]: doc._id,
        }).update({
          $set: {
            [updateNS]: extractor(doc),
          },
        }, {
          multi: true,
        });
      });

      return bulk.execute();
    });
  }

  onRemove({ source, destination, refProperty, type, ns }) {
    this.events.on(operations.remove, destination, async ({ query }) => {
      const refDocs = await this.db.collection(destination).find(query, { _id: 1 }).toArray();
      const refDocsIds = refDocs.map(({ _id }) => _id);
      const updatePayload = {
        $unset: {},
      };

      if (type === types.one) {
        updatePayload.$unset[ns] = '';
      } else {
        refDocsIds.forEach((id) => {
          updatePayload.$unset[`${ns}.${id}`] = '';
        });
      }

      return this.db.collection(source).update({
        [refProperty]: {
          $in: refDocsIds,
        },
      }, updatePayload, {
        multi: true,
      });
    });
  }

  notifyUpdate(collection, query) {
    return this.events.notify({
      operation: operations.update,
      collection,
      query,
    });
  }

  notifyRemove(collection, query) {
    return this.events.notify({
      operation: operations.remove,
      collection,
      query,
    });
  }
}
<commit_msg>result visitor<commit_after>import { operations, types } from './constants';
import RefStore from './RefStore';
import Events from './Events';

export default class RefManager {
  constructor(db) {
    this.db = db;
    this.events = new Events();
    this.references = new RefStore();
  }

  add(rawConfig) {
    const config = RefStore.parseRefConfig(rawConfig);

    this.references.add(config);

    if (config.syncOn.includes(operations.update)) {
      this.onUpdate(config);
    }

    if (config.syncOn.includes(operations.remove)) {
      this.onRemove(config);
    }

    return this;
  }

  async sync({ collection, references, data, cursor, visit = () => {} }) {
    if (cursor) {
      while (await cursor.hasNext()) {
        const next = await cursor.next();
        const result = await this.sync({ collection, references, data: next });
        visit(result);
      }

      return Promise.resolve();
    }

    const docs = Array.isArray(data) ? data : [data];
    const refsConfig = this.references.get(collection);
    const refPropsList = Array.isArray(references) ? references : Object.keys(refsConfig);
    const refsIds = RefStore.getRefsIds({ docs, refPropsList });
    const refsList = await RefStore.getRefsList({ db: this.db, refsIds, refsConfig });
    const refsMap = refPropsList.reduce((acc, refPropr, index) => ({
      ...acc,
      [refPropr]: refsList[index],
    }), {});

    const bulk = this.db.collection(collection).initializeUnorderedBulkOp();
    let hasOperations = false;
    docs.forEach((doc) => {
      const updatePayload = RefStore.getDocUpdatePayload({ doc, refsConfig, refsMap });
      const hasUpdates = Object.keys(updatePayload).length;
      if (hasUpdates) {
        hasOperations = true;
        bulk.find({ _id: doc._id }).updateOne({
          $set: updatePayload,
        });
      }
    });

    if (hasOperations) {
      return bulk.execute();
    }

    return Promise.resolve();
  }

  async syncAll({ collections }) {
    if (!collections) {
      collections = Array.from(this.references.getCollections()); // eslint-disable-line
    }

    return Promise.all(
      collections.map(
        (collection) => this.sync({ collection, cursor: this.db.collection(collection).find() })
      )
    );
  }

  onUpdate({ source, destination, refProperty, extractor, type, ns }) {
    this.events.on(operations.update, destination, async ({ query }) => {
      const refDocs = await this.db.collection(destination).find(query).toArray();
      const bulk = this.db.collection(source).initializeUnorderedBulkOp();

      refDocs.forEach((doc) => {
        const updateNS = type === types.one ? ns : `${ns}.${doc._id}`;

        bulk.find({
          [refProperty]: doc._id,
        }).update({
          $set: {
            [updateNS]: extractor(doc),
          },
        }, {
          multi: true,
        });
      });

      return bulk.execute();
    });
  }

  onRemove({ source, destination, refProperty, type, ns }) {
    this.events.on(operations.remove, destination, async ({ query }) => {
      const refDocs = await this.db.collection(destination).find(query, { _id: 1 }).toArray();
      const refDocsIds = refDocs.map(({ _id }) => _id);
      const updatePayload = {
        $unset: {},
      };

      if (type === types.one) {
        updatePayload.$unset[ns] = '';
      } else {
        refDocsIds.forEach((id) => {
          updatePayload.$unset[`${ns}.${id}`] = '';
        });
      }

      return this.db.collection(source).update({
        [refProperty]: {
          $in: refDocsIds,
        },
      }, updatePayload, {
        multi: true,
      });
    });
  }

  notifyUpdate(collection, query) {
    return this.events.notify({
      operation: operations.update,
      collection,
      query,
    });
  }

  notifyRemove(collection, query) {
    return this.events.notify({
      operation: operations.remove,
      collection,
      query,
    });
  }
}
<|endoftext|>


<commit_before>var Github = require('github-api')
var asciify = require('asciify')

module.exports = function(object, request) {

  getDetails(object)

  function getDetails(object) {
    console.log("emailbody", object.plain)
    // add if not from github, don't do anything
    var subject = object.headers.Subject
    var detailsArray = subject.split(" added you to ")
    var details = { "username": detailsArray[0],
                    "repo": detailsArray[1]
                  }
    details.repoURI = "https://www.github.com/"
                    + details.username + "/"
                    + details.repo + ".git"
    console.log(details.username, "added you as a contributor.")
    asciiArt(details)
  }

  function asciiArt(details) {
    asciify(details.username, {font:'isometric2'}, function(err, res){
      if (err) console.log("ascii error", err)
      var artwork = res
      writeRepo(artwork, details)
    })
  }

  function writeRepo(artwork, details) {

    var github = new Github({
      auth: "oauth",
      token: process.env['REPOROBOT_TOKEN']
    })

    var repo = github.getRepo(details.username, details.repo)

    var branchName = "add-" + details.username
    var filePath = 'contributors/add-' + details.username + '.txt'

    // reporobot will overwrite the existing file which should just
    // contain a username

    repo.write(branchName, filePath, artwork, 'drew ur picture', function(err) {
      if (err) console.log("Error writing to repo", err)
      console.log("Commited to a repo")
    })
  }
}
<commit_msg>filter out non collab emails<commit_after>var Github = require('github-api')
var asciify = require('asciify')

module.exports = function(object, request) {

  getDetails(object)

  function getDetails(object) {
    // console.log("emailbody", object.plain)
    // add if not from github, don't do anything
    var subject = object.headers.Subject

    if (!subject.match("added you to")) return
    console.log(["email subject", subject])

    var detailsArray = subject.split(" added you to ")
    var details = { "username": detailsArray[0],
                    "repo": detailsArray[1]
                  }
    details.repoURI = "https://www.github.com/"
                    + details.username + "/"
                    + details.repo + ".git"
    console.log(details.username, "added you as a contributor.")
    asciiArt(details)
  }

  function asciiArt(details) {
    asciify(details.username, {font:'isometric2'}, function(err, res){
      if (err) console.log("ascii error", err)
      var artwork = res
      writeRepo(artwork, details)
    })
  }

  function writeRepo(artwork, details) {

    var github = new Github({
      auth: "oauth",
      token: process.env['REPOROBOT_TOKEN']
    })

    var repo = github.getRepo(details.username, details.repo)

    var branchName = "add-" + details.username
    var filePath = 'contributors/add-' + details.username + '.txt'

    // reporobot will overwrite the existing file which should just
    // contain a username

    repo.write(branchName, filePath, artwork, 'drew ur picture', function(err) {
      if (err) console.log("Error writing to repo", err)
      console.log("Commited to a repo")
    })
  }
}
<|endoftext|>


<commit_before>document.getElementById("id_logic_level_version").innerHTML="Business level version 2017.11.08.0";

var canvas = document.getElementById("id_canvas");
var context = canvas.getContext("2d");

window.addEventListener("touchstart", on_touch_start);

function on_touch_start(e)
{
    var touches = e.changedTouches;
    for (i = 0; i < e.changedTouches.lenght; i++){
        context.beginPath();
        context.arc(touches[i].pageX, touches[i].pageY, 10, 0, Math.PI);
        context.stroke();
    }

}<commit_msg>Correct syntax again..<commit_after>document.getElementById("id_logic_level_version").innerHTML="Business level version 2017.11.08.0";

var canvas = document.getElementById("id_canvas");
var context = canvas.getContext("2d");

window.addEventListener("touchstart", on_touch_start);

function on_touch_start(e)
{
    var touches = e.changedTouches;
    for (i = 0; i < e.changedTouches.length; i++){
        context.beginPath();
        context.arc(touches[i].pageX, touches[i].pageY, 10, 0, Math.PI);
        context.stroke();
    }

}<|endoftext|>


<commit_before>const { Buffer } = require('safe-buffer')

module.exports = StructType

function StructType (descr, mapRead = [], mapWrite = []) {
  const type = function read (buf, ...rest) {
    return type.read(
      Buffer.isBuffer(buf) ? { buf: buf, offset: 0 } : buf,
      ...rest
    )
  }

  Object.keys(descr).forEach((key) => {
    type[key] = descr[key]
  })

  type.$structType = true

  type.read = function read (...args) {
    let val = descr.read.apply(type, args)
    for (let i = 0, l = mapRead.length; i < l; i++) {
      val = mapRead[i].call(type, val)
    }
    return val
  }

  if (type.write == null) {
    type.write = function write () {
      throw new Error('unimplemented')
    }
  } else {
    type.write = function write (opts, originalVal) {
      let val = originalVal
      for (let i = mapWrite.length - 1; i >= 0; i--) {
        val = mapWrite[i].call(type, val, opts)
      }
      return descr.write.call(type, opts, val)
    }
  }

  type.transform =
  type.mapRead = (fn) => StructType(
    descr,
    [ ...mapRead, fn ],
    mapWrite
  )
  type.mapWrite = (fn) => StructType(
    descr,
    mapRead,
    [ ...mapWrite, fn ]
  )
  type.map = (read, write) => StructType(
    descr,
    read ? [ ...mapRead, read ] : mapRead,
    write ? [ ...mapWrite, write ] : mapWrite
  )

  if (type.size == null) {
    type.size = () => {
      throw new Error('unimplemented')
    }
  } else if (typeof type.size === 'number') {
    type.size = () => descr.size
  }

  // abstract-encoding
  type.encode = encode
  type.decode = decode
  type.encodingLength = type.size

  return type

  function encode (value, buffer, offset) {
    if (!offset) {
      offset = 0
    }
    if (!buffer) {
      buffer = Buffer.alloc(type.size(value))
    }
    const opts = { buf: buffer, offset: offset }
    type.write(opts, value)
    encode.bytes = opts.offset - offset
    return opts.buf
  }
  function decode (buffer, start, end) {
    if (!start) {
      start = 0
    }
    const opts = { buf: buffer, offset: start }
    const value = type.read(opts)
    decode.bytes = opts.offset - start
    return value
  }
}
<commit_msg>Remove rest args from read()<commit_after>const { Buffer } = require('safe-buffer')

module.exports = StructType

function StructType (descr, mapRead = [], mapWrite = []) {
  const type = function read (buf, parent) {
    const opts = Buffer.isBuffer(buf) ? { buf: buf, offset: 0 } : buf
    return type.read(opts, parent)
  }

  Object.keys(descr).forEach((key) => {
    type[key] = descr[key]
  })

  type.$structType = true

  const readImpl = descr.read.bind(type)
  type.read = function read (opts, parent) {
    let val = readImpl(opts, parent)
    for (let i = 0, l = mapRead.length; i < l; i++) {
      val = mapRead[i].call(type, val)
    }
    return val
  }

  if (type.write == null) {
    type.write = function write () {
      throw new Error('unimplemented')
    }
  } else {
    const writeImpl = descr.write.bind(type)
    type.write = function write (opts, originalVal) {
      let val = originalVal
      for (let i = mapWrite.length - 1; i >= 0; i--) {
        val = mapWrite[i].call(type, val, opts)
      }
      return writeImpl(opts, val)
    }
  }

  type.transform =
  type.mapRead = (fn) => StructType(
    descr,
    [ ...mapRead, fn ],
    mapWrite
  )
  type.mapWrite = (fn) => StructType(
    descr,
    mapRead,
    [ ...mapWrite, fn ]
  )
  type.map = (read, write) => StructType(
    descr,
    read ? [ ...mapRead, read ] : mapRead,
    write ? [ ...mapWrite, write ] : mapWrite
  )

  if (type.size == null) {
    type.size = () => {
      throw new Error('unimplemented')
    }
  } else if (typeof type.size === 'number') {
    type.size = () => descr.size
  }

  // abstract-encoding
  type.encode = encode
  type.decode = decode
  type.encodingLength = type.size

  return type

  function encode (value, buffer, offset) {
    if (!offset) {
      offset = 0
    }
    if (!buffer) {
      buffer = Buffer.alloc(type.size(value))
    }
    const opts = { buf: buffer, offset: offset }
    type.write(opts, value)
    encode.bytes = opts.offset - offset
    return opts.buf
  }
  function decode (buffer, start, end) {
    if (!start) {
      start = 0
    }
    const opts = { buf: buffer, offset: start }
    const value = type.read(opts, undefined)
    decode.bytes = opts.offset - start
    return value
  }
}
<|endoftext|>