<commit_before>'use strict';
/* globals gT: true */
/* globals gIn: true */

function clickWrapper(webEl) {
  if (gT.engineConsts.extJsClickDelay) {
    gT.s.driver.sleep(gT.engineConsts.extJsClickDelay, false);
  }
  return webEl.click();
}

function dblClickWrapper(webEl) {
  if (gT.engineConsts.extJsClickDelay) {
    gT.s.driver.sleep(gT.engineConsts.extJsClickDelay, false);
  }
  return new gT.sOrig.ActionSequence(gT.sOrig.driver).doubleClick(webEl).perform();
}

function printTextAndDblClick(logAction) {
  return function (webEl) {
    webEl.getText()
      .then(function (text) { // Using of selenium queue, so not then.then.
        gIn.logger.logIfNotDisabled(', Item text: "' + text + '" ... ', logAction);
      });
    return dblClickWrapper(webEl);
  };
}

function printTextAndClick(logAction) {
  return function (webEl) {
    webEl.getText()
      .then(function (text) { // Using of selenium queue, so not then.then.
        gIn.logger.logIfNotDisabled(', Item text: "' + text + '" ... ', logAction);
      });
    return clickWrapper(webEl);
  };
}

function printTextAndClickTextItem(logAction) {
  return function (webEl) {
    return webEl.findElement(gT.sOrig.by.css('.x-tree-node-text'))
      .then(function (el) {
        el.getText()
          .then(function (text) { // Using of selenium queue, so not then.then.
            gIn.logger.logIfNotDisabled(', Item text: "' + text + '" ... ', logAction);
          });
        return clickWrapper(el);
      });
    // webEl.getAttribute('class')
    //   .then(function (attrVal) {
    //     gIn.logger.logIfNotDisabled(', Item class: "' + attrVal + '"', logAction);
    //   });
    // webEl.getText()
    //   .then(function (text) { // Using of selenium queue, so not then.then.
    //     gIn.logger.logIfNotDisabled(', Item text: "' + text + '" ... ', logAction);
    //   });

  };
}

/**
 * Some ExtJs API functions require parameter to be numeric type and not equal string.
 * @param val
 * @returns {string}
 */
function valueToParameter(val) {
  if (typeof val === 'number') {
    return `${val}`;
  }
  if (typeof val === 'string') {
    return `'${val}'`;
  }
}

// Note that for tree only expanded nodes are taking into account.
exports.tableItemByIndex = function (tableId, tableName, itemIndex, logAction) {
  return gIn.wrap(`Click table '${tableName}' item by index '${itemIndex}'`, logAction, function () {
    return gT.s.browser.executeScript(`return tiaEJ.hEById.getTableItemByIndex('${tableId}', ${itemIndex});`, false)
      .then(printTextAndClickTextItem(logAction));
  });
};

exports.tableItemByField = function (tableId, tableName, fieldValue, fieldName, logAction) {
  fieldName = fieldName ? fieldName : 'name';
  return gIn.wrap(`Click table '${tableName}' item, fieldValue: ${valueToParameter(fieldValue)}, fieldName: '${fieldName}'`,
    logAction, function () {
      return gT.s.browser.executeScript(
        `return tiaEJ.hEById.getTableItemByField('${tableId}', ${valueToParameter(fieldValue)}, '${fieldName}');`
        , false
      ).then(printTextAndClickTextItem(logAction));
    });
};

exports.tableItemByFieldLocKey = function (tableId, tableName, fieldValueKey, fieldName, logAction) {
  fieldName = fieldName ? fieldName : 'name';
  return gIn.wrap(`Click table '${tableName}' item, fieldValue: '${fieldValueKey}', fieldName: '${fieldName}'`,
    logAction, function () {
      return gT.s.browser.executeScript(
        `return tiaEJ.hEById.getTableItemByFieldLocKey('${tableId}', '${fieldValueKey}', '${fieldName}');`
        , false
      ).then(printTextAndClickTextItem(logAction));
    });
};

exports.tableItemByFieldId = function (tableId, tableName, id, logAction) {
  return gIn.wrap(`Click table '${tableName}' item, with id: '${id}'`, logAction, function () {
    return gT.s.browser.executeScript(
      `return tiaEJ.hEById.getTableItemByField('${tableId}', ${valueToParameter(id)}, 'id');`
      , false
    ).then(printTextAndClickTextItem(logAction));
  });
};

exports.comboBoxItemByIndex = function (cbId, itemIndex, logAction) {
  return gIn.wrap(``, logAction, function () {
    return gT.s.browser.executeScript(`return tiaEJ.hEById.getNameAndLabels('${cbId}');`, false)
      .then(function (obj) {
        gIn.logger.logIfNotDisabled(
          `Click combo box(name: '${obj.name}', label: '${obj.label}'), item #${itemIndex}`, logAction);
      })
      .then(function () {
        return gT.s.browser.executeScript(`return tiaEJ.hEById.getInputEl('${cbId}');`, false);
      })
      .then(function (inputEl) {
        return clickWrapper(inputEl);
      })
      .then(function () {
        return gT.sOrig.driver.wait(function () {
          return gT.s.browser.executeScriptWrapper(`return tiaEJ.hEById.isCBPickerVisible('${cbId}', ${itemIndex});`);
        }, 5000);
      })
      .then(function () {
        return gT.s.browser.executeScript(`return tiaEJ.hEById.getCBItemByIndex('${cbId}', ${itemIndex});`, false);
      })
      .then(printTextAndClick(logAction));
  });
};

exports.comboBoxItemByField = function (cbId, fieldValue, fieldName, logAction) {
  return gIn.wrap(``, logAction, function () {
    return gT.s.browser.executeScript(`return tiaEJ.hEById.getNameAndLabels('${cbId}');`, false)
      .then(function (obj) {
        gIn.logger.logIfNotDisabled(
          `Click combo box(name: '${obj.name}', label: '${obj.label}'), by field (name: ${fieldName}, value: ${fieldValue})`,
          logAction);
      })
      .then(function () {
        return gT.s.browser.executeScript(`return tiaEJ.hEById.getInputEl('${cbId}');`, false);
      })
      .then(function (inputEl) {
        return clickWrapper(inputEl);
      })
      .then(function () {
        return gT.sOrig.driver.wait(function () {
          return gT.s.browser.executeScriptWrapper(`return tiaEJ.hEById.isCBPickerVisible('${cbId}');`);
        }, 5000);
      })
      .then(function () {
        return gT.s.browser.executeScript(
          `return tiaEJ.hEById.getCBItemByField('${cbId}', ${valueToParameter(fieldValue)}, '${fieldName}');`,
          false);
      })
      .then(printTextAndClick(logAction));
  });
};

exports.comboBoxItemByFormIdNameIndex = function (formId, name, index, logAction) {
  return gIn.wrap(`Click combobox item by formId: ${formId}, name: ${name}, index: ${index}`,
    logAction, function () {
      return gT.s.browser.executeScript(`return tiaEJ.hEById.getInputElByFormName('${formId}', '${name}');`, false)
        .then(function (inputEl) {
          return clickWrapper(inputEl);
        })
        .then(function () {
          return gT.sOrig.driver.wait(function () {
            return gT.s.browser.executeScriptWrapper(`return tiaEJ.hEById.isCBPickerVisibleByFormName('${formId}', '${name}', ${index});`);
          }, 5000);
        })
        .then(function () {
          return gT.s.browser.executeScript(
            `return tiaEJ.hEById.getCBItemByFormNameIndex('${formId}', '${name}', ${index});`,
            false);
        })
        .then(printTextAndClick(logAction));
    });
};

exports.dblComboBoxItemByFormIdNameIndex = function (formId, name, index, logAction) {
  return gIn.wrap(`Double Click combobox item by formId: ${formId}, name: ${name}, index: ${index}`,
    logAction, function () {
      return gT.s.browser.executeScript(`return tiaEJ.hEById.getInputElByFormName('${formId}', '${name}');`, false)
        .then(function (inputEl) {
          return clickWrapper(inputEl);
        })
        .then(function () {
          return gT.sOrig.driver.wait(function () {
            return gT.s.browser.executeScriptWrapper(`return tiaEJ.hEById.isCBPickerVisibleByFormName('${formId}', '${name}', ${index});`);
          }, 5000);
        })
        .then(function () {
          return gT.s.browser.executeScript(
            `return tiaEJ.hEById.getCBItemByFormNameIndex('${formId}', '${name}', ${index});`,
            false);
        })
        .then(printTextAndDblClick(logAction));
    });
};

exports.fieldByFormIdName = function (formId, name, logAction) {
  return gIn.wrap(`Click form field item by formId: ${formId}, name: ${name} ... `,
    logAction, function () {
      return gT.s.browser.executeScript(`return tiaEJ.hEById.getInputElByFormName('${formId}', '${name}');`, false)
        .then(function (inputEl) {
          return clickWrapper(inputEl);
        });
    });
};

exports.checkBoxByFormIdName = function (formId, name, logAction) {
  return gIn.wrap(`Click checkBox (name: ${name}) on form (id: ${formId}) ... `,
    logAction, function () {
      return gT.s.browser.executeScript(`return tiaEJ.hEById.getElByFormName('${formId}', '${name}');`, false)
        .then(printTextAndClick(logAction));
    });
};

exports.comboBoxItemByFormIdNameField = function (formId, name, fieldValue, fieldName, logAction) {
  return gIn.wrap(`Click combobox item by formId: ${formId}, name: ${name}, fieldName: ${fieldName}, fieldValue: ${fieldValue}`,
    logAction, function () {
      return gT.s.browser.executeScript(`return tiaEJ.hEById.getInputElByFormName('${formId}', '${name}');`, false)
        .then(function (inputEl) {
          return clickWrapper(inputEl);
        })
        .then(function () {
          return gT.sOrig.driver.wait(function () {
            return gT.s.browser.executeScriptWrapper(`return tiaEJ.hEById.isCBPickerVisibleByFormName('${formId}', '${name}');`);
          }, 5000);
        })
        .then(function () {
          return gT.s.browser.executeScript(
            `return tiaEJ.hEById.getCBItemByFormNameField('${formId}', '${name}', ${valueToParameter(fieldValue)}, '${fieldName}');`,
            false);
        })
        .then(printTextAndClick(logAction));
    });
};

function clickById(fName) {
  return function (dynId) {
    gIn.tracer.trace3(`${fName}:, id of found element: ${dynId}`);
    return clickWrapper(gT.sOrig.driver.findElement(gT.sOrig.by.id(dynId)));
  };
}

/**
 * Clicks on tab with specified itemId of component specified by id.
 * @param id - component HTML id.
 * @param itemId - tab itemId
 * @param logAction - enable/disalbe logging for this action.
 * @returns {*}
 */
exports.tabByIdItemId = function (id, itemId, logAction) {
  return gIn.wrap(`Click on tab ${itemId} of component ${id} ... `, logAction, function () {
    return gT.s.browser.executeScriptWrapper(`return tiaEJ.dynId.getTabIdByIdItemId('${id}', '${itemId}')`)
      .then(clickById('clickTabByIdItemId'));
  });
};

/**
 * Clicks on tab with specified text of component specified by id.
 * @param id - component HTML id.
 * @param {Object/string} text - tab itemId
 * @param logAction - enable/disalbe logging for this action.
 * @returns {*}
 * TODO: Not sure for unicode text for logs (text value can be utf-8 encoded).
 * Object: str for search, str for log.
 */
exports.tabByIdText = function (id, text, logAction) {
  return gIn.wrap(`Click on tab with text ${text} of component ${id} ... `, logAction, function () {
    return gT.s.browser.executeScriptWrapper(`return tiaEJ.dynId.getTabIdByIdText ('${id}', '${text}')`)
      .then(clickById('clickTabByIdText'));
  });
};

/**
 * Clicks on tab with specified locale key of component specified by id.
 * @param id - component HTML id.
 * @param {Object/string} locKey - tab itemId
 * @param logAction - enable/disalbe logging for this action.
 * @returns {*}
 * TODO: Not sure for unicode text for logs (text value can be utf-8 encoded).
 * Object: str for search, str for log.
 */
