docs: function(params, callback) {
                  var count = 50,
                      offset = 0,
                      content = {},
                      whereClause = {},
                      returnCache = false;
  
                  if (params) {
                      if (parseInt(params.count) > 0) {
                          count = params.count;
                      }
  
                      if (parseInt(params.offset) > 0) {
                          offset = params.offset;
                      }
  
                      if (typeof params.name === 'string') {
                          content.name = whereClause.name = params.name;
                      }
  
                      if (Array.isArray(params.threadIds)) {
                          content.threadIds = whereClause.threadIds = params.threadIds;
                      }
  
                      if (typeof params.new === 'boolean') {
                          content.new = params.new;
                      }
  
                      if (parseInt(params.creatorCoreUserId) > 0) {
                          content.creatorCoreUserId = whereClause.creatorCoreUserId = params.creatorCoreUserId;
                      }
  
                      if (parseInt(params.partnerCoreUserId) > 0) {
                          content.partnerCoreUserId = whereClause.partnerCoreUserId = params.partnerCoreUserId;
                      }
  
                      if (parseInt(params.partnerCoreContactId) > 0) {
                          content.partnerCoreContactId = whereClause.partnerCoreContactId = params.partnerCoreContactId;
                      }
                  }
  
                  content.count = count;
                  content.offset = offset;
  
                  var sendMessageParams = {
                      chatMessageVOType: chatMessageVOTypes.GET_THREADS,
                      typeCode: params.typeCode,
                      content: content
                  };
  
                  /**
                   * Retrieve threads from cache
                   */
                  if (canUseCache) {
                      if (db) {
                          var thenAble;
  
                          if (Object.keys(whereClause).length === 0) {
                              thenAble = db.threads.where('[owner+time]')
                                  .between([userInfo.id, minIntegerValue], [userInfo.id, maxIntegerValue * 1000])
                                  .reverse();
                          }
                          else {
                              if (whereClause.hasOwnProperty('threadIds')) {
                                  thenAble = db.threads.where('id')
                                      .anyOf(whereClause.threadIds)
                                      .and(function(thread) {
                                          return thread.owner == userInfo.id;
                                      });
                              }
  
                              if (whereClause.hasOwnProperty('name')) {
                                  thenAble = db.threads.where('owner')
                                      .equals(userInfo.id)
                                      .filter(function(thread) {
                                          var reg = new RegExp(whereClause.name);
                                          return reg.test(chatDecrypt(thread.title, cacheSecret, thread.salt));
                                      });
                              }
  
                              if (whereClause.hasOwnProperty('creatorCoreUserId')) {
                                  thenAble = db.threads.where('owner')
                                      .equals(userInfo.id)
                                      .filter(function(thread) {
                                          return parseInt(thread.inviter.id) == parseInt(whereClause.creatorCoreUserId);
                                      });
                              }
                          }
  
                          thenAble.offset(offset)
                              .limit(count)
                              .toArray()
                              .then(function(threads) {
                                  db.threads.where('owner')
                                      .equals(userInfo.id)
                                      .count()
                                      .then(function(threadsCount) {
                                          var cacheData = [];
  
                                          for (var i = 0; i < threads.length; i++) {
                                              try {
                                                  var tempData = {},
                                                      salt = threads[i].salt;
  
                                                  cacheData.push(createThread(JSON.parse(chatDecrypt(threads[i].data, cacheSecret, threads[i].salt)), false));
                                              }
                                              catch (error) {
                                                  fireEvent('error', {
                                                      code: error.code,
                                                      message: error.message,
                                                      error: error
                                                  });
                                              }
                                          }
  
                                          var returnData = {
                                              hasError: false,
                                              cache: true,
                                              errorCode: 0,
                                              errorMessage: '',
                                              result: {
                                                  threads: cacheData,
                                                  contentCount: threadsCount,
                                                  hasNext: (offset + count < threadsCount && threads.length > 0),
                                                  nextOffset: offset + threads.length
                                              }
                                          };
  
                                          if (cacheData.length > 0) {
                                              callback && callback(returnData);
                                              callback = undefined;
                                              returnCache = true;
                                          }
                                      });
                              })
                              .catch(function(error) {
                                  fireEvent('error', {
                                      code: error.code,
                                      message: error.message,
                                      error: error
                                  });
                              });
                      }
                      else {
                          fireEvent('error', {
                              code: 6601,
                              message: CHAT_ERRORS[6601],
                              error: null
                          });
                      }
                  }
  
                  /**
                   * Retrive get threads response from server
                   */
                  return sendMessage(sendMessageParams, {
                      onResult: function(result) {
                          var returnData = {
                              hasError: result.hasError,
                              cache: false,
                              errorMessage: result.errorMessage,
                              errorCode: result.errorCode
                          };
  
                          if (!returnData.hasError) {
  
                              var messageContent = result.result,
                                  messageLength = messageContent.length,
                                  resultData = {
                                      threads: [],
                                      contentCount: result.contentCount,
                                      hasNext: (offset + count < result.contentCount && messageLength > 0),
                                      nextOffset: offset + messageLength
                                  },
                                  threadData;
  
                              for (var i = 0; i < messageLength; i++) {
                                  threadData = createThread(messageContent[i], false);
                                  if (threadData) {
                                      resultData.threads.push(threadData);
                                  }
                              }
  
                              returnData.result = resultData;
  
                              /**
                               * Updating cache on separated worker to find and
                               * delete all messages that have been deleted from
                               * thread's last section
                               *
                               * This option works on browser only - no Node support
                               * TODO: Implement Node Version
                               */
                              if (typeof Worker !== 'undefined' && productEnv != 'ReactNative' && canUseCache) {
                                  if (typeof(cacheSyncWorker) == 'undefined') {
                                      cacheSyncWorker = new Worker('src/browser-worker.js');
                                  }
  
                                  var workerCommand = {
                                      type: 'getThreads',
                                      userId: userInfo.id,
                                      data: JSON.stringify(resultData.threads)
                                  };
  
                                  cacheSyncWorker.postMessage(JSON.stringify(workerCommand));
  
                                  cacheSyncWorker.onmessage = function(event) {
                                      if (event.data == 'terminate') {
                                          cacheSyncWorker.terminate();
                                          cacheSyncWorker = undefined;
                                      }
                                  };
  
                                  cacheSyncWorker.onerror = function(event) {
                                      console.log(event);
                                  };
                              }
  
                              /**
                               * Add Threads into cache database #cache
                               */
                              if (canUseCache) {
                                  if (db) {
                                      var cacheData = [];
  
                                      for (var i = 0; i < resultData.threads.length; i++) {
                                          try {
                                              var tempData = {},
                                                  salt = Utility.generateUUID();
  
                                              tempData.id = resultData.threads[i].id;
                                              tempData.owner = userInfo.id;
                                              tempData.title = Utility.crypt(resultData.threads[i].title, cacheSecret, salt);
                                              tempData.time = resultData.threads[i].time;
                                              tempData.data = Utility.crypt(JSON.stringify(unsetNotSeenDuration(resultData.threads[i])), cacheSecret, salt);
                                              tempData.salt = salt;
  
                                              cacheData.push(tempData);
                                          }
                                          catch (error) {
                                              fireEvent('error', {
                                                  code: error.code,
                                                  message: error.message,
                                                  error: error
                                              });
                                          }
                                      }
  
                                      db.threads.bulkPut(cacheData)
                                          .catch(function(error) {
                                              fireEvent('error', {
                                                  code: error.code,
                                                  message: error.message,
                                                  error: error
                                              });
                                          });
                                  }
                                  else {
                                      fireEvent('error', {
                                          code: 6601,
                                          message: CHAT_ERRORS[6601],
                                          error: null
                                      });
                                  }
                              }
                          }
  
                          callback && callback(returnData);
                          /**
                           * Delete callback so if server pushes response before
                           * cache, cache won't send data again
                           */
                          callback = undefined;
  
                          if (!returnData.hasError && returnCache) {
                              fireEvent('threadEvents', {
                                  type: 'THREADS_LIST_CHANGE',
                                  result: returnData.result
                              });
                          }
                      }
                  });
              }