docs: function skladConnection_get() {
          const isMulti = (arguments.length === 1 && typeof arguments[0] === 'object');
          const objStoreNames = isMulti ? Object.keys(arguments[0]) : [arguments[0]];
  
          const allObjStoresExist = checkContainingStores.call(this, objStoreNames);
          if (!allObjStoresExist) {
              const err = createError('NotFoundError', `Database ${this.database.name} (version ${this.database.version}) doesn't contain all needed stores`);
              return Promise.reject(err);
          }
  
          let result = {};
          let data, abortErr;
  
          if (isMulti) {
              data = arguments[0];
          } else {
              data = {};
              data[arguments[0]] = arguments[1];
          }
  
          objStoreNames.forEach(function (objStoreName) {
              result[objStoreName] = [];
          });
  
          return new Promise((resolve, reject) => {
              let transaction;
  
              // Safari9 can't run multi-objectstore transactions
              // divide one transaction into many with one object store to fix this
              try {
                  transaction = this.database.transaction(objStoreNames, TRANSACTION_READONLY);
              } catch (ex) {
                  if (ex.name === 'NotFoundError') {
                      const promises = {};
  
                      objStoreNames.forEach(objStoreName => {
                          const promise = this.get(objStoreName, data[objStoreName]);
                          promises[objStoreName] = promise;
                      });
  
                      resolveAllPromises(promises).then(resolve).catch(reject);
                  } else {
                      reject(ex);
                  }
  
                  return;
              }
  
              transaction.oncomplete = transaction.onerror = transaction.onabort = function skladConnection_get_onFinish(evt) {
                  const err = abortErr || evt.target.error;
                  const isSuccess = !err && evt.type === 'complete';
  
                  if (isSuccess) {
                      resolve(isMulti ? result : result[objStoreNames[0]]);
                  } else {
                      reject(ensureError(err));
                  }
  
                  if (evt.type === 'error') {
                      evt.preventDefault();
                  }
              };
  
              for (let objStoreName in data) {
                  const objStore = transaction.objectStore(objStoreName);
                  const options = data[objStoreName] || {};
                  const direction = options.direction || skladAPI.ASC;
                  const range = options.range instanceof IDBKeyRangeRef ? options.range : null;
  
                  let useGetAll = false;
                  let iterateRequest;
  
                  if (supportsObjStoreGetAll) {
                      // getAll doesn't work for index ranges + it doesn't support special directions
                      // @see https://developer.mozilla.org/en-US/docs/Web/API/IDBObjectStore/getAll
                      useGetAll = Object.keys(options).every(key => (key !== 'index' && key !== 'direction'));
                  }
  
                  if (options.index) {
                      if (!objStore.indexNames.contains(options.index)) {
                          abortErr = createError('NotFoundError', `Object store ${objStore.name} doesn't contain "${options.index}" index`);
                          return;
                      }
  
                      try {
                          iterateRequest = objStore.index(options.index).openCursor(range, direction);
                      } catch (ex) {
                          abortErr = ex;
                          return;
                      }
                  } else if (useGetAll) {
                      // If browser supports getAll/getAllKeys methods it could be faster to run these methods
                      // to get all records if there's no `index` or `direction` options set
                      // Unfortunately getAll doesn't expose result keys so we have to run both these methods
                      // to get all keys and values
                      // Anyway it seems like 2 getAll* ops are faster in modern browsers than that one
                      // working with UDBCursor
                      //
                      // @see https://developer.mozilla.org/en-US/docs/Web/API/IDBObjectStore/getAll
                      // @see https://developer.mozilla.org/en-US/docs/Web/API/IDBObjectStore/getAllKeys
                      // @see http://jsperf.com/idb-idbcursor-vs-idbobjectstore-getall-ops/3
                      const args = [range];
                      let offset = 0;
  
                      if (options.limit) {
                          args.push(options.limit);
  
                          if (options.offset) {
                              args[1] += options.offset;
                              offset = options.offset;
                          }
                      }
  
                      try {
                          // get all values request
                          objStore.getAll(...args).onsuccess = function (evt) {
                              const values = evt.target.result;
  
                              values.forEach((value, index) => {
                                  if (index < offset) {
                                      return;
                                  }
  
                                  const resultIndex = index - offset;
                                  result[objStoreName][resultIndex] = result[objStoreName][resultIndex] || {};
                                  result[objStoreName][resultIndex].value = value;
                              });
                          };
  
                          // get all keys request
                          objStore.getAllKeys(...args).onsuccess = function (evt) {
                              const keys = evt.target.result;
  
                              keys.forEach((key, index) => {
                                  if (index < offset) {
                                      return;
                                  }
  
                                  const resultIndex = index - offset;
                                  result[objStoreName][resultIndex] = result[objStoreName][resultIndex] || {};
                                  result[objStoreName][resultIndex].key = key;
                              });
                          };
                      } catch (ex) {
                          abortErr = ex;
                      }
  
                      // there are 2 separate IDBRequests running
                      // so there's no need to bind listener to success event of any of them
                      continue;
                  } else {
                      try {
                          iterateRequest = objStore.openCursor(range, direction);
                      } catch (ex) {
                          abortErr = ex;
                          return;
                      }
                  }
  
                  let cursorPositionMoved = false;
  
                  iterateRequest.onsuccess = function (evt) {
                      const cursor = evt.target.result;
  
                      // no more results
                      if (!cursor) {
                          return;
                      }
  
                      if (options.offset && !cursorPositionMoved) {
                          cursorPositionMoved = true;
                          cursor.advance(options.offset);
  
                          return;
                      }
  
                      result[objStoreName].push({
                          key: cursor.key,
                          value: cursor.value
                      });
  
                      if (options.limit && options.limit === result[objStoreName].length) {
                          return;
                      }
  
                      cursor.continue();
                  };
              }
          });
      }