exports.tabByIdLocKey = function (id, locKey, logAction) {
  return gIn.wrap(`Click on tab with locale key ${locKey} of component ${id} ... `, logAction, function () {
    return gT.s.browser.executeScriptWrapper(`return tiaEJ.dynId.getTabIdByIdLocKey('${id}', '${locKey}')`)
      .then(clickById('clickTabByIdLocKey'));
  });
};

/**
 * Clicks on a component using id, reference, localization key.
 * @param id - component HTML id.
 * @param ref - reference inside component found by id.
 * @param key - key in locale.
 * @param logAction - enable/disalbe logging for this action.
 * @returns {*}
 * Object: str for search, str for log.
 */
exports.compByIdRefKey = function (id, ref, key, logAction) {
  return gIn.wrap(`Click on tab by id (${id}), reference (${ref}), key ${key} ... `, logAction, function () {
    return gT.s.browser.executeScriptWrapper(`return tiaEJ.dynId.getByIdRefKey('${id}', '${ref}', '${key}')`)
      .then(clickById('clickByIdRefKey'));
  });
};
<commit_msg>Improved stability API for comboboxes.<commit_after>'use strict';
/* globals gT: true */
/* globals gIn: true */

function clickWrapper(webEl) {
  if (gT.engineConsts.extJsClickDelay) {
    gT.s.driver.sleep(gT.engineConsts.extJsClickDelay, false);
  }
  return webEl.click();
}

function dblClickWrapper(webEl) {
  if (gT.engineConsts.extJsClickDelay) {
    gT.s.driver.sleep(gT.engineConsts.extJsClickDelay, false);
  }
  return new gT.sOrig.ActionSequence(gT.sOrig.driver).doubleClick(webEl).perform();
}

function printTextAndDblClick(logAction) {
  return function (webEl) {
    webEl.getText()
      .then(function (text) { // Using of selenium queue, so not then.then.
        gIn.logger.logIfNotDisabled(', Item text: "' + text + '" ... ', logAction);
      });
    return dblClickWrapper(webEl);
  };
}

function printTextAndClick(logAction) {
  return function (webEl) {
    webEl.getText()
      .then(function (text) { // Using of selenium queue, so not then.then.
        gIn.logger.logIfNotDisabled(', Item text: "' + text + '" ... ', logAction);
      });
    return clickWrapper(webEl);
  };
}

function printTextAndClickTextItem(logAction) {
  return function (webEl) {
    return webEl.findElement(gT.sOrig.by.css('.x-tree-node-text'))
      .then(function (el) {
        el.getText()
          .then(function (text) { // Using of selenium queue, so not then.then.
            gIn.logger.logIfNotDisabled(', Item text: "' + text + '" ... ', logAction);
          });
        return clickWrapper(el);
      });
    // webEl.getAttribute('class')
    //   .then(function (attrVal) {
    //     gIn.logger.logIfNotDisabled(', Item class: "' + attrVal + '"', logAction);
    //   });
    // webEl.getText()
    //   .then(function (text) { // Using of selenium queue, so not then.then.
    //     gIn.logger.logIfNotDisabled(', Item text: "' + text + '" ... ', logAction);
    //   });

  };
}

/**
 * Some ExtJs API functions require parameter to be numeric type and not equal string.
 * @param val
 * @returns {string}
 */
function valueToParameter(val) {
  if (typeof val === 'number') {
    return `${val}`;
  }
  if (typeof val === 'string') {
    return `'${val}'`;
  }
}

// Note that for tree only expanded nodes are taking into account.
exports.tableItemByIndex = function (tableId, tableName, itemIndex, logAction) {
  return gIn.wrap(`Click table '${tableName}' item by index '${itemIndex}'`, logAction, function () {
    return gT.s.browser.executeScript(`return tiaEJ.hEById.getTableItemByIndex('${tableId}', ${itemIndex});`, false)
      .then(printTextAndClickTextItem(logAction));
  });
};

exports.tableItemByField = function (tableId, tableName, fieldValue, fieldName, logAction) {
  fieldName = fieldName ? fieldName : 'name';
  return gIn.wrap(`Click table '${tableName}' item, fieldValue: ${valueToParameter(fieldValue)}, fieldName: '${fieldName}'`,
    logAction, function () {
      return gT.s.browser.executeScript(
        `return tiaEJ.hEById.getTableItemByField('${tableId}', ${valueToParameter(fieldValue)}, '${fieldName}');`
        , false
      ).then(printTextAndClickTextItem(logAction));
    });
};

exports.tableItemByFieldLocKey = function (tableId, tableName, fieldValueKey, fieldName, logAction) {
  fieldName = fieldName ? fieldName : 'name';
  return gIn.wrap(`Click table '${tableName}' item, fieldValue: '${fieldValueKey}', fieldName: '${fieldName}'`,
    logAction, function () {
      return gT.s.browser.executeScript(
        `return tiaEJ.hEById.getTableItemByFieldLocKey('${tableId}', '${fieldValueKey}', '${fieldName}');`
        , false
      ).then(printTextAndClickTextItem(logAction));
    });
};

exports.tableItemByFieldId = function (tableId, tableName, id, logAction) {
  return gIn.wrap(`Click table '${tableName}' item, with id: '${id}'`, logAction, function () {
    return gT.s.browser.executeScript(
      `return tiaEJ.hEById.getTableItemByField('${tableId}', ${valueToParameter(id)}, 'id');`
      , false
    ).then(printTextAndClickTextItem(logAction));
  });
};

exports.comboBoxItemByIndex = function (cbId, itemIndex, logAction) {
  return gIn.wrap(``, logAction, function () {
    return gT.s.browser.executeScript(`return tiaEJ.hEById.getNameAndLabels('${cbId}');`, false)
      .then(function (obj) {
        gIn.logger.logIfNotDisabled(
          `Click combo box(name: '${obj.name}', label: '${obj.label}'), item #${itemIndex}`, logAction);
      })
      .then(function () {
        return gT.s.browser.executeScript(`return tiaEJ.hEById.getInputEl('${cbId}');`, false);
      })
      .then(function (inputEl) {
        return clickWrapper(inputEl);
      })
      .then(function () {
        return gT.sOrig.driver.wait(function () {
          return gT.s.browser.executeScriptWrapper(`return tiaEJ.hEById.isCBPickerVisible('${cbId}', ${itemIndex});`);
        }, 5000);
      })
      .then(function () {
        return gT.s.browser.executeScript(`return tiaEJ.hEById.getCBItemByIndex('${cbId}', ${itemIndex});`, false);
      })
      .then(printTextAndClick(logAction));
  });
};

exports.comboBoxItemByField = function (cbId, fieldValue, fieldName, logAction) {
  return gIn.wrap(``, logAction, function () {
    return gT.s.browser.executeScript(`return tiaEJ.hEById.getNameAndLabels('${cbId}');`, false)
      .then(function (obj) {
        gIn.logger.logIfNotDisabled(
          `Click combo box(name: '${obj.name}', label: '${obj.label}'), by field (name: ${fieldName}, value: ${fieldValue})`,
          logAction);
      })
      .then(function () {
        return gT.s.browser.executeScript(`return tiaEJ.hEById.getInputEl('${cbId}');`, false);
      })
      .then(function (inputEl) {
        return clickWrapper(inputEl);
      })
      .then(function () {
        return gT.sOrig.driver.wait(function () {
          return gT.s.browser.executeScriptWrapper(`return tiaEJ.hEById.isCBPickerVisible('${cbId}');`);
        }, 5000);
      })
      .then(function () {
        return gT.s.browser.executeScript(
          `return tiaEJ.hEById.getCBItemByField('${cbId}', ${valueToParameter(fieldValue)}, '${fieldName}');`,
          false);
      })
      .then(printTextAndClick(logAction));
  });
};

exports.comboBoxItemByFormIdNameIndex = function (formId, name, index, logAction) {
  return gIn.wrap(`Click combobox item by formId: ${formId}, name: ${name}, index: ${index}`,
    logAction, function () {
      return gT.s.browser.executeScript(`return tiaEJ.hEById.getInputElByFormName('${formId}', '${name}');`, false)
        .then(function (inputEl) {
          return clickWrapper(inputEl);
        })
        .then(function () {
          return gT.sOrig.driver.wait(function () {
            return gT.s.browser.executeScriptWrapper(`return tiaEJ.hEById.isCBPickerVisibleByFormName('${formId}', '${name}', ${index});`);
          }, 5000);
        })
        .then(function () {
          return gT.s.browser.executeScript(
            `return tiaEJ.hEById.getCBItemByFormNameIndex('${formId}', '${name}', ${index});`,
            false);
        })
        .then(printTextAndClick(logAction));
    });
};

exports.dblComboBoxItemByFormIdNameIndex = function (formId, name, index, logAction) {
  return gIn.wrap(`Double Click combobox item by formId: ${formId}, name: ${name}, index: ${index}`,
    logAction, function () {
      return gT.s.browser.executeScript(`return tiaEJ.hEById.getInputElByFormName('${formId}', '${name}');`, false)
        .then(function (inputEl) {
          return clickWrapper(inputEl)
            .then(function () {
              return gT.e.wait.ajaxRequestsFinish(5000, false);
            });
        })
        .then(function () {
          return gT.sOrig.driver.wait(function () {
            return gT.s.browser.executeScriptWrapper(`return tiaEJ.hEById.isCBPickerVisibleByFormName('${formId}', '${name}', ${index});`);
          }, 3000);
        })
        .then(function () {
        }, function (err) { // One more chance to click combobox.
          return clickWrapper(inputEl)
            .then(function () {
              return gT.e.wait.ajaxRequestsFinish(5000, false);
            })
            .then(function () {
              return gT.sOrig.driver.wait(function () {
                return gT.s.browser.executeScriptWrapper(`return tiaEJ.hEById.isCBPickerVisibleByFormName('${formId}', '${name}');`);
              }, 3000);
            });
        })
        .then(function () {
          return gT.s.browser.executeScript(
            `return tiaEJ.hEById.getCBItemByFormNameIndex('${formId}', '${name}', ${index});`,
            false);
        })
        .then(printTextAndDblClick(logAction));
    });
};

exports.fieldByFormIdName = function (formId, name, logAction) {
  return gIn.wrap(`Click form field item by formId: ${formId}, name: ${name} ... `,
    logAction, function () {
      return gT.s.browser.executeScript(`return tiaEJ.hEById.getInputElByFormName('${formId}', '${name}');`, false)
        .then(function (inputEl) {
          return clickWrapper(inputEl);
        });
    });
};

exports.checkBoxByFormIdName = function (formId, name, logAction) {
  return gIn.wrap(`Click checkBox (name: ${name}) on form (id: ${formId}) ... `,
    logAction, function () {
      return gT.s.browser.executeScript(`return tiaEJ.hEById.getElByFormName('${formId}', '${name}');`, false)
        .then(printTextAndClick(logAction));
    });
};

exports.comboBoxItemByFormIdNameField = function (formId, name, fieldValue, fieldName, logAction) {
  return gIn.wrap(`Click combobox item by formId: ${formId}, name: ${name}, fieldName: ${fieldName}, fieldValue: ${fieldValue}`,
    logAction, function () {
      return gT.s.browser.executeScript(`return tiaEJ.hEById.getInputElByFormName('${formId}', '${name}');`, false)
        .then(function (inputEl) {
          return clickWrapper(inputEl)
            .then(function () {
              return gT.e.wait.ajaxRequestsFinish(5000, false);
            });
        })
        .then(function () {
          return gT.sOrig.driver.wait(function () {
            return gT.s.browser.executeScriptWrapper(`return tiaEJ.hEById.isCBPickerVisibleByFormName('${formId}', '${name}');`);
          }, 3000);
        })
        .then(function () {
        }, function (err) { // One more chance to click combobox.
          return clickWrapper(inputEl)
            .then(function () {
              return gT.e.wait.ajaxRequestsFinish(5000, false);
            })
            .then(function () {
              return gT.sOrig.driver.wait(function () {
                return gT.s.browser.executeScriptWrapper(`return tiaEJ.hEById.isCBPickerVisibleByFormName('${formId}', '${name}');`);
              }, 3000);
            });
        })
        .then(function () {
          return gT.s.browser.executeScript(
            `return tiaEJ.hEById.getCBItemByFormNameField('${formId}', '${name}', ${valueToParameter(fieldValue)}, '${fieldName}');`,
            false);
        })
        .then(printTextAndClick(logAction));
    });
};

function clickById(fName) {
  return function (dynId) {
    gIn.tracer.trace3(`${fName}:, id of found element: ${dynId}`);
    return clickWrapper(gT.sOrig.driver.findElement(gT.sOrig.by.id(dynId)));
  };
}

/**
 * Clicks on tab with specified itemId of component specified by id.
 * @param id - component HTML id.
 * @param itemId - tab itemId
 * @param logAction - enable/disalbe logging for this action.
 * @returns {*}
 */
exports.tabByIdItemId = function (id, itemId, logAction) {
  return gIn.wrap(`Click on tab ${itemId} of component ${id} ... `, logAction, function () {
    return gT.s.browser.executeScriptWrapper(`return tiaEJ.dynId.getTabIdByIdItemId('${id}', '${itemId}')`)
      .then(clickById('clickTabByIdItemId'));
  });
};

/**
 * Clicks on tab with specified text of component specified by id.
 * @param id - component HTML id.
 * @param {Object/string} text - tab itemId
 * @param logAction - enable/disalbe logging for this action.
 * @returns {*}
 * TODO: Not sure for unicode text for logs (text value can be utf-8 encoded).
 * Object: str for search, str for log.
 */
exports.tabByIdText = function (id, text, logAction) {
  return gIn.wrap(`Click on tab with text ${text} of component ${id} ... `, logAction, function () {
    return gT.s.browser.executeScriptWrapper(`return tiaEJ.dynId.getTabIdByIdText ('${id}', '${text}')`)
      .then(clickById('clickTabByIdText'));
  });
};

/**
 * Clicks on tab with specified locale key of component specified by id.
 * @param id - component HTML id.
 * @param {Object/string} locKey - tab itemId
 * @param logAction - enable/disalbe logging for this action.
 * @returns {*}
 * TODO: Not sure for unicode text for logs (text value can be utf-8 encoded).
 * Object: str for search, str for log.
 */
exports.tabByIdLocKey = function (id, locKey, logAction) {
  return gIn.wrap(`Click on tab with locale key ${locKey} of component ${id} ... `, logAction, function () {
    return gT.s.browser.executeScriptWrapper(`return tiaEJ.dynId.getTabIdByIdLocKey('${id}', '${locKey}')`)
      .then(clickById('clickTabByIdLocKey'));
  });
};

/**
 * Clicks on a component using id, reference, localization key.
 * @param id - component HTML id.
 * @param ref - reference inside component found by id.
 * @param key - key in locale.
 * @param logAction - enable/disalbe logging for this action.
 * @returns {*}
 * Object: str for search, str for log.
 */
exports.compByIdRefKey = function (id, ref, key, logAction) {
  return gIn.wrap(`Click on tab by id (${id}), reference (${ref}), key ${key} ... `, logAction, function () {
    return gT.s.browser.executeScriptWrapper(`return tiaEJ.dynId.getByIdRefKey('${id}', '${ref}', '${key}')`)
      .then(clickById('clickByIdRefKey'));
  });
};
<|endoftext|>


<commit_before>'use strict';

/* rule.js
 * Expresses CSS rules.
 */

const create = require('./create');

let Rule = create(function(name, list1, list2){

}, {

});

module.exports = exports = Rule;
<commit_msg>Add rule<commit_after>'use strict';

/* rule.js
 * Expresses CSS rules.
 */

const create = require('./create');

let Rule = create(function(names, list1, list2){
  this.names = names;
  if (typeof name === 'string') this.names = [names];

  if (this.names.length > 1 || arguments.length === 2) {
    this.type = 'selector';
    this.selectors = this.names || [];
    this.items = list1 || [];
  } else if (this.names.length === 1 && this.names[0][0] === '@') {
    this.type = 'at-rule';
    if (arguments.length === 3) {
      this.type += 'expressive';
      this.name = this.names[0];
      this.expression = list1 || [];
      this.items = list2 || [];
    } else if (arguments.length === 2) this.input = list1;
  } else {
    this.type = 'unknown';
    this.items = [].concat(...(Array.prototype.slice.call(arguments, 1))) || [];
  }
}, {

  add: function(){
    this.items.push(...arguments);
    return this;
  },

  remove: function(loc){
    this.items.splice(loc, 1);
    return this;
  },

  set: function(loc, value){
    this.items[loc] = value;
    return this;
  },

});

module.exports = exports = Rule;
<|endoftext|>


<commit_before>"use strict";

let events = require( 'events' );
let jsonGate = require( 'json-gate' );

let pem = require( './pem.js' );
let time = require( './time.js' );
let DefinitionSink = require( './definition-sink.js' );
let HormoneSink = require( './hormone-sink.js' );

// The hormone names must not start or end with '/' and must not be empty
let nameSchema = jsonGate.createSchema( {
	type: 'string',
	pattern: '^[^/]+.*$'
} );

let definitionRE = /^definition\/(.*)$/;
let hormoneRE = /^hormone\/(.*)$/;

class Sink extends events.EventEmitter {

	constructor( es, filter, certCheck ) {

		super();

		// TODO: Check es

		this._es = es;
		this._filter = filter;
		this._certCheck = certCheck;

		// Create some stores ...
		this._subscriptions = {}; // Received definitions and subscribed hormones
		this._hormones = {};      // Last received hormone by name

		// Subscribe to defintion messages
		es._subscribe( 'definition/' + filter, ( topic, payload ) => {
			this._receivedDefinition( topic, payload );
		} ).catch( ( err ) => {
			this.emit( 'error', err );
		} );

	}

	destroy() {

		// Unsubscribe definition topic
		return this._es._unsubscribe( 'defintion/' + this._filter ).then( () => {

			let unsubJobs = [];

			// Unsubscribe all hormones
			for( let name in this._subscriptions ) {
				unsubJobs.push( this._removeHormoneSource( name ) );
			}

			// Wait for all unsubscriptions to be done
			return Promise.all( unsubJobs );

		});

	}

	_receivedDefinition( topic, payload ) {

		// Extract name
		let tmp = definitionRE.exec( topic );
		if( ! tmp ) {
			return this.emit( 'receiveError', new Error( "Received no definition" ) );
		}
		let name = tmp[ 1 ];

		// The payload decides what to do
		if( payload == '' ) {
			// Remove hormone source
			this._removeHormoneSource( name ).catch( ( err ) => {
				this.emit( 'receiveError', err );
			} );
		} else {
			// Add hormone source
			this._addHormoneSource( name, payload ).catch( ( err ) => {
				this.emit( 'receiveError', err );
			} );
		}

	}

	_addHormoneSource( name, payload ) {

		// Store for definition data
		let definition;

		return new Promise( ( resolve ) => {

			// Create new definition from payload
			// Makes sure the format and signature are vaild
			definition = new DefinitionSink( payload );

			// At this point the payload was readable and okay
			resolve();

		} ).then( () => {

			// Check the certificate
			let data = definition.data;
			return Promise.all( [
				pem.verifySigningChain( data.cert, this._es._ca ),
				pem.readCertificateInfo( data.cert )
			] )

		} ).then( ( results ) => {

			// Is cert signed by CA?
			if( ! results[0] ) {
				throw new Error( "Certificate has not been signed by CA" );
			}

			// Is the cert still valid?
			// First get current time. If we have an ESdummy, read time from es.
			// Otherwise get the current time from the OS
			let now = time.now();
			if( results[1].validity.start > now ) {
				throw new Error( "Certificate is valid in the future" );
			}
			if( results[1].validity.end < now ) {
				throw new Error( "Certificate has been expired" );
			}

			// Own check function
			if( typeof this._certCheck == 'function' ) {
				// The check function my return a promise, a value (-> everything is
				// ok) or throws an error
				return this._certCheck( name, results[ 1 ] );
			}

		} ).then( () => {

			// If the hormone is known, remove the old one first
			if( this._subscriptions[ name ] ) this._removeHormoneSource( name );

			// Subscribe to hormone
			return this._es._subscribe( 'hormone/' + name, ( topic, payload ) => {

				// Skip empty messages
				if( payload == '' ) return;

				// Hand over hormone to handler
				this._receivedHormone( topic, payload );

			} );

		} ).then( () => {

			// Store handle
			this._subscriptions[ name ] = definition;

			// Report that we started listening to hormones
			this.emit( 'subscribe', name, definition );

		} );

	}

	_removeHormoneSource( name ) {

		// Check if we know the hormone source that shall be removed
		if( ! this._subscriptions[ name ] ) {
			return Promise.reject( new Error( "Received unkown definition removal" ) );
		}

		// Unsubscribe hormone
		return this._es._unsubscribe( 'hormone/' + name ).then( () => {

			// Remove timeout if it is pending
			if( this._hormones[ name ].timeout ) {
				clearTimeout( this._hormones[ name ].timeout );
			}

			// Remove subscription and last hormone
			delete this._subscriptions[ name ];
			delete this._hormones[ name ];

		} );

	}

	_receivedHormone( topic, payload ) {
		try {

			// Extract name
			let tmp = hormoneRE.exec( topic );
			if( ! tmp ) throw new Error( "Received no hormone" );
			let name = tmp[ 1 ];

			// Check if we have a definition for the received hormone
			if( ! this._subscriptions[ name ] ) throw new Error( "Received unkown hormone" );

			// Interprete hormone
			let hormone = new HormoneSink( this._subscriptions[ name ], payload );

			// At this point the hormone is okay :) At least the signature.

			// Keep the current timestamp
			let receivedAt = time.now();

			// If this is the first hormone we recieved under this name, make sure we
			// have at least some dummy data to compare with
			let formerHormone = this._hormones[ name ] ? this._hormones[ name ] : {
				isFresh: true,
				err: 0,
				timeout: null,
				stateChangedAt: time.now()
			} ;

			// Stop expiration timeout if present
			if( formerHormone.timeout ) clearTimeout( formerHormone.timeout );

			// Further checks:
			let stateChanged = false;
			// - Freshness
			let isFresh = hormone.isFresh;
			if( isFresh && ! formerHormone.isFresh ) {
				// The expired hormone went fresh -> tell the good news!
				this.emit( 'hormoneRefresh', name, hormone );
				stateChanged = true;
			} else if( ! isFresh && formerHormone.isFresh ) {
				// The received hormone has expired!
				this.emit( 'hormoneExpired', name, hormone );
				stateChanged = true;
			}
			// - Error
			let err = hormone.error;
			if( err == 0 && formerHormone.err != err ) {
				// The hormone reported recovery
				this.emit( 'hormoneRecover', name, hormone );
				stateChanged = true;
			} else if( err > 0 && formerHormone.err != err ) {
				// Some error occured!
				this.emit( 'hormoneError', name, hormone );
				stateChanged = true;
			}

			// If the hormone is fresh, set an expire timeout
			let timeout = null;
			if( isFresh ) timeout = setTimeout( () => {
				// The hormone just expired!
				this._hormones[ name ].isFresh = false;
				this._hormones[ name ].timeout = null;
				this.emit( 'hormoneExpired', name, hormone );
			}, hormone.freshness * 1000 );

			// Store all hormone related data
			this._hormones[ name ] = {
				receivedAt: receivedAt,
				stateChangedAt: stateChanged ? receivedAt : formerHormone.stateChangedAt,
				hormone: hormone,
				isFresh: isFresh,
				err: err,
				timeout: timeout
			};

			// Tell the user we got a new hormone
			this.emit( 'hormone', name, hormone );

		} catch( err ) {
			this.emit( 'receiveError', err );
		}

	}

	_fetchHormones( cond ) {
		if( typeof cond !== 'object' ) cond = {};

		// Copy all hormones into an array
		let hormones = [];
		for( let name in this._hormones ) {
			let h = this._hormones[ name ];

			// First we assume that the hormone is part of the required set
			let add = true;

			// Check conditions. If a condition is not met, add is set false
			// - A certain freshness is required
			if( cond.isFresh !== undefined && h.isFresh !== cond.isFresh ) {
				add = false
			}
			// - The hormone error state is interesting
			if( cond.err !== undefined ) {
				if( cond.err && h.err <= 0 ) {
					add = false;
				} else if( ! cond.err && h.err > 0 ) {
					add = false;
				}
			}

			// If no show stopper occured, add the hormone to the set
			if( add ) hormones.push( {
				name: name,
				sentAt: h.hormone.timestamp,
				receivedAt: h.receivedAt,
				stateChangedAt: h.stateChangedAt,
				data: h.hormone.data,
				err: h.err,
				isFresh: h.isFresh
			);

		}

		return hormones;

	}

	get hormones() { return this._fetchHormones( {} ); }
	get expiredHormones() { return this._fetchHormones( { isFresh: false } ); }
	get errorHormones() { return this._fetchHormones( { err: true } ); }
	get goodHormones() { return this._fetchHormones( { err: false, isFresh: true } ); }

}

module.exports = Sink;
<commit_msg>Added option checks to sink class<commit_after>"use strict";

let events = require( 'events' );
let jsonGate = require( 'json-gate' );

let pem = require( './pem.js' );
let time = require( './time.js' );
let DefinitionSink = require( './definition-sink.js' );
let HormoneSink = require( './hormone-sink.js' );

// The hormone names must not start or end with '/' and must not be empty
let nameSchema = jsonGate.createSchema( {
	type: 'string',
	pattern: '^[^/]+.*$'
} );

let definitionRE = /^definition\/(.*)$/;
let hormoneRE = /^hormone\/(.*)$/;

class Sink extends events.EventEmitter {

	constructor( es, filter, certCheck ) {

		super();

		// TODO: Check es
		this._es = es;

		// Check given filter
		if( typeof filter != 'string' ) {
			throw new Error( "Filter must be a string" );
		}
		nameSchema.validate( filter );
		this._filter = filter;

		// If the cert check is given an not a function, complain about it
		if( certCheck !== undefined && typeof certCheck != 'function' ) {
			throw new Error( "Given cert check is not a function" );
		}
		this._certCheck = certCheck;

		// Create some stores ...
		this._subscriptions = {}; // Received definitions and subscribed hormones
		this._hormones = {};      // Last received hormone by name

		// Subscribe to defintion messages
		es._subscribe( 'definition/' + filter, ( topic, payload ) => {
			this._receivedDefinition( topic, payload );
		} ).catch( ( err ) => {
			this.emit( 'error', err );
		} );

	}

	destroy() {

		// Unsubscribe definition topic
		return this._es._unsubscribe( 'defintion/' + this._filter ).then( () => {

			let unsubJobs = [];

			// Unsubscribe all hormones
			for( let name in this._subscriptions ) {
				unsubJobs.push( this._removeHormoneSource( name ) );
			}

			// Wait for all unsubscriptions to be done
			return Promise.all( unsubJobs );

		});

	}

	_receivedDefinition( topic, payload ) {

		// Extract name
		let tmp = definitionRE.exec( topic );
		if( ! tmp ) {
			return this.emit( 'receiveError', new Error( "Received no definition" ) );
		}
		let name = tmp[ 1 ];

		// The payload decides what to do
		if( payload == '' ) {
			// Remove hormone source
			this._removeHormoneSource( name ).catch( ( err ) => {
				this.emit( 'receiveError', err );
			} );
		} else {
			// Add hormone source
			this._addHormoneSource( name, payload ).catch( ( err ) => {
				this.emit( 'receiveError', err );
			} );
		}

	}

	_addHormoneSource( name, payload ) {

		// Store for definition data
		let definition;

		return new Promise( ( resolve ) => {

			// Create new definition from payload
			// Makes sure the format and signature are vaild
			definition = new DefinitionSink( payload );

			// At this point the payload was readable and okay
			resolve();

		} ).then( () => {

			// Check the certificate
			let data = definition.data;
			return Promise.all( [
				pem.verifySigningChain( data.cert, this._es._ca ),
				pem.readCertificateInfo( data.cert )
			] )

		} ).then( ( results ) => {

			// Is cert signed by CA?
			if( ! results[0] ) {
				throw new Error( "Certificate has not been signed by CA" );
			}

			// Is the cert still valid?
			// First get current time. If we have an ESdummy, read time from es.
			// Otherwise get the current time from the OS
			let now = time.now();
			if( results[1].validity.start > now ) {
				throw new Error( "Certificate is valid in the future" );
			}
			if( results[1].validity.end < now ) {
				throw new Error( "Certificate has been expired" );
			}

			// Own check function
			if( typeof this._certCheck == 'function' ) {
				// The check function my return a promise, a value (-> everything is
				// ok) or throws an error
				return this._certCheck( name, results[ 1 ] );
			}

		} ).then( () => {

			// If the hormone is known, remove the old one first
			if( this._subscriptions[ name ] ) this._removeHormoneSource( name );

			// Subscribe to hormone
			return this._es._subscribe( 'hormone/' + name, ( topic, payload ) => {

				// Skip empty messages
				if( payload == '' ) return;

				// Hand over hormone to handler
				this._receivedHormone( topic, payload );

			} );

		} ).then( () => {

			// Store handle
			this._subscriptions[ name ] = definition;

			// Report that we started listening to hormones
			this.emit( 'subscribe', name, definition );

		} );

	}

	_removeHormoneSource( name ) {

		// Check if we know the hormone source that shall be removed
		if( ! this._subscriptions[ name ] ) {
			return Promise.reject( new Error( "Received unkown definition removal" ) );
		}

		// Unsubscribe hormone
		return this._es._unsubscribe( 'hormone/' + name ).then( () => {

			// Remove timeout if it is pending
			if( this._hormones[ name ].timeout ) {
				clearTimeout( this._hormones[ name ].timeout );
			}

			// Remove subscription and last hormone
			delete this._subscriptions[ name ];
			delete this._hormones[ name ];

		} );

	}

	_receivedHormone( topic, payload ) {
		try {

			// Extract name
			let tmp = hormoneRE.exec( topic );
			if( ! tmp ) throw new Error( "Received no hormone" );
			let name = tmp[ 1 ];

			// Check if we have a definition for the received hormone
			if( ! this._subscriptions[ name ] ) throw new Error( "Received unkown hormone" );

			// Interprete hormone
			let hormone = new HormoneSink( this._subscriptions[ name ], payload );

			// At this point the hormone is okay :) At least the signature.

			// Keep the current timestamp
			let receivedAt = time.now();

			// If this is the first hormone we recieved under this name, make sure we
			// have at least some dummy data to compare with
			let formerHormone = this._hormones[ name ] ? this._hormones[ name ] : {
				isFresh: true,
				err: 0,
				timeout: null,
				stateChangedAt: time.now()
			} ;

			// Stop expiration timeout if present
			if( formerHormone.timeout ) clearTimeout( formerHormone.timeout );

			// Further checks:
			let stateChanged = false;
			// - Freshness
			let isFresh = hormone.isFresh;
			if( isFresh && ! formerHormone.isFresh ) {
				// The expired hormone went fresh -> tell the good news!
				this.emit( 'hormoneRefresh', name, hormone );
				stateChanged = true;
			} else if( ! isFresh && formerHormone.isFresh ) {
				// The received hormone has expired!
				this.emit( 'hormoneExpired', name, hormone );
				stateChanged = true;
			}
			// - Error
			let err = hormone.error;
			if( err == 0 && formerHormone.err != err ) {
				// The hormone reported recovery
				this.emit( 'hormoneRecover', name, hormone );
				stateChanged = true;
			} else if( err > 0 && formerHormone.err != err ) {
				// Some error occured!
				this.emit( 'hormoneError', name, hormone );
				stateChanged = true;
			}

			// If the hormone is fresh, set an expire timeout
			let timeout = null;
			if( isFresh ) timeout = setTimeout( () => {
				// The hormone just expired!
				this._hormones[ name ].isFresh = false;
				this._hormones[ name ].timeout = null;
				this.emit( 'hormoneExpired', name, hormone );
			}, hormone.freshness * 1000 );

			// Store all hormone related data
			this._hormones[ name ] = {
				receivedAt: receivedAt,
				stateChangedAt: stateChanged ? receivedAt : formerHormone.stateChangedAt,
				hormone: hormone,
				isFresh: isFresh,
				err: err,
				timeout: timeout
			};

			// Tell the user we got a new hormone
			this.emit( 'hormone', name, hormone );

		} catch( err ) {
			this.emit( 'receiveError', err );
		}

	}

	_fetchHormones( cond ) {
		if( typeof cond !== 'object' ) cond = {};

		// Copy all hormones into an array
		let hormones = [];
		for( let name in this._hormones ) {
			let h = this._hormones[ name ];

			// First we assume that the hormone is part of the required set
			let add = true;

			// Check conditions. If a condition is not met, add is set false
			// - A certain freshness is required
			if( cond.isFresh !== undefined && h.isFresh !== cond.isFresh ) {
				add = false
			}
			// - The hormone error state is interesting
			if( cond.err !== undefined ) {
				if( cond.err && h.err <= 0 ) {
					add = false;
				} else if( ! cond.err && h.err > 0 ) {
					add = false;
				}
			}

			// If no show stopper occured, add the hormone to the set
			if( add ) hormones.push( {
				name: name,
				sentAt: h.hormone.timestamp,
				receivedAt: h.receivedAt,
				stateChangedAt: h.stateChangedAt,
				data: h.hormone.data,
				err: h.err,
				isFresh: h.isFresh
			} );

		}

		return hormones;

	}

	get hormones() { return this._fetchHormones( {} ); }
	get expiredHormones() { return this._fetchHormones( { isFresh: false } ); }
	get errorHormones() { return this._fetchHormones( { err: true } ); }
	get goodHormones() { return this._fetchHormones( { err: false, isFresh: true } ); }

}

module.exports = Sink;
<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */

var Post = require('./post');
var Media = require('./media');
var debug = require('debug')('wpcom:site');

/**
 * Create a Site instance
 *
 * @param {WPCOM} wpcom
 * @api public
 */

function Site(id, wpcom){
  if (!(this instanceof Site)) return new Site(id, wpcom);
  this.wpcom = wpcom;

  debug('set `%s` site id', id);
  this._id = id;
}

/**
 * Require site information
 *
 * @param {Object} [query]
 * @param {Function} fn
 * @api public
 */

Site.prototype.get = function(query, fn){
  this.wpcom.sendRequest('/sites/' + this._id, query, null, fn);
};

/**
 * Require site posts list
 *
 * @param {Object} [query]
 * @param {Function} fn
 * @api public
 */

Site.prototype.postsList = function(query, fn){
  this.wpcom.sendRequest('/sites/' + this._id + '/posts', query, null, fn);
};

/**
 * Require the site media list
 *
 * @param {Object} [query]
 * @param {Function} fn
 * @api public
 */

Site.prototype.mediaList = function(query, fn){
  this.wpcom.sendRequest('/sites/' + this._id + '/media', query, null, fn);
};

/**
 * List the users of a site
 *
 * @param {Object} [query]
 * @param {Function} fn
 * @api public
 */

Site.prototype.usersList = function(query, fn){
  this.wpcom.sendRequest('/sites/' + this._id + '/users', query, null, fn);
};

/**
 * Create a `Post` instance
 *
 * @param {String} id
 * @api public
 */

Site.prototype.post = function(id){
  return Post(id, this._id, this.wpcom);
};

/**
 * Add a new blog post
 *
 * @param {Object} body
 * @param {Function} fn
 * @return {Post} new Post instance
 */

Site.prototype.addPost = function(body, fn){
  var post = Post(null, this._id, this.wpcom);
  post.add(body, fn);
  return post;
};

/**
 * Delete a blog post
 *
 * @param {String} id
 * @param {Function} fn
 * @return {Post} remove Post instance
 */

Site.prototype.deletePost = function(id, fn){
  var post = Post(id, this._id, this.wpcom);
  post.delete(fn);
  return post;
};

/**
 * Create a `Media` instance
 *
 * @param {String} id
 * @api public
 */

Site.prototype.media = function(id){
  return Media(id, this._id, this.wpcom);
};

/**
 * Add a media from a file
 *
 * @param {Array|String} files
 * @param {Function} fn
 * @return {Post} new Post instance
 */

Site.prototype.addMediaFile = function(files, fn){
  var media = Media(null, this._id, this.wpcom);
  media.addFile(files, fn);
  return media;
};

/**
 * Add a new media from url
 *
 * @param {Array|String} files
 * @param {Function} fn
 * @return {Post} new Post instance
 */

Site.prototype.addMediaUrl = function(files, fn){
  var media = Media(null, this._id, this.wpcom);
  media.addUrl(files, fn);
  return media;
};

/**
 * Expose `Site` module
 */

module.exports = Site;
<commit_msg>site: rename media methods<commit_after>/**
 * Module dependencies.
 */

var Post = require('./post');
var Media = require('./media');
var debug = require('debug')('wpcom:site');

/**
 * Create a Site instance
 *
 * @param {WPCOM} wpcom
 * @api public
 */

function Site(id, wpcom){
  if (!(this instanceof Site)) return new Site(id, wpcom);
  this.wpcom = wpcom;

  debug('set `%s` site id', id);
  this._id = id;
}

/**
 * Require site information
 *
 * @param {Object} [query]
 * @param {Function} fn
 * @api public
 */

Site.prototype.get = function(query, fn){
  this.wpcom.sendRequest('/sites/' + this._id, query, null, fn);
};

/**
 * Require site posts list
 *
 * @param {Object} [query]
 * @param {Function} fn
 * @api public
 */

Site.prototype.postsList = function(query, fn){
  this.wpcom.sendRequest('/sites/' + this._id + '/posts', query, null, fn);
};

/**
 * Require the site media list
 *
 * @param {Object} [query]
 * @param {Function} fn
 * @api public
 */

Site.prototype.mediaList = function(query, fn){
  this.wpcom.sendRequest('/sites/' + this._id + '/media', query, null, fn);
};

/**
 * List the users of a site
 *
 * @param {Object} [query]
 * @param {Function} fn
 * @api public
 */

Site.prototype.usersList = function(query, fn){
  this.wpcom.sendRequest('/sites/' + this._id + '/users', query, null, fn);
};

/**
 * Create a `Post` instance
 *
 * @param {String} id
 * @api public
 */

Site.prototype.post = function(id){
  return Post(id, this._id, this.wpcom);
};

/**
 * Add a new blog post
 *
 * @param {Object} body
 * @param {Function} fn
 * @return {Post} new Post instance
 */

Site.prototype.addPost = function(body, fn){
  var post = Post(null, this._id, this.wpcom);
  post.add(body, fn);
  return post;
};

/**
 * Delete a blog post
 *
 * @param {String} id
 * @param {Function} fn
 * @return {Post} remove Post instance
 */

Site.prototype.deletePost = function(id, fn){
  var post = Post(id, this._id, this.wpcom);
  post.delete(fn);
  return post;
};

/**
 * Create a `Media` instance
 *
 * @param {String} id
 * @api public
 */

Site.prototype.media = function(id){
  return Media(id, this._id, this.wpcom);
};

/**
 * Add a media from a file
 *
 * @param {Array|String} files
 * @param {Function} fn
 * @return {Post} new Post instance
 */

Site.prototype.addMediaFiles = function(files, fn){
  var media = Media(null, this._id, this.wpcom);
  media.addFiles(files, fn);
  return media;
};

/**
 * Add a new media from url
 *
 * @param {Array|String} files
 * @param {Function} fn
 * @return {Post} new Post instance
 */

Site.prototype.addMediaUrls = function(files, fn){
  var media = Media(null, this._id, this.wpcom);
  media.addUrls(files, fn);
  return media;
};

/**
 * Expose `Site` module
 */

module.exports = Site;
<|endoftext|>


<commit_before>var _ = require('lazy.js');
var blessed = require('blessed');
var path = require('path');
var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));

var Editor = require('./editor');
var Coordinate = require('./coordinate');
var markup = require('./markup');
var util = require('./util');
var textUtil = require('./textUtil');

function Slap (opts) {
  var self = this;

  if (!(self instanceof blessed.Node)) { return new Slap(opts); }

  blessed.Screen.call(self, opts);

  self.header = new blessed.Box(_({
    tags: true,
    top: 0,
    left: 0,
    right: 0,
    height: 1
  }).merge(opts.header || {}).merge({parent: self}).toObject());

  self.editor = new Editor(_({
    parent: self,
    top: 1,
    left: 0,
    right: 0,
    bottom: 0
  }).merge(opts.editor || {}).toObject());
  self.editor.focus();

  self._initHandlers().render();
}
Slap.prototype.__proto__ = blessed.Screen.prototype;

Slap.normalizePath = function (givenPath) {
  if (!givenPath) { givenPath = ''; }
  if (givenPath[0] === '~') { givenPath = path.join(process.env.HOME, givenPath.slice(1)); }
  return path.normalize(givenPath);
};
Slap.prototype.path = util.getterSetter('path', null, Slap.normalizePath);
Slap.prototype.open = function (givenPath) {
  var self = this;
  givenPath = Slap.normalizePath(givenPath);
  return fs.readFileAsync(givenPath).then(function (data) {
    self.editor.text(data, givenPath.split('.').pop());
    return self.path(givenPath);
  });
};
Slap.prototype.save = function (givenPath) {
  var self = this;
  givenPath = givenPath ? Slap.normalizePath(givenPath) : self.path();
  if (!givenPath) { return; }

  var text = self.editor.text();
  return fs.writeFileAsync(givenPath, text, {flags: 'w'})
    .then(function () {
      self.editor.changeStack.save();
      self.emit('save', givenPath, text);
      return self.path(givenPath).message("saved to " + givenPath, 'success');
    })
    .catch(function (err) {
      switch (err.code) {
        case 'EACCES': case 'EISDIR':
          self.message(err.message, 'error');
          break;
        default: throw err;
      }
    });
};
Slap.prototype.saveAs = function () {
  var self = this;
  return self.prompt("save as").then(function (results) {
    if (results) { return self.save(results[0]); }
  });
};

Slap.prototype.prompt = function (prompts) {
  var self = this;

  return new Promise(function (resolve, reject) {
    prompts = typeof prompts !== 'string' ? prompts : [prompts];

    self.closePrompt();
    self.editor.bottom += prompts.length;
    self.form = new blessed.Form({
      parent: self,
      height: prompts.length,
      left: 0,
      right: 0,
      bottom: 0
    });

    var labelWidth = _(prompts).pluck('length').max() + 3;
    var label = new blessed.Box({
      parent: self.form,
      tags: true,
      content: prompts.map(function (prompt) {
        return markup.markupLine(
          (' ' + prompt + ':' + _.repeat(' ', labelWidth).join('')).slice(0, labelWidth),
        self.options.style.prompt || self.options.style.main);
      }).join('\n'),
      top: 0,
      left: 0,
      width: labelWidth,
      bottom: 0
    });

    function done (submit) {
      self.closePrompt(true);

      // FIXME: nasty hack to stop Enter key event propagating to the wrong element
      self.lockKeys = true;
      process.nextTick(function () {
        self.lockKeys = false;
        resolve(submit ? _(inputs).pluck('value').toArray() : null);
      });
    }

    var inputs = prompts.map(function (prompt, i) {
      var input = new blessed.Textbox(_(self.options.editor || {}).merge({
        parent: self.form,
        inputOnFocus: true,
        top: i,
        height: 1,
        left: labelWidth,
        right: 0
      }).merge(self.options.input || {}).toObject());
      if (!i) { input.focus(); }
      input.on('submit', done.bind(null, true));
      input.on('cancel', done.bind(null, false));
      return input;
    });

    self.render();
  });
};

Slap.prototype.closePrompt = function (focus) {
  var self = this;

  if (self.form) {
    self.remove(self.form);
    self.form = null;
    if (focus) { self.editor.focus(); }
    self.editor.bottom = 0;
    self.render();
  }

  return self;
};

Slap.prototype.exit = function () {
  process.exit(0);

  return this; // Just in case
};

Slap.prototype.message = util.getterSetter('message', null, function (message, styleName) {
  var self = this;

  clearTimeout(self.data.messageTimer);
  self.data.messageTimer = setTimeout(function () {
    self.message(null);
  }, self.options.messageDuration);

  return message !== null ? markup.markupLine(' '+message+' ', self.options.style[styleName]) : null;
});

Slap._regExpRegExp = /^\/(.+)\/$/;
Slap.prototype._initHandlers = function () {
  var self = this;

  var style = self.options.style;
  self.on('element keypress', function (el, ch, key) {
    self.message(null);
    switch (util.getBinding(self.options.bindings, key)) {
      case 'quit':
        if (self.editor.changeStack.dirty()) {
          self.exit(); // FIXME: warn user focused element needs finishing
        } else {
          self.exit();
        }
        break;
      case 'find':
        self.prompt("find (/.*/ for regex)").done(function (results) {
          if (results) {
            pattern = results[0];
            var regExpMatch = pattern.match(Slap._regExpRegExp);
            pattern = regExpMatch
              ? new RegExp(regExpMatch[1])
              : new RegExp(textUtil.escapeRegExp(pattern));

            var startPos = self.editor.cursor();
            startPos.x += 1;
            var pos = textUtil.find(self.editor.lines(), pattern, startPos);

            if (pos) {
              self.editor.select(null, pos);
            } else {
              pos = textUtil.find(self.editor.lines(), pattern);
              if (pos) {
                self.message(Coordinate.linear.cmp(pos, self.editor.cursor()) !== 0
                  ? "search wrapped"
                  : "this is the only occurrence", 'info');
                self.editor.select(null, pos);
              } else {
                self.message("no matches", 'warning');
              }
            }
          }
        });
        break;
      case 'save':
        if (self.path()) { self.save(); }
        else { self.saveAs(); }
        break;
      case 'saveAs':
        self.saveAs();
        break;
    }
  });

  ['resize', 'message', 'save'].forEach(function (evt) {
    self.on(evt, function () { self.render(); });
  });

  ['change', 'cursor', 'insertMode'].forEach(function (evt) {
    self.editor.on(evt, function () { self.render(); });
  });

  return self;
};
Slap.prototype.render = function () {
  var self = this;

  var style = self.options.style;
  var cursor = self.editor.cursor();

  var left = ' \u270b ';
  var originalPath = self.path();
  var markupPath = markup.escapeCurlies(originalPath || 'new file');
  if (self.editor.changeStack.dirty() || !originalPath) {
    markupPath = markup.markupLine(markupPath+'*', style.changed);
  }
  left += markupPath;

  var right = [(cursor.y + 1)+','+(cursor.x + 1) + ' ('+self.editor.lines().length+')'];
  if (!self.editor.insertMode()) { right.unshift(markup.markupLine('ovr', '{red-bg}{white-fg}')); }
  var message = self.message();
  if (message) {
    right.unshift(markup.markupLine(message, self.data.blink ? style.blink : ''));
    if (self.options.blinkRate && !self.data.messageBlinkTimer) {
      (function blink () {
        self.data.messageBlinkTimer = setTimeout(function () {
          self.data.blink = !self.data.blink;
          self.render();
          blink();
        }, self.options.blinkRate);
      }());
    }
  } else {
    clearTimeout(self.data.messageBlinkTimer);
    self.data.messageBlinkTimer = false;
    self.data.blink = false;
  }
  right = right.join('  ') + ' ';

  var remainingWidth = self.header.width - self.header.iwidth - markup.removeMarkup(left + right).length;
  self.header.setContent(markup.markupLine(
    left + _.repeat(' ', remainingWidth).join('') + right
  , style.header || style.main));

  return blessed.Screen.prototype.render.apply(self, arguments);
};

module.exports = Slap;
<commit_msg>Adds unsaved changes prompt<commit_after>var _ = require('lazy.js');
var blessed = require('blessed');
var path = require('path');
var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));

var Editor = require('./editor');
var Coordinate = require('./coordinate');
var markup = require('./markup');
var util = require('./util');
var textUtil = require('./textUtil');

function Slap (opts) {
  var self = this;

  if (!(self instanceof blessed.Node)) { return new Slap(opts); }

  blessed.Screen.call(self, opts);

  self.header = new blessed.Box(_({
    tags: true,
    top: 0,
    left: 0,
    right: 0,
    height: 1
  }).merge(opts.header || {}).merge({parent: self}).toObject());

  self.editor = new Editor(_({
    parent: self,
    top: 1,
    left: 0,
    right: 0,
    bottom: 0
  }).merge(opts.editor || {}).toObject());
  self.editor.focus();

  self._initHandlers().render();
}
Slap.prototype.__proto__ = blessed.Screen.prototype;

Slap.normalizePath = function (givenPath) {
  if (!givenPath) { givenPath = ''; }
  if (givenPath[0] === '~') { givenPath = path.join(process.env.HOME, givenPath.slice(1)); }
  return path.normalize(givenPath);
};
Slap.prototype.path = util.getterSetter('path', null, Slap.normalizePath);
Slap.prototype.open = function (givenPath) {
  var self = this;
  givenPath = Slap.normalizePath(givenPath);
  return fs.readFileAsync(givenPath).then(function (data) {
    self.editor.text(data, givenPath.split('.').pop());
    return self.path(givenPath);
  });
};
Slap.prototype.save = function (givenPath) {
  var self = this;
  givenPath = givenPath ? Slap.normalizePath(givenPath) : self.path();
  if (!givenPath) { return; }

  var text = self.editor.text();
  return fs.writeFileAsync(givenPath, text, {flags: 'w'})
    .then(function () {
      self.editor.changeStack.save();
      self.emit('save', givenPath, text);
      return self.path(givenPath).message("saved to " + givenPath, 'success');
    })
    .catch(function (err) {
      switch (err.code) {
        case 'EACCES': case 'EISDIR':
          self.message(err.message, 'error');
          break;
        default: throw err;
      }
    });
};
Slap.prototype.saveAs = function () {
  var self = this;
  return self.prompt("save as").then(function (results) {
    if (results) { return self.save(results[0]); }
  });
};

Slap.prototype.prompt = function (prompts) {
  var self = this;

  return new Promise(function (resolve, reject) {
    prompts = typeof prompts !== 'string' ? prompts : [prompts];

    self.closePrompt();
    self.editor.bottom += prompts.length;
    self.form = new blessed.Form({
      parent: self,
      height: prompts.length,
      left: 0,
      right: 0,
      bottom: 0
    });

    var labelWidth = _(prompts).pluck('length').max() + 3;
    var label = new blessed.Box({
      parent: self.form,
      tags: true,
      content: prompts.map(function (prompt) {
        return markup.markupLine(
          (' ' + prompt + ':' + _.repeat(' ', labelWidth).join('')).slice(0, labelWidth),
        self.options.style.prompt || self.options.style.main);
      }).join('\n'),
      top: 0,
      left: 0,
      width: labelWidth,
      bottom: 0
    });

    function done (submit) {
      self.closePrompt(true);

      // FIXME: nasty hack to stop Enter key event propagating to the wrong element
      self.lockKeys = true;
      process.nextTick(function () {
        self.lockKeys = false;
        resolve(submit ? _(inputs).pluck('value').toArray() : null);
      });
    }

    var inputs = prompts.map(function (prompt, i) {
      var input = new blessed.Textbox(_(self.options.editor || {}).merge({
        parent: self.form,
        inputOnFocus: true,
        top: i,
        height: 1,
        left: labelWidth,
        right: 0
      }).merge(self.options.input || {}).toObject());
      if (!i) { input.focus(); }
      input.on('submit', done.bind(null, true));
      input.on('cancel', done.bind(null, false));
      return input;
    });

    self.render();
  });
};

Slap.prototype.closePrompt = function (focus) {
  var self = this;

  if (self.form) {
    self.remove(self.form);
    self.form = null;
    if (focus) { self.editor.focus(); }
    self.editor.bottom = 0;
    self.render();
  }

  return self;
};

Slap.prototype.exit = function () {
  process.exit(0);

  return this; // Just in case
};

Slap.prototype.message = util.getterSetter('message', null, function (message, styleName) {
  var self = this;

  clearTimeout(self.data.messageTimer);
  self.data.messageTimer = setTimeout(function () {
    self.message(null);
  }, self.options.messageDuration);

  return message !== null ? markup.markupLine(' '+message+' ', self.options.style[styleName]) : null;
});

Slap._regExpRegExp = /^\/(.+)\/$/;
Slap.prototype._initHandlers = function () {
  var self = this;

  var style = self.options.style;
  self.on('element keypress', function (el, ch, key) {
    self.message(null);
    switch (util.getBinding(self.options.bindings, key)) {
      case 'quit':
        if (self.editor.changeStack.dirty()) {
          self.prompt("save unsaved changes to (leave empty to quit)")
            .then(function (results) {
              if (results && results[0]) { return self.save(results[0]); }
            })
            .done(function () { self.exit(); });
        } else {
          self.exit();
        }
        break;
      case 'find':
        self.prompt("find (/.*/ for regex)").done(function (results) {
          if (results) {
            pattern = results[0];
            var regExpMatch = pattern.match(Slap._regExpRegExp);
            pattern = regExpMatch
              ? new RegExp(regExpMatch[1])
              : new RegExp(textUtil.escapeRegExp(pattern));

            var startPos = self.editor.cursor();
            startPos.x += 1;
            var pos = textUtil.find(self.editor.lines(), pattern, startPos);

            if (pos) {
              self.editor.select(null, pos);
            } else {
              pos = textUtil.find(self.editor.lines(), pattern);
              if (pos) {
                self.message(Coordinate.linear.cmp(pos, self.editor.cursor()) !== 0
                  ? "search wrapped"
                  : "this is the only occurrence", 'info');
                self.editor.select(null, pos);
              } else {
                self.message("no matches", 'warning');
              }
            }
          }
        });
        break;
      case 'save':
        if (self.path()) { self.save(); }
        else { self.saveAs(); }
        break;
      case 'saveAs':
        self.saveAs();
        break;
    }
  });

  ['resize', 'message', 'save'].forEach(function (evt) {
    self.on(evt, function () { self.render(); });
  });

  ['change', 'cursor', 'insertMode'].forEach(function (evt) {
    self.editor.on(evt, function () { self.render(); });
  });

  return self;
};
Slap.prototype.render = function () {
  var self = this;

  var style = self.options.style;
  var cursor = self.editor.cursor();

  var left = ' \u270b ';
  var originalPath = self.path();
  var markupPath = markup.escapeCurlies(originalPath || 'new file');
  if (self.editor.changeStack.dirty() || !originalPath) {
    markupPath = markup.markupLine(markupPath+'*', style.changed);
  }
  left += markupPath;

  var right = [(cursor.y + 1)+','+(cursor.x + 1) + ' ('+self.editor.lines().length+')'];
  if (!self.editor.insertMode()) { right.unshift(markup.markupLine('ovr', '{red-bg}{white-fg}')); }
  var message = self.message();
  if (message) {
    right.unshift(markup.markupLine(message, self.data.blink ? style.blink : ''));
    if (self.options.blinkRate && !self.data.messageBlinkTimer) {
      (function blink () {
        self.data.messageBlinkTimer = setTimeout(function () {
          self.data.blink = !self.data.blink;
          self.render();
          blink();
        }, self.options.blinkRate);
      }());
    }
  } else {
    clearTimeout(self.data.messageBlinkTimer);
    self.data.messageBlinkTimer = false;
    self.data.blink = false;
  }
  right = right.join('  ') + ' ';

  var remainingWidth = self.header.width - self.header.iwidth - markup.removeMarkup(left + right).length;
  self.header.setContent(markup.markupLine(
    left + _.repeat(' ', remainingWidth).join('') + right
  , style.header || style.main));

  return blessed.Screen.prototype.render.apply(self, arguments);
};

module.exports = Slap;
<|endoftext|>


<commit_before>function FilePointer(username, owner, mapKey, rootDirKey) {
    this.username = username; //String
    this.owner = owner; //User
    this.mapKey = mapKey; //ByteArrayWrapper
    this.rootDirKey = rootDirKey; //SymmetricKey

    this.serialize = function() {
	var bout = new ByteBuffer(username.length+96 + 32 + 32 + 16);
	bout.writeArray(string2arraybuffer(username));
	bout.writeArray(owner.getSecretKeys());
	bout.writeArray(mapKey);
	bout.writeArray(rootDirKey.key);
	return bout.toArray();
    }
}

FilePointer.deserialize = function(buf) {
    var bin = new ByteBuffer(buf);
    var name = "";
    var ua = bin.readArray();
    for (var i = 0; i < ua.length; i++)
        name += String.fromCharCode(ua.readUnsignedByte());
    var secKeys = bin.readArray();
    var mapKey = bin.readArray();
    var rootDirKeySecret = bin.readArray();
    return new SharedRootDir(name, User.fromSecretKeys(secKeys), mapKey, new SymmetricKey(rootDirKeySecret));
}

function SymmetricLink(link) {
    this.link = slice(link, SymmetricKey.NONCE_BYTES, link.length);
    this.nonce = slice(link, 0, SymmetricKey.NONCE_BYTES);

    this.serialize = function() {
	return concat(nonce, link);
    }

    this.target = function(from) {
	var encoded = from.decrypt(link, nonce);
	return new SymmetricKey(encoded);
    }
}
SymmetricLink.fromPair = function(from, to, nonce) {
    return new SymmetricLink(concat(nonce, from.encrypt(to.key, nonce)));
}

// String, UserPublicKey, Uint8Array
function Location(owner, subKey, mapKey) {
    this.owner = owner;
    this.subKey = subKey;
    this.mapKey = mapKey;

    this.serialize = function(bout) {
	var bout = new ByteBuffer(username.length + 64 + 32 + 12);
	bout.writeArray(string2arraybuffer(username));
	bout.writeArray(subKey.getPublicKeys());
	bout.writeArray(mapKey);
	return bout.toArray();
    }

    this.encrypt = function(key, nonce) {
	return key.encrypt(serialize(), nonce);
    }
}

function FileAccess(parent2meta, properties, retriever) {
    this.parent2meta = parent2meta;
    this.properties = properties;
    this.retriever = retriever;

    this.serialize = function(bout) {
	bout.writeArray(parent2meta.serialize());
	bout.writeArray(properties);
	bout.writeByte(retriever != null ? 1 : 0);
	if (retriever != null)
	    retriever.serialize(bout);
	bout.writeByte(this.getType());
    }

    // 0=FILE, 1=DIR
    this.getType = function() {
	return 0;
    }
}
FileAccess.deserialize = function(buf, ourKey /*SymmetricKey*/) {
    var p2m = buf.readArray();
    var type = buf.readUnsignedByte();
    var metaNonce = buf.readArray();
    switch(type) {
    case 0:
	return DirAccess.deserialize(buf, ourKey, concat(metaNonce, encryptedMetadata));
    case 1:
	return FileAccess.deserialize(buf, concat(metaNonce, encryptedMetadata));
    case 2:
	return new Metadata(type, metaNonce, encryptedMetadata);
    default: throw new Error("Unknown Metadata type: "+type);
    }
}

function DirAccess(subfolders2files, subfolders2parent, subfolders, files, parent2meta, properties, retriever) {
    FileAccess.call(this, parent2meta, properties, retriever);
    this.subfolders2files = subfolders2files;
    this.subfolders2parent = subfolders2parent;
    this.subfolders = subfolders;
    this.files = files;

    this.superSerialize = this.serialize;
    this.serialize = function(bout) {
	superSerialize(bout);
	bout.writeArray(subfolders2parent);
	bout.writeArray(subfolders2files);
	bout.write
	console.log("here");
    }

    // 0=FILE, 1=DIR
    this.getType = function() {
	return 1;
    }

}

function string2arraybuffer(str) {
  var buf = new ArrayBuffer(str.length);
  var bufView = new Uint8Array(buf);
  for (var i=0, strLen=str.length; i<strLen; i++) {
    bufView[i] = str.charCodeAt(i);
  }
  return bufView;
}
<commit_msg>most of cryptree implemented<commit_after>function FilePointer(username, owner, mapKey, rootDirKey) {
    this.username = username; //String
    this.owner = owner; //User
    this.mapKey = mapKey; //ByteArrayWrapper
    this.rootDirKey = rootDirKey; //SymmetricKey

    this.serialize = function() {
	var bout = new ByteBuffer(username.length+96 + 32 + 32 + 16);
	bout.writeArray(string2arraybuffer(username));
	bout.writeArray(owner.getSecretKeys());
	bout.writeArray(mapKey);
	bout.writeArray(rootDirKey.key);
	return bout.toArray();
    }
}

FilePointer.deserialize = function(buf) {
    var bin = new ByteBuffer(buf);
    var name = "";
    var ua = bin.readArray();
    for (var i = 0; i < ua.length; i++)
        name += String.fromCharCode(ua.readUnsignedByte());
    var secKeys = bin.readArray();
    var mapKey = bin.readArray();
    var rootDirKeySecret = bin.readArray();
    return new SharedRootDir(name, User.fromSecretKeys(secKeys), mapKey, new SymmetricKey(rootDirKeySecret));
}

function SymmetricLink(link) {
    this.link = slice(link, SymmetricKey.NONCE_BYTES, link.length);
    this.nonce = slice(link, 0, SymmetricKey.NONCE_BYTES);

    this.serialize = function() {
	return concat(nonce, link);
    }

    this.target = function(from) {
	var encoded = from.decrypt(link, nonce);
	return new SymmetricKey(encoded);
    }
}
SymmetricLink.fromPair = function(from, to, nonce) {
    return new SymmetricLink(concat(nonce, from.encrypt(to.key, nonce)));
}

function AsymmetricLink() {

}

// String, UserPublicKey, Uint8Array
function Location(owner, subKey, mapKey) {
    this.owner = owner;
    this.subKey = subKey;
    this.mapKey = mapKey;

    this.serialize = function(bout) {
	var bout = new ByteBuffer(username.length + 64 + 32 + 12);
	bout.writeArray(string2arraybuffer(username));
	bout.writeArray(subKey.getPublicKeys());
	bout.writeArray(mapKey);
	return bout.toArray();
    }

    this.encrypt = function(key, nonce) {
	return key.encrypt(serialize(), nonce);
    }
}

function FileAccess(parent2meta, properties, retriever) {
    this.parent2meta = parent2meta;
    this.properties = properties;
    this.retriever = retriever;

    this.serialize = function(bout) {
	bout.writeArray(parent2meta.serialize());
	bout.writeArray(properties);
	bout.writeByte(retriever != null ? 1 : 0);
	if (retriever != null)
	    retriever.serialize(bout);
	bout.writeByte(this.getType());
    }

    // 0=FILE, 1=DIR
    this.getType = function() {
	return 0;
    }
}
FileAccess.deserialize = function(buf, ourKey /*SymmetricKey*/) {
    var p2m = buf.readArray();
    var properties = buf.readArray();
    var hasRetreiver = buf.readUnsignedByte();
    var retriever =  (hasRetreiver == 1) ? FileRetriever.deserialize(buf) : null;
    var type = buf.readUnsignedByte();
    switch(type) {
    case 0:
	return FileAccess.deserialize(buf, concat(metaNonce, encryptedMetadata));
    case 1:
	return DirAccess.deserialize(buf, ourKey, concat(metaNonce, encryptedMetadata));
    default: throw new Error("Unknown Metadata type: "+type);
    }
}

function DirAccess(subfolders2files, subfolders2parent, subfolders, files, parent2meta, properties, retriever) {
    FileAccess.call(this, parent2meta, properties, retriever);
    this.subfolders2files = subfolders2files;
    this.subfolders2parent = subfolders2parent;
    this.subfolders = subfolders;
    this.files = files;

    this.superSerialize = this.serialize;
    this.serialize = function(bout) {
	superSerialize(bout);
	bout.writeArray(subfolders2parent);
	bout.writeArray(subfolders2files);
	bout.writeUnsignedInt(0);
	bout.writeUnsignedInt(subfolders.length)
	for (var i=0; i < subfolders.length; i++)
	    bout.writeArray(subfolders[i].serialize());
	bout.writeUnsignedInt(files.length)
	for (var i=0; i < files.length; i++)
	    bout.writeArray(files[i].serialize());
    }

    // 0=FILE, 1=DIR
    this.getType = function() {
	return 1;
    }
}

DirAccess.deserialize = function(base, bin) {
    var s2p = bin.readArray();
    var s2f = bin.readArray();
    var nSharingKeys = bin.readUnsignedInt();
    var files = [], subfolders = [];
    var nsubfolders = bin.readUnsignedInt();
    for (var i=0; i < nsubfolders; i++)
	nsubfolders[i] = new AsymmetricLink(bin.readArray());
    var nfiles = bin.readUnsignedInt();
    for (var i=0; i < nfiles; i++)
	nfiles[i] = new AsymmetricLink(bin.readArray());
    return new DirAccess(s2f, s2p, subfolders, files, base.parent2meta, base.properties, base.retriever);
}

function FileRetriever() {
}
FileRetriever.deserialize = function(bin) {
    var type = bin.readUnsignedByte();
    switch (type) {
	case 0:
	throw new Exception("Simple FileRetriever not implemented!");
	case 1:
	return EncryptedChunkRetriever.deserialize(bin);
	default:
	throw new Exception("Unknown FileRetriever type: "+type);
    }
}

function EncryptedChunkRetriever(chunkNonce, chunkAuth, fragmentHashes, nextChunk) {
    this.chunkNonce = chunkNonce;
    this.chunkAuth = chunkAuth;
    this.fragmentHashes = fragmentHashes;
    this.nextChunk = nextChunk;

}
EncryptedChunkRetriever.deserialize = function(bin) {
    //TODO
}

function string2arraybuffer(str) {
  var buf = new ArrayBuffer(str.length);
  var bufView = new Uint8Array(buf);
  for (var i=0, strLen=str.length; i<strLen; i++) {
    bufView[i] = str.charCodeAt(i);
  }
  return bufView;
}
<|endoftext|>


<commit_before>/*
 * BSD 3-Clause License
 *
 * Copyright (c) 2017, MapCreator
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 *  Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import Maps4News from './Maps4News';
import PaginatedResourceWrapper from './PaginatedResourceWrapper';
import {hashObject} from './utils/hash';
import {getTypeName, isParentOf} from './utils/reflection';
import {encodeQueryString} from './utils/requests';

/**
 * Proxy for accessing paginated resources
 */
export default class PaginatedResourceListing {
  /**
   * @param {Maps4News} api - Instance of the api
   * @param {String} route - Resource route
   * @param {ResourceBase} Target - Wrapper target
   * @param {Object|{}} query - Search query
   * @param {Number} page - Page number
   * @param {Number} perPage - Amount of items per page
   * @param {Number} pageCount - Resolved page count
   * @param {Number} rowCount - Resolved rowCount
   * @param {Array<ResourceBase>} data - Resolved data
   * @private
   */
  constructor(api, route, Target, query = {}, page = 1, perPage = api.defaults.perPage, pageCount = null, rowCount = 0, data = []) {
    if (!isParentOf(Maps4News, api)) {
      throw new TypeError('Expected api to be of type Maps4News');
    }

    this._api = api;
    this._route = route;
    this._Target = Target;
    this._query = query;

    this._data = data;
    this._page = page;
    this._perPage = perPage;
    this._rows = rowCount;
    this._pageCount = pageCount;
  }

  /**
   * Pagination header prefix
   * @returns {String} - Header prefix
   */
  static get headerPrefix() {
    return 'X-Paginate';
  }

  /**
   * Get api instance
   * @returns {Maps4News} - Api instance
   */
  get api() {
    return this._api;
  }

  /**
   * Target route
   * @returns {String} - url
   */
  get route() {
    return this._route;
  }

  /**
   * Override the target route
   * @param {String} value - route
   */
  set route(value) {
    this._route = value;
  }

  /**
   * Target to wrap results in
   * @returns {ResourceBase} - Target constructor
   * @constructor
   */
  get Target() {
    return this._Target;
  }

  /**
   * Current page number
   * @returns {Number} - Current page
   */
  get page() {
    return this._page;
  }

  /**
   * Maximum amount of items per page
   * @returns {Number} - Amount of items
   */
  get perPage() {
    return this._perPage;
  }

  /**
   * Amount of pages available
   * @returns {Number} - Page count
   */
  get pageCount() {
    return this._pageCount;
  }

  /**
   * Page data
   * @returns {Array<ResourceBase>} - Wrapped data
   */
  get data() {
    return this._data;
  }

  /**
   * Row count
   * @returns {Number} - Row count
   */
  get rows() {
    return this._rows;
  }

  /**
   * Optional search query
   * @default {}
   * @return {Object<String, String|Array<String>>} - Query
   */
  get query() {
    return this._query;
  }

  /**
   * Optional search query
   * @param {Object<String, String|Array<String>>} value - Query
   * @throws TypeError
   * @default {}
   * @see {@link ResourceProxy#search}
   */
  set query(value) {
    // Verify query structure
    if (typeof value !== 'object') {
      throw new TypeError(`Expected value to be of type "Object" got "${getTypeName(value)}"`);
    }

    for (const key of Object.keys(value)) {
      if (typeof key !== 'string') {
        throw new TypeError(`Expected key to be of type "String" got "${getTypeName(key)}"`);
      }

      if (Array.isArray(value[key])) {
        if (value[key].length > 0) {
          for (const query of value[key]) {
            if (typeof query !== 'string') {
              throw new TypeError(`Expected query for "${key}" to be of type "String" got "${getTypeName(query)}"`);
            }
          }
        } else {
          // Drop empty nodes
          delete value[key];
        }
      } else if (value[key] === null) {
        delete value[key];
      } else if (typeof value[key] !== 'string') {
        throw new TypeError(`Expected query value to be of type "string" or "Array" got "${getTypeName(key)}"`);
      }
    }

    this._query = value;
  }

  /**
   * Get target page
   * @param {Number} page - Page number
   * @param {Number} perPage - Amount of items per page (max 50)
   * @returns {Promise} - Resolves with {@link PaginatedResourceListing} instance and rejects with {@link ApiError}
   */
  getPage(page, perPage = this.perPage) {
    page = Math.max(1, page);

    const query = {page};

    if (perPage) {
      perPage = Math.max(1, perPage);
      perPage = Math.min(50, perPage);

      query['per_page'] = perPage;
    }

    // Add search query (if any)
    if (Object.keys(this.query).length > 0) {
      query['search'] = this.query;
    }

    const glue = this.route.includes('?') ? '&' : '?';
    const url = `${this.route}${glue}${encodeQueryString(query)}`;

    return new Promise((resolve, reject) => {
      this.api.request(url, 'GET', {}, {}, '', true)
        .then(request => {
          const response = JSON.parse(request.responseText);
          const rowCount = Number(request.getResponseHeader(`${PaginatedResourceListing.headerPrefix}-Total`));
          const totalPages = Number(request.getResponseHeader(`${PaginatedResourceListing.headerPrefix}-Pages`));

          const instance = new PaginatedResourceListing(
            this.api, this.route, this._Target, this.query,
            page, perPage, totalPages, rowCount,
            response.data.map(row => new this._Target(this.api, row)),
          );

          resolve(instance, request);
        }).catch(reject);
    });
  }

  /**
   * If there is a next page
   * @returns {boolean} - If there is a next page
   */
  get hasNext() {
    return this.page < this.pageCount;
  }

  /**
   * If there is a previous page
   * @returns {boolean} - If there is a previous page
   */
  get hasPrevious() {
    return this.page > 1;
  }

  /**
   * Used for caching pages internally
   * @returns {string} - Cache token
   * @see {@link PaginatedResourceWrapper}
   * @see {@link ResourceCache}
   */
  get cacheToken() {
    return hashObject({query: this.query});
  }

  /**
   * Get next page
   * @returns {Promise} - Resolves with {@link PaginatedResourceListing} instance and rejects with {@link ApiError}
   */
  next() {
    return this.getPage(this.page + 1);
  }

  /**
   * Get previous page
   * @returns {Promise} - Resolves with {@link PaginatedResourceListing} instance and rejects with {@link ApiError}
   */
  previous() {
    return this.getPage(this.page - 1);
  }

  /**
   * Wraps {@link PaginatedResourceWrapper} around the page
   * @param {Boolean} cacheEnabled - If the pagination cache should be used
   * @param {Number} cacheTime - Amount of seconds to store a value in cache
   * @param {Boolean} shareCache - Share cache across instances
   * @returns {PaginatedResourceWrapper} - Wrapped resource listing
   */
  wrap(cacheEnabled = this.api.defaults.cacheEnabled, cacheTime = this.api.defaults.cacheSeconds, shareCache = this.api.defaults._shareCache) {
    return new PaginatedResourceWrapper(this, this.api, cacheEnabled, cacheTime, shareCache);
  }
}
<commit_msg>Add reasonable defaults for ResourceListing<commit_after>/*
 * BSD 3-Clause License
 *
 * Copyright (c) 2017, MapCreator
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 *  Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import Maps4News from './Maps4News';
import PaginatedResourceWrapper from './PaginatedResourceWrapper';
import {hashObject} from './utils/hash';
import {getTypeName, isParentOf} from './utils/reflection';
import {encodeQueryString} from './utils/requests';

/**
 * Proxy for accessing paginated resources
 */
export default class PaginatedResourceListing {
  /**
   * @param {Maps4News} api - Instance of the api
   * @param {String} route - Resource route
   * @param {ResourceBase} Target - Wrapper target
   * @param {Object|{}} query - Search query
   * @param {Number} page - Page number
   * @param {Number} perPage - Amount of items per page
   * @param {Number} pageCount - Resolved page count
   * @param {Number} rowCount - Resolved rowCount
   * @param {Array<ResourceBase>} data - Resolved data
   * @private
   */
  constructor(api, route, Target, query = {}, page = 1, perPage = api.defaults.perPage, pageCount = null, rowCount = 0, data = []) {
    if (!isParentOf(Maps4News, api)) {
      throw new TypeError('Expected api to be of type Maps4News');
    }

    this._api = api;
    this._route = route;
    this._Target = Target;
    this._query = query;

    this._data = data;
    this._page = page;
    this._perPage = perPage;
    this._rows = rowCount;
    this._pageCount = pageCount;
  }

  /**
   * Pagination header prefix
   * @returns {String} - Header prefix
   */
  static get headerPrefix() {
    return 'X-Paginate';
  }

  /**
   * Get api instance
   * @returns {Maps4News} - Api instance
   */
  get api() {
    return this._api;
  }

  /**
   * Target route
   * @returns {String} - url
   */
  get route() {
    return this._route;
  }

  /**
   * Override the target route
   * @param {String} value - route
   */
  set route(value) {
    this._route = value;
  }

  /**
   * Target to wrap results in
   * @returns {ResourceBase} - Target constructor
   * @constructor
   */
  get Target() {
    return this._Target;
  }

  /**
   * Current page number
   * @returns {Number} - Current page
   */
  get page() {
    return this._page;
  }

  /**
   * Maximum amount of items per page
   * @returns {Number} - Amount of items
   */
  get perPage() {
    return this._perPage;
  }

  /**
   * Amount of pages available
   * @returns {Number} - Page count
   */
  get pageCount() {
    return this._pageCount;
  }

  /**
   * Page data
   * @returns {Array<ResourceBase>} - Wrapped data
   */
  get data() {
    return this._data;
  }

  /**
   * Row count
   * @returns {Number} - Row count
   */
  get rows() {
    return this._rows;
  }

  /**
   * Optional search query
   * @default {}
   * @return {Object<String, String|Array<String>>} - Query
   */
  get query() {
    return this._query;
  }

  /**
   * Optional search query
   * @param {Object<String, String|Array<String>>} value - Query
   * @throws TypeError
   * @default {}
   * @see {@link ResourceProxy#search}
   */
  set query(value) {
    // Verify query structure
    if (typeof value !== 'object') {
      throw new TypeError(`Expected value to be of type "Object" got "${getTypeName(value)}"`);
    }

    for (const key of Object.keys(value)) {
      if (typeof key !== 'string') {
        throw new TypeError(`Expected key to be of type "String" got "${getTypeName(key)}"`);
      }

      if (Array.isArray(value[key])) {
        if (value[key].length > 0) {
          for (const query of value[key]) {
            if (typeof query !== 'string') {
              throw new TypeError(`Expected query for "${key}" to be of type "String" got "${getTypeName(query)}"`);
            }
          }
        } else {
          // Drop empty nodes
          delete value[key];
        }
      } else if (value[key] === null) {
        delete value[key];
      } else if (typeof value[key] !== 'string') {
        throw new TypeError(`Expected query value to be of type "string" or "Array" got "${getTypeName(key)}"`);
      }
    }

    this._query = value;
  }

  /**
   * Get target page
   * @param {Number} page - Page number
   * @param {Number} perPage - Amount of items per page (max 50)
   * @returns {Promise} - Resolves with {@link PaginatedResourceListing} instance and rejects with {@link ApiError}
   */
  getPage(page, perPage = this.perPage) {
    page = Math.max(1, page);

    const query = {page};

    if (perPage) {
      perPage = Math.max(1, perPage);
      perPage = Math.min(50, perPage);

      query['per_page'] = perPage;
    }

    // Add search query (if any)
    if (Object.keys(this.query).length > 0) {
      query['search'] = this.query;
    }

    const glue = this.route.includes('?') ? '&' : '?';
    const url = `${this.route}${glue}${encodeQueryString(query)}`;

    return new Promise((resolve, reject) => {
      this.api.request(url, 'GET', {}, {}, '', true)
        .then(request => {
          const response = JSON.parse(request.responseText);
          const rowCount = Number(request.getResponseHeader(`${PaginatedResourceListing.headerPrefix}-Total`)) || response.data.length;
          const totalPages = Number(request.getResponseHeader(`${PaginatedResourceListing.headerPrefix}-Pages`)) || 1;

          const instance = new PaginatedResourceListing(
            this.api, this.route, this._Target, this.query,
            page, perPage, totalPages, rowCount,
            response.data.map(row => new this._Target(this.api, row)),
          );

          resolve(instance, request);
        }).catch(reject);
    });
  }

  /**
   * If there is a next page
   * @returns {boolean} - If there is a next page
   */
  get hasNext() {
    return this.page < this.pageCount;
  }

  /**
   * If there is a previous page
   * @returns {boolean} - If there is a previous page
   */
  get hasPrevious() {
    return this.page > 1;
  }

  /**
   * Used for caching pages internally
   * @returns {string} - Cache token
   * @see {@link PaginatedResourceWrapper}
   * @see {@link ResourceCache}
   */
  get cacheToken() {
    return hashObject({query: this.query});
  }

  /**
   * Get next page
   * @returns {Promise} - Resolves with {@link PaginatedResourceListing} instance and rejects with {@link ApiError}
   */
  next() {
    return this.getPage(this.page + 1);
  }

  /**
   * Get previous page
   * @returns {Promise} - Resolves with {@link PaginatedResourceListing} instance and rejects with {@link ApiError}
   */
  previous() {
    return this.getPage(this.page - 1);
  }

  /**
   * Wraps {@link PaginatedResourceWrapper} around the page
   * @param {Boolean} cacheEnabled - If the pagination cache should be used
   * @param {Number} cacheTime - Amount of seconds to store a value in cache
   * @param {Boolean} shareCache - Share cache across instances
   * @returns {PaginatedResourceWrapper} - Wrapped resource listing
   */
  wrap(cacheEnabled = this.api.defaults.cacheEnabled, cacheTime = this.api.defaults.cacheSeconds, shareCache = this.api.defaults._shareCache) {
    return new PaginatedResourceWrapper(this, this.api, cacheEnabled, cacheTime, shareCache);
  }
}
<|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict';

var program = require('commander');
var pkg = require('../package.json');

program
    .version(pkg.version)
    .parse(process.argv);

if (program.args.length < 1) {
    program.help();
}

var index = require('../lib/index');
index(program.args[0]);
<commit_msg>More than one #3<commit_after>#!/usr/bin/env node
'use strict';

var program = require('commander');
var _ = require('lodash');
var pkg = require('../package.json');

program
    .version(pkg.version)
    .parse(process.argv);

if (program.args.length < 1) {
    program.help();
}

var index = require('../lib/index');

_.forEach(program.args, function (arg) {
    index(arg);
});
<|endoftext|>


<commit_before>'use strict';

const fs = require('fs');
const path = require('path');
const filePath = path.join(__dirname, '../debugger.html');
const startRemoteDev = require('remotedev-server');

const html = fs.readFileSync(filePath, 'utf-8');
const name = 'react-native';

module.exports = function(argv) {
  let moduleName = name;
  if (argv.desktop) {
    moduleName = 'react-native-desktop';
  }
  const distPath = path.join(process.cwd(), `node_modules/${moduleName}/local-cli/server/util/debugger.html`);
  if (argv.runserver) {
    argv.port = argv.port || 8000;
    startRemoteDev(argv);
  }
  if (argv.hostname || argv.port) {
    fs.writeFileSync(
      distPath,
      html.replace(
        '__remotedevOptionsSet__',
        'window.remotedevOptions = ' + JSON.stringify({
          hostname: argv.hostname,
          port: argv.port || 8000,
          autoReconnect: true
        })
      )
    );
  } else {
    fs.writeFileSync(distPath, html);
  }
};
<commit_msg>Inject bundle code to dist path<commit_after>'use strict';

const fs = require('fs');
const path = require('path');
const name = 'react-native';
const flag = '';
const bundleCode = fs.readFileSync(__dirname + '/../bundle.js', 'utf-8');
const startRemoteDev = require('remotedev-server');

function injectBundleCode(filePath, scriptTag, options) {
  const code =
    flag +
    '

' +
    (options ? `<script>window.remotedevOptions = ${JSON.stringify(options)}</script>` : '') +
    scriptTag +
    `</body></html>`;

  const html = fs.readFileSync(filePath, 'utf-8');
  let position = html.indexOf(flag);  // already injected?
  if (position === -1) {
    position = html.indexOf('');
  }
  fs.writeFileSync(filePath, html.substr(0, position) + code);
}

module.exports = function(argv) {
  if (argv.runserver) {
    argv.port = argv.port || 8000;
    startRemoteDev(argv);
  }

  // Support react-native/react-native-desktop
  let moduleName = name;
  if (argv.desktop) {
    moduleName = 'react-native-desktop';
  }

  // Development mode
  let scriptTag = `<script>${bundleCode}</script>`;
  if (argv.__DEV__) {
    scriptTag = `<script src="http://localhost:3030/js/bundle.js"></script>`;
  }

  const filePath = path.join(
    process.cwd(),
    `node_modules/${moduleName}/local-cli/server/util/debugger.html`
  );
  if (argv.hostname || argv.port) {
    injectBundleCode(filePath, scriptTag, {
      hostname: argv.hostname,
      port: argv.port || 8000,
      autoReconnect: true
    });
  } else {
    injectBundleCode(filePath, scriptTag);
  }
};
<|endoftext|>