<commit_before>// Had to copy this from unexpected's lib/defaultDepth.js because the value is not exposed to plugins :/
let defaultDepth = 3;
if (typeof window !== 'undefined' && typeof window.location !== 'undefined') {
  const m = window.location.search.match(/[?&]depth=(\d+)(?:$|&)/);
  if (m) {
    defaultDepth = parseInt(m[1], 10);
  }
} else if (typeof process !== 'undefined' && process.env.UNEXPECTED_DEPTH) {
  defaultDepth = parseInt(process.env.UNEXPECTED_DEPTH, 10);
}

module.exports = {
  name: 'unexpected-set',
  version: require('../package.json').version,
  installInto: function unexpectedSet(expect) {
    expect.addType({
      name: 'Set',
      base: 'object',
      indent: true,
      identify(obj) {
        return obj instanceof Set;
      },
      equal(a, b) {
        if (a.size !== b.size) {
          return false;
        }
        for (const value of a) {
          if (!b.has(value)) {
            return false;
          }
        }
        return true;
      },
      prefix(output) {
        return output.jsKeyword('new').sp().jsKeyword('Set').text('([');
      },
      suffix(output) {
        return output.text('])');
      },
      inspect(set, depth, output, inspect) {
        // Mostly copied from array-like's inspect:
        const prefixOutput = this.prefix(output.clone(), set);
        const suffixOutput = this.suffix(output.clone(), set);
        if (set.size === 0) {
          return output.append(prefixOutput).append(suffixOutput);
        }

        if (depth === 1 && set.size > 10) {
          return output.append(prefixOutput).text('...').append(suffixOutput);
        }

        const inspectedItems = [];
        set.forEach((item) => {
          inspectedItems.push(inspect(item));
        });

        const currentDepth = defaultDepth - Math.min(defaultDepth, depth);
        const maxLineLength =
          output.preferredWidth -
          20 -
          currentDepth * output.indentationWidth -
          2;
        let width = 0;
        const multipleLines = inspectedItems.some((o) => {
          if (o.isMultiline()) {
            return true;
          }

          const size = o.size();
          width += size.width;
          return width > maxLineLength;
        });

        const type = this;
        inspectedItems.forEach((inspectedItem, index) => {
          inspectedItem.amend(
            type.delimiter(output.clone(), index, inspectedItems.length)
          );
        });

        output.append(prefixOutput);
        if (this.forceMultipleLines || multipleLines) {
          if (!prefixOutput.isEmpty()) {
            output.nl();
          }
          if (this.indent) {
            output.indentLines();
          }
          inspectedItems.forEach((inspectedItem, index) => {
            output
              .nl(index > 0 ? 1 : 0)
              .i()
              .block(inspectedItem);
          });
          if (this.indent) {
            output.outdentLines();
          }
          if (!suffixOutput.isEmpty()) {
            output.nl();
          }
        } else {
          output.sp(prefixOutput.isEmpty() ? 0 : 1);
          inspectedItems.forEach((inspectedItem, index) => {
            output.append(inspectedItem);
            const lastIndex = index === inspectedItems.length - 1;
            if (!lastIndex) {
              output.sp();
            }
          });
          output.sp(suffixOutput.isEmpty() ? 0 : 1);
        }
        output.append(suffixOutput);
      },
      diff(actual, expected, output, diff, inspect, equal) {
        output.inline = true;
        const prefixOutput = this.prefix(output.clone(), actual);
        const suffixOutput = this.suffix(output.clone(), actual);

        output.append(prefixOutput).nl(prefixOutput.isEmpty() ? 0 : 1);
        if (this.indent) {
          output.indentLines();
        }
        const type = this;
        let index = 0;
        actual.forEach((actualElement) => {
          output
            .nl(index > 0 ? 1 : 0)
            .i()
            .block(function () {
              this.appendInspected(actualElement);
              type.delimiter(this, index, actual.size);
              if (!expected.has(actualElement)) {
                this.sp().annotationBlock(function () {
                  this.error('should be removed');
                });
              }
            });
          index += 1;
        });
        expected.forEach((expectedElement) => {
          if (!actual.has(expectedElement)) {
            output
              .nl(index > 0 ? 1 : 0)
              .i()
              .annotationBlock(function () {
                this.error('missing').sp().appendInspected(expectedElement);
              });
            index += 1;
          }
        });
        if (this.indent) {
          output.outdentLines();
        }
        output.nl(suffixOutput.isEmpty() ? 0 : 1).append(suffixOutput);

        return output;
      },
    });

    expect.addAssertion(
      '<array-like> with set semantics <assertion?>',
      (expect, subject) => expect.shift(new Set(subject))
    );

    expect.addAssertion(
      '<Set> [not] to contain <any>',
      (expect, subject, value) => {
        expect.withError(
          () => {
            expect(subject.has(value), '[not] to be true');
          },
          () => {
            expect.fail({
              diff(output, diff, inspect, equal) {
                output.inline = true;
                expect.subjectType.prefix(output, subject);
                output.nl().indentLines();

                const subjectElements = [];
                subject.forEach((element) => {
                  subjectElements.push(element);
                });

                subjectElements.forEach((subjectElement, subjectIndex) => {
                  output
                    .i()
                    .block(function () {
                      this.appendInspected(subjectElement);
                      expect.subjectType.delimiter(
                        this,
                        subjectIndex,
                        subjectElements.length
                      );
                      if (expect.flags.not && subjectElement === value) {
                        this.sp().annotationBlock(function () {
                          this.error('should be removed');
                        });
                      }
                    })
                    .nl();
                });
                if (!expect.flags.not) {
                  output
                    .i()
                    .block(function () {
                      this.annotationBlock(function () {
                        this.error('missing').sp().appendInspected(value);
                      });
                    })
                    .nl();
                }

                output.outdentLines();
                expect.subjectType.suffix(output, subject);

                return output;
              },
            });
          }
        );
      }
    );

    expect.addAssertion('<Set> [not] to be empty', (expect, subject) => {
      expect(subject.size, '[not] to equal', 0);
    });

    expect.addAssertion(
      [
        '<Set> to have items [exhaustively] satisfying <any>',
        '<Set> to have items [exhaustively] satisfying <assertion>',
      ],
      (expect, subject, nextArg) => {
        expect.errorMode = 'nested';
        expect(subject, 'not to be empty');
        expect.errorMode = 'bubble';

        const subjectElements = [];
        subject.forEach((element) => {
          subjectElements.push(element);
        });

        const expected = [];
        subjectElements.forEach((subjectElement) => {
          if (typeof nextArg === 'string') {
            expected.push(expect.it((s) => expect.shift(s, 0)));
          } else {
            expected.push(nextArg);
          }
        });

        return expect.withError(
          () => expect(subjectElements, 'to [exhaustively] satisfy', expected),
          (err) => {
            expect.fail({
              message(output) {
                output.append(
                  expect.standardErrorMessage(output.clone(), { compact: true })
                );
              },
              diff(output) {
                output.inline = true;
                return output
                  .jsKeyword('new')
                  .sp()
                  .jsKeyword('Set')
                  .text('(')
                  .append(err.getDiff({ output }))
                  .text(')');
              },
            });
          }
        );
      }
    );

    expect.addAssertion(
      [
        '<Set> to have an item [exhaustively] satisfying <any>',
        '<Set> to have an item [exhaustively] satisfying <assertion>',
      ],
      (expect, subject, nextArg) => {
        expect.errorMode = 'nested';
        expect(subject, 'not to be empty');
        expect.errorMode = 'bubble';

        const subjectElements = [];
        subject.forEach((element) => {
          subjectElements.push(element);
        });

        const expected =
          typeof nextArg === 'string'
            ? expect.it((s) => expect.shift(s, 0))
            : nextArg;
        const expectedType = expect.findTypeOf(expected);
        const results = new Array(subjectElements.length);
        subjectElements.forEach((subjectElement, subjectIndex) => {
          results[subjectIndex] = expect.promise(() => {
            if (expectedType.is('expect.it')) {
              expect.context.thisObject = subject;
              return expected(subjectElement, expect.context);
            } else {
              return expect(
                subjectElement,
                'to [exhaustively] satisfy',
                nextArg
              );
            }
          });
        });

        return expect.promise.settle(results).then(function () {
          if (results.every((result) => !result.isFulfilled())) {
            expect.fail({
              message(output) {
                output.append(
                  expect.standardErrorMessage(output.clone(), { compact: true })
                );
              },
              diff(output, diff, inspect, equal) {
                output.inline = true;
                const prefixOutput = expect.subjectType.prefix(
                  output.clone(),
                  subject
                );
                const suffixOutput = expect.subjectType.suffix(
                  output.clone(),
                  subject
                );

                output.append(prefixOutput);
                if (!prefixOutput.isEmpty()) {
                  output.nl();
                }
                if (expect.subjectType.indent) {
                  output.indentLines();
                }

                let index = 0;
                subjectElements.forEach((subjectElement, subjectIndex) => {
                  output.omitSubject = subject;
                  output
                    .nl(index > 0 ? 1 : 0)
                    .i()
                    .block(function () {
                      const delimiterOutput = expect.subjectType.delimiter(
                        output.clone(),
                        subjectIndex,
                        subjectElements.length
                      );

                      const err = results[subjectIndex].reason();
                      const diff = err.getDiff({ output: output.clone() });

                      if (diff && diff.inline) {
                        this.append(diff).amend(delimiterOutput);
                      } else {
                        this.appendInspected(subjectElement)
                          .amend(delimiterOutput)
                          .sp()
                          .annotationBlock(function () {
                            this.omitSubject = subjectElement;
                            this.appendErrorMessage(err);
                          });
                      }
                    });
                  index += 1;
                });

                if (expect.subjectType.indent) {
                  output.outdentLines();
                }
                if (!suffixOutput.isEmpty()) {
                  output.nl();
                }
                output.append(suffixOutput);

                return output;
              },
            });
          }
        });
      }
    );

    expect.addAssertion(
      '<Set> to [exhaustively] satisfy <Set>',
      (expect, subject, value) => {
        const subjectElements = [];
        subject.forEach((element) => {
          subjectElements.push(element);
        });
        const valueElements = [];
        value.forEach((element) => {
          valueElements.push(element);
        });

        const promiseBySubjectIndexAndValueIndex = subjectElements.map(
          () => new Array(valueElements.length)
        );

        const promiseByValueIndexAndSubjectIndex = valueElements.map(
          (valueElement, valueIndex) =>
            subjectElements.map((subjectElement, subjectIndex) => {
              const promise = expect.promise(() => {
                const valueElementType = expect.findTypeOf(valueElement);
                if (valueElementType.is('function')) {
                  return valueElement(subjectElement);
                } else {
                  return expect(
                    subjectElement,
                    'to [exhaustively] satisfy',
                    valueElement
                  );
                }
              });
              promiseBySubjectIndexAndValueIndex[subjectIndex][
                valueIndex
              ] = promise;
              return promise;
            })
        );

        return expect.promise
          .settle(promiseByValueIndexAndSubjectIndex)
          .then(() => {
            if (
              !promiseByValueIndexAndSubjectIndex.every((row) =>
                row.some((promise) => promise.isFulfilled())
              ) ||
              !subjectElements.every((subjectElement, i) =>
                promiseByValueIndexAndSubjectIndex.some((row) =>
                  row[i].isFulfilled()
                )
              )
            ) {
              expect.fail({
                diff(output, diff, inspect, equal) {
                  output.inline = true;
                  const prefixOutput = expect.subjectType.prefix(
                    output.clone(),
                    subject
                  );
                  const suffixOutput = expect.subjectType.suffix(
                    output.clone(),
                    subject
                  );

                  output.append(prefixOutput);
                  if (!prefixOutput.isEmpty()) {
                    output.nl();
                  }
                  if (expect.subjectType.indent) {
                    output.indentLines();
                  }

                  let index = 0;
                  subjectElements.forEach((subjectElement, subjectIndex) => {
                    output
                      .nl(index > 0 ? 1 : 0)
                      .i()
                      .block(function () {
                        this.appendInspected(subjectElement);
                        expect.subjectType.delimiter(
                          this,
                          subjectIndex,
                          subjectElements.length
                        );
                        if (
                          !promiseBySubjectIndexAndValueIndex[
                            subjectIndex
                          ].some((promise, valueIndex) =>
                            promiseBySubjectIndexAndValueIndex[subjectIndex][
                              valueIndex
                            ].isFulfilled()
                          )
                        ) {
                          this.sp().annotationBlock(function () {
                            this.error('should be removed');
                          });
                        }
                      });
                    index += 1;
                  });
                  valueElements.forEach((valueElement, valueIndex) => {
                    if (
                      promiseByValueIndexAndSubjectIndex[
                        valueIndex
                      ].every((promise) => promise.isRejected())
                    ) {
                      output
                        .nl(index > 0 ? 1 : 0)
                        .i()
                        .annotationBlock(function () {
                          if (expect.findTypeOf(valueElement).is('function')) {
                            this.omitSubject = subjectElements[0];
                            this.error('missing:')
                              .sp()
                              .appendErrorMessage(
                                promiseByValueIndexAndSubjectIndex[
                                  valueIndex
                                ][0].reason()
                              );
                          } else {
                            this.error('missing')
                              .sp()
                              .appendInspected(valueElement);
                          }
                        });
                      index += 1;
                    }
                  });

                  if (expect.subjectType.indent) {
                    output.outdentLines();
                  }
                  if (!suffixOutput.isEmpty()) {
                    output.nl();
                  }
                  output.append(suffixOutput);

                  return output;
                },
              });
            }
          });
      }
    );

    expect.addAssertion(
      '<Set> [not] to have size <number>',
      (expect, subject, value) => {
        expect(subject.size, '[not] to equal', value);
      }
    );
  },
};
<commit_msg>prettier --write '**/*.{js,json,md}'<commit_after>// Had to copy this from unexpected's lib/defaultDepth.js because the value is not exposed to plugins :/
let defaultDepth = 3;
if (typeof window !== 'undefined' && typeof window.location !== 'undefined') {
  const m = window.location.search.match(/[?&]depth=(\d+)(?:$|&)/);
  if (m) {
    defaultDepth = parseInt(m[1], 10);
  }
} else if (typeof process !== 'undefined' && process.env.UNEXPECTED_DEPTH) {
  defaultDepth = parseInt(process.env.UNEXPECTED_DEPTH, 10);
}

module.exports = {
  name: 'unexpected-set',
  version: require('../package.json').version,
  installInto: function unexpectedSet(expect) {
    expect.addType({
      name: 'Set',
      base: 'object',
      indent: true,
      identify(obj) {
        return obj instanceof Set;
      },
      equal(a, b) {
        if (a.size !== b.size) {
          return false;
        }
        for (const value of a) {
          if (!b.has(value)) {
            return false;
          }
        }
        return true;
      },
      prefix(output) {
        return output.jsKeyword('new').sp().jsKeyword('Set').text('([');
      },
      suffix(output) {
        return output.text('])');
      },
      inspect(set, depth, output, inspect) {
        // Mostly copied from array-like's inspect:
        const prefixOutput = this.prefix(output.clone(), set);
        const suffixOutput = this.suffix(output.clone(), set);
        if (set.size === 0) {
          return output.append(prefixOutput).append(suffixOutput);
        }

        if (depth === 1 && set.size > 10) {
          return output.append(prefixOutput).text('...').append(suffixOutput);
        }

        const inspectedItems = [];
        set.forEach((item) => {
          inspectedItems.push(inspect(item));
        });

        const currentDepth = defaultDepth - Math.min(defaultDepth, depth);
        const maxLineLength =
          output.preferredWidth -
          20 -
          currentDepth * output.indentationWidth -
          2;
        let width = 0;
        const multipleLines = inspectedItems.some((o) => {
          if (o.isMultiline()) {
            return true;
          }

          const size = o.size();
          width += size.width;
          return width > maxLineLength;
        });

        const type = this;
        inspectedItems.forEach((inspectedItem, index) => {
          inspectedItem.amend(
            type.delimiter(output.clone(), index, inspectedItems.length)
          );
        });

        output.append(prefixOutput);
        if (this.forceMultipleLines || multipleLines) {
          if (!prefixOutput.isEmpty()) {
            output.nl();
          }
          if (this.indent) {
            output.indentLines();
          }
          inspectedItems.forEach((inspectedItem, index) => {
            output
              .nl(index > 0 ? 1 : 0)
              .i()
              .block(inspectedItem);
          });
          if (this.indent) {
            output.outdentLines();
          }
          if (!suffixOutput.isEmpty()) {
            output.nl();
          }
        } else {
          output.sp(prefixOutput.isEmpty() ? 0 : 1);
          inspectedItems.forEach((inspectedItem, index) => {
            output.append(inspectedItem);
            const lastIndex = index === inspectedItems.length - 1;
            if (!lastIndex) {
              output.sp();
            }
          });
          output.sp(suffixOutput.isEmpty() ? 0 : 1);
        }
        output.append(suffixOutput);
      },
      diff(actual, expected, output, diff, inspect, equal) {
        output.inline = true;
        const prefixOutput = this.prefix(output.clone(), actual);
        const suffixOutput = this.suffix(output.clone(), actual);

        output.append(prefixOutput).nl(prefixOutput.isEmpty() ? 0 : 1);
        if (this.indent) {
          output.indentLines();
        }
        const type = this;
        let index = 0;
        actual.forEach((actualElement) => {
          output
            .nl(index > 0 ? 1 : 0)
            .i()
            .block(function () {
              this.appendInspected(actualElement);
              type.delimiter(this, index, actual.size);
              if (!expected.has(actualElement)) {
                this.sp().annotationBlock(function () {
                  this.error('should be removed');
                });
              }
            });
          index += 1;
        });
        expected.forEach((expectedElement) => {
          if (!actual.has(expectedElement)) {
            output
              .nl(index > 0 ? 1 : 0)
              .i()
              .annotationBlock(function () {
                this.error('missing').sp().appendInspected(expectedElement);
              });
            index += 1;
          }
        });
        if (this.indent) {
          output.outdentLines();
        }
        output.nl(suffixOutput.isEmpty() ? 0 : 1).append(suffixOutput);

        return output;
      },
    });

    expect.addAssertion(
      '<array-like> with set semantics <assertion?>',
      (expect, subject) => expect.shift(new Set(subject))
    );

    expect.addAssertion(
      '<Set> [not] to contain <any>',
      (expect, subject, value) => {
        expect.withError(
          () => {
            expect(subject.has(value), '[not] to be true');
          },
          () => {
            expect.fail({
              diff(output, diff, inspect, equal) {
                output.inline = true;
                expect.subjectType.prefix(output, subject);
                output.nl().indentLines();

                const subjectElements = [];
                subject.forEach((element) => {
                  subjectElements.push(element);
                });

                subjectElements.forEach((subjectElement, subjectIndex) => {
                  output
                    .i()
                    .block(function () {
                      this.appendInspected(subjectElement);
                      expect.subjectType.delimiter(
                        this,
                        subjectIndex,
                        subjectElements.length
                      );
                      if (expect.flags.not && subjectElement === value) {
                        this.sp().annotationBlock(function () {
                          this.error('should be removed');
                        });
                      }
                    })
                    .nl();
                });
                if (!expect.flags.not) {
                  output
                    .i()
                    .block(function () {
                      this.annotationBlock(function () {
                        this.error('missing').sp().appendInspected(value);
                      });
                    })
                    .nl();
                }

                output.outdentLines();
                expect.subjectType.suffix(output, subject);

                return output;
              },
            });
          }
        );
      }
    );

    expect.addAssertion('<Set> [not] to be empty', (expect, subject) => {
      expect(subject.size, '[not] to equal', 0);
    });

    expect.addAssertion(
      [
        '<Set> to have items [exhaustively] satisfying <any>',
        '<Set> to have items [exhaustively] satisfying <assertion>',
      ],
      (expect, subject, nextArg) => {
        expect.errorMode = 'nested';
        expect(subject, 'not to be empty');
        expect.errorMode = 'bubble';

        const subjectElements = [];
        subject.forEach((element) => {
          subjectElements.push(element);
        });

        const expected = [];
        subjectElements.forEach((subjectElement) => {
          if (typeof nextArg === 'string') {
            expected.push(expect.it((s) => expect.shift(s, 0)));
          } else {
            expected.push(nextArg);
          }
        });

        return expect.withError(
          () => expect(subjectElements, 'to [exhaustively] satisfy', expected),
          (err) => {
            expect.fail({
              message(output) {
                output.append(
                  expect.standardErrorMessage(output.clone(), { compact: true })
                );
              },
              diff(output) {
                output.inline = true;
                return output
                  .jsKeyword('new')
                  .sp()
                  .jsKeyword('Set')
                  .text('(')
                  .append(err.getDiff({ output }))
                  .text(')');
              },
            });
          }
        );
      }
    );

    expect.addAssertion(
      [
        '<Set> to have an item [exhaustively] satisfying <any>',
        '<Set> to have an item [exhaustively] satisfying <assertion>',
      ],
      (expect, subject, nextArg) => {
        expect.errorMode = 'nested';
        expect(subject, 'not to be empty');
        expect.errorMode = 'bubble';

        const subjectElements = [];
        subject.forEach((element) => {
          subjectElements.push(element);
        });

        const expected =
          typeof nextArg === 'string'
            ? expect.it((s) => expect.shift(s, 0))
            : nextArg;
        const expectedType = expect.findTypeOf(expected);
        const results = new Array(subjectElements.length);
        subjectElements.forEach((subjectElement, subjectIndex) => {
          results[subjectIndex] = expect.promise(() => {
            if (expectedType.is('expect.it')) {
              expect.context.thisObject = subject;
              return expected(subjectElement, expect.context);
            } else {
              return expect(
                subjectElement,
                'to [exhaustively] satisfy',
                nextArg
              );
            }
          });
        });

        return expect.promise.settle(results).then(function () {
          if (results.every((result) => !result.isFulfilled())) {
            expect.fail({
              message(output) {
                output.append(
                  expect.standardErrorMessage(output.clone(), { compact: true })
                );
              },
              diff(output, diff, inspect, equal) {
                output.inline = true;
                const prefixOutput = expect.subjectType.prefix(
                  output.clone(),
                  subject
                );
                const suffixOutput = expect.subjectType.suffix(
                  output.clone(),
                  subject
                );

                output.append(prefixOutput);
                if (!prefixOutput.isEmpty()) {
                  output.nl();
                }
                if (expect.subjectType.indent) {
                  output.indentLines();
                }

                let index = 0;
                subjectElements.forEach((subjectElement, subjectIndex) => {
                  output.omitSubject = subject;
                  output
                    .nl(index > 0 ? 1 : 0)
                    .i()
                    .block(function () {
                      const delimiterOutput = expect.subjectType.delimiter(
                        output.clone(),
                        subjectIndex,
                        subjectElements.length
                      );

                      const err = results[subjectIndex].reason();
                      const diff = err.getDiff({ output: output.clone() });

                      if (diff && diff.inline) {
                        this.append(diff).amend(delimiterOutput);
                      } else {
                        this.appendInspected(subjectElement)
                          .amend(delimiterOutput)
                          .sp()
                          .annotationBlock(function () {
                            this.omitSubject = subjectElement;
                            this.appendErrorMessage(err);
                          });
                      }
                    });
                  index += 1;
                });

                if (expect.subjectType.indent) {
                  output.outdentLines();
                }
                if (!suffixOutput.isEmpty()) {
                  output.nl();
                }
                output.append(suffixOutput);

                return output;
              },
            });
          }
        });
      }
    );

    expect.addAssertion(
      '<Set> to [exhaustively] satisfy <Set>',
      (expect, subject, value) => {
        const subjectElements = [];
        subject.forEach((element) => {
          subjectElements.push(element);
        });
        const valueElements = [];
        value.forEach((element) => {
          valueElements.push(element);
        });

        const promiseBySubjectIndexAndValueIndex = subjectElements.map(
          () => new Array(valueElements.length)
        );

        const promiseByValueIndexAndSubjectIndex = valueElements.map(
          (valueElement, valueIndex) =>
            subjectElements.map((subjectElement, subjectIndex) => {
              const promise = expect.promise(() => {
                const valueElementType = expect.findTypeOf(valueElement);
                if (valueElementType.is('function')) {
                  return valueElement(subjectElement);
                } else {
                  return expect(
                    subjectElement,
                    'to [exhaustively] satisfy',
                    valueElement
                  );
                }
              });
              promiseBySubjectIndexAndValueIndex[subjectIndex][valueIndex] =
                promise;
              return promise;
            })
        );

        return expect.promise
          .settle(promiseByValueIndexAndSubjectIndex)
          .then(() => {
            if (
              !promiseByValueIndexAndSubjectIndex.every((row) =>
                row.some((promise) => promise.isFulfilled())
              ) ||
              !subjectElements.every((subjectElement, i) =>
                promiseByValueIndexAndSubjectIndex.some((row) =>
                  row[i].isFulfilled()
                )
              )
            ) {
              expect.fail({
                diff(output, diff, inspect, equal) {
                  output.inline = true;
                  const prefixOutput = expect.subjectType.prefix(
                    output.clone(),
                    subject
                  );
                  const suffixOutput = expect.subjectType.suffix(
                    output.clone(),
                    subject
                  );

                  output.append(prefixOutput);
                  if (!prefixOutput.isEmpty()) {
                    output.nl();
                  }
                  if (expect.subjectType.indent) {
                    output.indentLines();
                  }

                  let index = 0;
                  subjectElements.forEach((subjectElement, subjectIndex) => {
                    output
                      .nl(index > 0 ? 1 : 0)
                      .i()
                      .block(function () {
                        this.appendInspected(subjectElement);
                        expect.subjectType.delimiter(
                          this,
                          subjectIndex,
                          subjectElements.length
                        );
                        if (
                          !promiseBySubjectIndexAndValueIndex[
                            subjectIndex
                          ].some((promise, valueIndex) =>
                            promiseBySubjectIndexAndValueIndex[subjectIndex][
                              valueIndex
                            ].isFulfilled()
                          )
                        ) {
                          this.sp().annotationBlock(function () {
                            this.error('should be removed');
                          });
                        }
                      });
                    index += 1;
                  });
                  valueElements.forEach((valueElement, valueIndex) => {
                    if (
                      promiseByValueIndexAndSubjectIndex[valueIndex].every(
                        (promise) => promise.isRejected()
                      )
                    ) {
                      output
                        .nl(index > 0 ? 1 : 0)
                        .i()
                        .annotationBlock(function () {
                          if (expect.findTypeOf(valueElement).is('function')) {
                            this.omitSubject = subjectElements[0];
                            this.error('missing:')
                              .sp()
                              .appendErrorMessage(
                                promiseByValueIndexAndSubjectIndex[
                                  valueIndex
                                ][0].reason()
                              );
                          } else {
                            this.error('missing')
                              .sp()
                              .appendInspected(valueElement);
                          }
                        });
                      index += 1;
                    }
                  });

                  if (expect.subjectType.indent) {
                    output.outdentLines();
                  }
                  if (!suffixOutput.isEmpty()) {
                    output.nl();
                  }
                  output.append(suffixOutput);

                  return output;
                },
              });
            }
          });
      }
    );

    expect.addAssertion(
      '<Set> [not] to have size <number>',
      (expect, subject, value) => {
        expect(subject.size, '[not] to equal', value);
      }
    );
  },
};
<|endoftext|>


<commit_before>/*
 * jQuery UI Selectable @VERSION
 *
 * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://docs.jquery.com/UI/Selectables
 *
 * Depends:
 *	ui.core.js
 */
(function($) {

$.widget("ui.selectable", $.extend({}, $.ui.mouse, {

	_init: function() {
		var self = this;

		this.element.addClass("ui-selectable");

		this.dragged = false;

		// cache selectee children based on filter
		var selectees;
		this.refresh = function() {
			selectees = $(self.options.filter, self.element[0]);
			selectees.each(function() {
				var $this = $(this);
				var pos = $this.offset();
				$.data(this, "selectable-item", {
					element: this,
					$element: $this,
					left: pos.left,
					top: pos.top,
					right: pos.left + $this.outerWidth(),
					bottom: pos.top + $this.outerHeight(),
					startselected: false,
					selected: $this.hasClass('ui-selected'),
					selecting: $this.hasClass('ui-selecting'),
					unselecting: $this.hasClass('ui-unselecting')
				});
			});
		};
		this.refresh();

		this.selectees = selectees.addClass("ui-selectee");

		this._mouseInit();

		this.helper = $(document.createElement('div'))
			.css({border:'1px dotted black'})
			.addClass("ui-selectable-helper");
	},

	destroy: function() {
		this.element
			.removeClass("ui-selectable ui-selectable-disabled")
			.removeData("selectable")
			.unbind(".selectable");
		this._mouseDestroy();
	},

	_mouseStart: function(event) {
		var self = this;

		this.opos = [event.pageX, event.pageY];

		if (this.options.disabled)
			return;

		var options = this.options;

		this.selectees = $(options.filter, this.element[0]);

		this._trigger("start", event);

		$('body').append(this.helper);
		// position helper (lasso)
		this.helper.css({
			"z-index": 100,
			"position": "absolute",
			"left": event.clientX,
			"top": event.clientY,
			"width": 0,
			"height": 0
		});

		if (options.autoRefresh) {
			this.refresh();
		}

		this.selectees.filter('.ui-selected').each(function() {
			var selectee = $.data(this, "selectable-item");
			selectee.startselected = true;
			if (!event.metaKey) {
				selectee.$element.removeClass('ui-selected');
				selectee.selected = false;
				selectee.$element.addClass('ui-unselecting');
				selectee.unselecting = true;
				// selectable UNSELECTING callback
				self._trigger("unselecting", event, {
					unselecting: selectee.element
				});
			}
		});

		$(event.target).parents().andSelf().each(function() {
			var selectee = $.data(this, "selectable-item");
			if (selectee) {
				selectee.$element.removeClass("ui-unselecting").addClass('ui-selecting');
				selectee.unselecting = false;
				selectee.selecting = true;
				selectee.selected = true;
				// selectable SELECTING callback
				self._trigger("selecting", event, {
					selecting: selectee.element
				});
				return false;
			}
		});

	},

	_mouseDrag: function(event) {
		var self = this;
		this.dragged = true;

		if (this.options.disabled)
			return;

		var options = this.options;

		var x1 = this.opos[0], y1 = this.opos[1], x2 = event.pageX, y2 = event.pageY;
		if (x1 > x2) { var tmp = x2; x2 = x1; x1 = tmp; }
		if (y1 > y2) { var tmp = y2; y2 = y1; y1 = tmp; }
		this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1});

		this.selectees.each(function() {
			var selectee = $.data(this, "selectable-item");
			//prevent helper from being selected if appendTo: selectable
			if (!selectee || selectee.element == self.element[0])
				return;
			var hit = false;
			if (options.tolerance == 'touch') {
				hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
			} else if (options.tolerance == 'fit') {
				hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
			}

			if (hit) {
				// SELECT
				if (selectee.selected) {
					selectee.$element.removeClass('ui-selected');
					selectee.selected = false;
				}
				if (selectee.unselecting) {
					selectee.$element.removeClass('ui-unselecting');
					selectee.unselecting = false;
				}
				if (!selectee.selecting) {
					selectee.$element.addClass('ui-selecting');
					selectee.selecting = true;
					// selectable SELECTING callback
					self._trigger("selecting", event, {
						selecting: selectee.element
					});
				}
			} else {
				// UNSELECT
				if (selectee.selecting) {
					if (event.metaKey && selectee.startselected) {
						selectee.$element.removeClass('ui-selecting');
						selectee.selecting = false;
						selectee.$element.addClass('ui-selected');
						selectee.selected = true;
					} else {
						selectee.$element.removeClass('ui-selecting');
						selectee.selecting = false;
						if (selectee.startselected) {
							selectee.$element.addClass('ui-unselecting');
							selectee.unselecting = true;
						}
						// selectable UNSELECTING callback
						self._trigger("unselecting", event, {
							unselecting: selectee.element
						});
					}
				}
				if (selectee.selected) {
					if (!event.metaKey && !selectee.startselected) {
						selectee.$element.removeClass('ui-selected');
						selectee.selected = false;

						selectee.$element.addClass('ui-unselecting');
						selectee.unselecting = true;
						// selectable UNSELECTING callback
						self._trigger("unselecting", event, {
							unselecting: selectee.element
						});
					}
				}
			}
		});

		return false;
	},

	_mouseStop: function(event) {
		var self = this;

		this.dragged = false;

		var options = this.options;

		$('.ui-unselecting', this.element[0]).each(function() {
			var selectee = $.data(this, "selectable-item");
			selectee.$element.removeClass('ui-unselecting');
			selectee.unselecting = false;
			selectee.startselected = false;
			self._trigger("unselected", event, {
				unselected: selectee.element
			});
		});
		$('.ui-selecting', this.element[0]).each(function() {
			var selectee = $.data(this, "selectable-item");
			selectee.$element.removeClass('ui-selecting').addClass('ui-selected');
			selectee.selecting = false;
			selectee.selected = true;
			selectee.startselected = true;
			self._trigger("selected", event, {
				selected: selectee.element
			});
		});
		this._trigger("stop", event);

		this.helper.remove();

		return false;
	}

}));

$.extend($.ui.selectable, {
	version: "@VERSION",
	defaults: {
		appendTo: 'body',
		autoRefresh: true,
		cancel: ":input,option",
		delay: 0,
		distance: 0,
		filter: '*',
		tolerance: 'touch'
	}
});

})(jQuery);
<commit_msg>Fixed #4341 - Selectable: option appendTo is ignored, helper always appends to body<commit_after>/*
 * jQuery UI Selectable @VERSION
 *
 * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://docs.jquery.com/UI/Selectables
 *
 * Depends:
 *	ui.core.js
 */
(function($) {

$.widget("ui.selectable", $.extend({}, $.ui.mouse, {

	_init: function() {
		var self = this;

		this.element.addClass("ui-selectable");

		this.dragged = false;

		// cache selectee children based on filter
		var selectees;
		this.refresh = function() {
			selectees = $(self.options.filter, self.element[0]);
			selectees.each(function() {
				var $this = $(this);
				var pos = $this.offset();
				$.data(this, "selectable-item", {
					element: this,
					$element: $this,
					left: pos.left,
					top: pos.top,
					right: pos.left + $this.outerWidth(),
					bottom: pos.top + $this.outerHeight(),
					startselected: false,
					selected: $this.hasClass('ui-selected'),
					selecting: $this.hasClass('ui-selecting'),
					unselecting: $this.hasClass('ui-unselecting')
				});
			});
		};
		this.refresh();

		this.selectees = selectees.addClass("ui-selectee");

		this._mouseInit();

		this.helper = $(document.createElement('div'))
			.css({border:'1px dotted black'})
			.addClass("ui-selectable-helper");
	},

	destroy: function() {
		this.element
			.removeClass("ui-selectable ui-selectable-disabled")
			.removeData("selectable")
			.unbind(".selectable");
		this._mouseDestroy();
	},

	_mouseStart: function(event) {
		var self = this;

		this.opos = [event.pageX, event.pageY];

		if (this.options.disabled)
			return;

		var options = this.options;

		this.selectees = $(options.filter, this.element[0]);

		this._trigger("start", event);

		$(options.appendTo).append(this.helper);
		// position helper (lasso)
		this.helper.css({
			"z-index": 100,
			"position": "absolute",
			"left": event.clientX,
			"top": event.clientY,
			"width": 0,
			"height": 0
		});

		if (options.autoRefresh) {
			this.refresh();
		}

		this.selectees.filter('.ui-selected').each(function() {
			var selectee = $.data(this, "selectable-item");
			selectee.startselected = true;
			if (!event.metaKey) {
				selectee.$element.removeClass('ui-selected');
				selectee.selected = false;
				selectee.$element.addClass('ui-unselecting');
				selectee.unselecting = true;
				// selectable UNSELECTING callback
				self._trigger("unselecting", event, {
					unselecting: selectee.element
				});
			}
		});

		$(event.target).parents().andSelf().each(function() {
			var selectee = $.data(this, "selectable-item");
			if (selectee) {
				selectee.$element.removeClass("ui-unselecting").addClass('ui-selecting');
				selectee.unselecting = false;
				selectee.selecting = true;
				selectee.selected = true;
				// selectable SELECTING callback
				self._trigger("selecting", event, {
					selecting: selectee.element
				});
				return false;
			}
		});

	},

	_mouseDrag: function(event) {
		var self = this;
		this.dragged = true;

		if (this.options.disabled)
			return;

		var options = this.options;

		var x1 = this.opos[0], y1 = this.opos[1], x2 = event.pageX, y2 = event.pageY;
		if (x1 > x2) { var tmp = x2; x2 = x1; x1 = tmp; }
		if (y1 > y2) { var tmp = y2; y2 = y1; y1 = tmp; }
		this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1});

		this.selectees.each(function() {
			var selectee = $.data(this, "selectable-item");
			//prevent helper from being selected if appendTo: selectable
			if (!selectee || selectee.element == self.element[0])
				return;
			var hit = false;
			if (options.tolerance == 'touch') {
				hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
			} else if (options.tolerance == 'fit') {
				hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
			}

			if (hit) {
				// SELECT
				if (selectee.selected) {
					selectee.$element.removeClass('ui-selected');
					selectee.selected = false;
				}
				if (selectee.unselecting) {
					selectee.$element.removeClass('ui-unselecting');
					selectee.unselecting = false;
				}
				if (!selectee.selecting) {
					selectee.$element.addClass('ui-selecting');
					selectee.selecting = true;
					// selectable SELECTING callback
					self._trigger("selecting", event, {
						selecting: selectee.element
					});
				}
			} else {
				// UNSELECT
				if (selectee.selecting) {
					if (event.metaKey && selectee.startselected) {
						selectee.$element.removeClass('ui-selecting');
						selectee.selecting = false;
						selectee.$element.addClass('ui-selected');
						selectee.selected = true;
					} else {
						selectee.$element.removeClass('ui-selecting');
						selectee.selecting = false;
						if (selectee.startselected) {
							selectee.$element.addClass('ui-unselecting');
							selectee.unselecting = true;
						}
						// selectable UNSELECTING callback
						self._trigger("unselecting", event, {
							unselecting: selectee.element
						});
					}
				}
				if (selectee.selected) {
					if (!event.metaKey && !selectee.startselected) {
						selectee.$element.removeClass('ui-selected');
						selectee.selected = false;

						selectee.$element.addClass('ui-unselecting');
						selectee.unselecting = true;
						// selectable UNSELECTING callback
						self._trigger("unselecting", event, {
							unselecting: selectee.element
						});
					}
				}
			}
		});

		return false;
	},

	_mouseStop: function(event) {
		var self = this;

		this.dragged = false;

		var options = this.options;

		$('.ui-unselecting', this.element[0]).each(function() {
			var selectee = $.data(this, "selectable-item");
			selectee.$element.removeClass('ui-unselecting');
			selectee.unselecting = false;
			selectee.startselected = false;
			self._trigger("unselected", event, {
				unselected: selectee.element
			});
		});
		$('.ui-selecting', this.element[0]).each(function() {
			var selectee = $.data(this, "selectable-item");
			selectee.$element.removeClass('ui-selecting').addClass('ui-selected');
			selectee.selecting = false;
			selectee.selected = true;
			selectee.startselected = true;
			self._trigger("selected", event, {
				selected: selectee.element
			});
		});
		this._trigger("stop", event);

		this.helper.remove();

		return false;
	}

}));

$.extend($.ui.selectable, {
	version: "@VERSION",
	defaults: {
		appendTo: 'body',
		autoRefresh: true,
		cancel: ":input,option",
		delay: 0,
		distance: 0,
		filter: '*',
		tolerance: 'touch'
	}
});

})(jQuery);
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013, David Farrell <shokku.ra@gmail.com>
 *  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 iBot 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.
 */

var util = require('util');

exports.mod = function(context, server)
{
	this.streams =
	{
		urgent: process.stdout,
		out: process.stdout,
		err: process.stderr,
		verbose: null,
	};

	this.core$cmd = function(prefix, target, command, params, $core)
	{
		if(command === 'log')
		{
			if(typeof this.data.targets !== 'object') this.data.targets = {};

			switch(params[0])
			{
				case '+':
					this.data.targets[params[1]] = true;
					$core._privmsg(target, 'Done');
					break;
				case '-':
					delete this.data.targets[params[1]];
					$core._privmsg(target, 'Done');
					break;
				case '?':
					// TODO
					break;
			}
		}
	}

	this.$log = function(data, stream)
	{
		this.log(data, stream)
	}

	this.log = function(data, stream)
	{
		this.logUnsafe(util.inspect(data), stream);
	}

	this.logUnsafe = function(data, stream)
	{
		var d = new Date();
		var t = '[' + d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds() + ']';

		if(typeof this.streams[stream] === 'undefined') stream = 'out';

		if(this.streams[stream] !== null)
		{
			this.streams[stream].write(t + ' ' + data + '\n');
		}
	}

	this._logTargets = function(data)
	{
		var dataSafe = util.inspect(data);

		if(typeof this.data.targets !== 'undefined')
		{
			for(var kTarget in this.data.targets)
			{
				if(this.data.targets[kTarget] === true)
				{
					server.do('core$privmsg', kTarget, dataSafe);
				}
			}
		}
	}
}
<commit_msg>Refactored logUnsafe to zerofill time segments.<commit_after>/*
 * Copyright (c) 2013, David Farrell <shokku.ra@gmail.com>
 *  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 iBot 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.
 */

var util = require('util');

exports.mod = function(context, server)
{
	this.streams =
	{
		urgent: process.stdout,
		out: process.stdout,
		err: process.stderr,
		verbose: null,
	};

	this.core$cmd = function(prefix, target, command, params, $core)
	{
		if(command === 'log')
		{
			if(typeof this.data.targets !== 'object') this.data.targets = {};

			switch(params[0])
			{
				case '+':
					this.data.targets[params[1]] = true;
					$core._privmsg(target, 'Done');
					break;
				case '-':
					delete this.data.targets[params[1]];
					$core._privmsg(target, 'Done');
					break;
				case '?':
					// TODO
					break;
			}
		}
	}

	this.$log = function(data, stream)
	{
		this.log(data, stream)
	}

	this.log = function(data, stream)
	{
		this.logUnsafe(util.inspect(data), stream);
	}

	this.logUnsafe = function(data, stream)
	{
		var d = new Date();
		var hours = d.getHours().toString();
		var minutes = d.getMinutes().toString();
		var seconds = d.getSeconds().toString();

		if(hours.length === 1) hours = '0' + hours;
		if(minutes.length === 1) minutes = '0' + minutes;
		if(seconds.length === 1) seconds = '0' + seconds;

		var t = '[' + hours + ':' + minutes + ':' + seconds + ']';

		if(typeof this.streams[stream] === 'undefined') stream = 'out';

		if(this.streams[stream] !== null)
		{
			this.streams[stream].write(t + ' ' + data + '\n');
		}
	}

	this._logTargets = function(data)
	{
		var dataSafe = util.inspect(data);

		if(typeof this.data.targets !== 'undefined')
		{
			for(var kTarget in this.data.targets)
			{
				if(this.data.targets[kTarget] === true)
				{
					server.do('core$privmsg', kTarget, dataSafe);
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>/**
 * Basic Config Variables
 * redis_url (string) - Redis hostname (defaults to localhost)
 * ttl (int) - TTL on keys set in redis (defaults to 1 day)
 */
var redis_url = process.env.REDISTOGO_URL || process.env.REDISCLOUD_URL || process.env.REDISGREEN_URL || process.env.REDIS_URL || "redis://127.0.0.1:6379",
    url = require('url'),
    ttl = process.env.PAGE_TTL || 86400;

// Parse out the connection vars from the env string.
var connection = url.parse(redis_url),
    redis = require('redis'),
    client = redis.createClient(connection.port, connection.hostname),
    redis_online = false,
    last_error = "",
    last_end_message = ""; // Make redis connection

// Select Redis database, parsed from the URL
connection.path = (connection.pathname || '/').slice(1);
connection.database = connection.path.length ? connection.path : '0';
client.select(connection.database);

// Parse out password from the connection string
if (connection.auth) {
    client.auth(connection.auth.split(":")[1]);
}

// Catch all error handler. If redis breaks for any reason it will be reported here.
client.on("error", function (err) {
    if(last_error === err.toString()) {
      // Swallow the error for now
    } else {
      last_error = err.toString();
      console.log("Redis Cache Error: " + err);
    }
});
//
client.on("ready", function () {
    redis_online = true;
    console.log("Redis Cache Connected");
});

client.on("end", function (err) {
  if(err) {
    err = err.toString();
    if(last_end_message == err) {
      // Swallow the error for now
    } else {
      last_end_message = err;
      redis_online = false;
      console.log("Redis Cache Conncetion Closed. Will now bypass redis until it's back.");
    }
  }
});

module.exports = {
    beforePhantomRequest: function (req, res, next) {
        //
        if (req.method !== 'GET' || redis_online !== true) {
            return next();
        }

        client.get(req.prerender.url, function (err, result) {
            // Page found - return to prerender and 200
            if (!err && result) {
                res.send(200, result);
            } else {
                next();
            }
        });
    },

    afterPhantomRequest: function (req, res, next) {
        if (redis_online !== true) {
            return next();
        }
        // Don't cache anything that didn't result in a 200. This is to stop caching of 3xx/4xx/5xx status codes
        if (req.prerender.statusCode === 200) {
            client.set(req.prerender.url, req.prerender.documentHTML, function (err, reply) {
                // If library set to cache set an expiry on the key.
                if (!err && reply && ttl && ttl != 0) {
                    client.expire(req.prerender.url, ttl, function (err, didSetExpiry) {
                        console.warn(!!didSetExpiry);
                    });
                }
            });
        }
        next();
    }
};
<commit_msg>Cleaning up the code. Adding more cachability w/ cachable status codes. Fixed console log levels. Updated to latest Phantom release API. 🎉<commit_after>/**
 * Basic Config Variables
 * redis_url (string) - Redis hostname (defaults to localhost)
 * ttl (int) - TTL on keys set in redis (defaults to 1 day)
 */
var REDIS_URL = process.env.REDISTOGO_URL ||
    process.env.REDISCLOUD_URL ||
    process.env.REDISGREEN_URL ||
    process.env.REDIS_URL ||
    'redis://127.0.0.1:6379';

var url = require('url');
var TTL = process.env.PAGE_TTL || 86400;

// Parse out the connection vars from the env string.
var connection = url.parse(REDIS_URL);
var redis = require('redis');
var client = redis.createClient(connection.port, connection.hostname);
var redisOnline = false;

var STATUS_CODES_TO_CACHE = {
    200: true,
    203: true,
    204: true,
    206: true,
    300: true,
    301: true,
    404: true,
    405: true,
    410: true,
    414: true,
    501: true
};

// Make redis connection
// Select Redis database, parsed from the URL
connection.path = (connection.pathname || '/').slice(1);
connection.database = connection.path.length ? connection.path : '0';
client.select(connection.database);

// Parse out password from the connection string
if (connection.auth) {
    client.auth(connection.auth.split(':')[1]);
}

// Catch all error handler. If redis breaks for any reason it will be reported here.
client.on('error', function (error) {
    console.warn('Redis Cache Error: ' + error);
});

client.on('ready', function () {
    redisOnline = true;
    console.log('Redis Cache Connected');
});

client.on('end', function () {
    redisOnline = false;
    console.warn(
        'Redis Cache Conncetion Closed. Will now bypass redis until it\'s back.'
    );
});

module.exports = {
    requestReceived: function (req, res, next) {
        //
        if (req.method !== 'GET' || !redisOnline) {
            return next();
        }

        client.get(req.prerender.url, function (error, result) {
            if (!error && result) {
                var response = JSON.parse(result);
                var headers = response.headers;
                var key;

                for (key in headers) {
                    if (headers.hasOwnProperty(key)) {
                        res.setHeader(key, headers[key]);
                    }
                }
                res.send(response.statusCode, response.content);
            } else {
                next();
            }
        });
    },

    pageLoaded: function (req, res, next) {
        if (!redisOnline || !STATUS_CODES_TO_CACHE[req.prerender.statusCode]) {
            return next();
        }

        var key = req.prerender.url;
        var response = {
            statusCode: req.prerender.statusCode,
            content: req.prerender.content,
            headers: req.prerender.headers
        };
        client.set(key, JSON.stringify(response), function (error, reply) {
            // If library set to cache set an expiry on the key.
            if (!error && reply && TTL) {
                client.expire(key, TTL, function (error, didSetExpiry) {
                    if (!error && !didSetExpiry) {
                        console.warn('Could not set expiry for "' + key + '"');
                    }
                });
            }
        });

        next();
    }
};
<|endoftext|>


<commit_before>/*!
 * jQuery UI Mouse @VERSION
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Mouse
//>>group: UI Core
//>>description: Abstracts mouse-based interactions to assist in creating certain widgets.
//>>docs: http://api.jqueryui.com/mouse/

( function( factory ) {
	if ( typeof define === "function" && define.amd ) {

		// AMD. Register as an anonymous module.
		define( [
			"jquery",
			"../ie",
			"../version",
			"../widget"
		], factory );
	} else {

		// Browser globals
		factory( jQuery );
	}
}( function( $ ) {

var mouseHandled = false;
$( document ).on( "mouseup", function() {
	mouseHandled = false;
} );

return $.widget( "ui.mouse", {
	version: "@VERSION",
	options: {
		cancel: "input, textarea, button, select, option",
		distance: 1,
		delay: 0
	},
	_mouseInit: function() {
		var that = this;

		this.element
			.on( "mousedown." + this.widgetName, function( event ) {
				return that._mouseDown( event );
			} )
			.on( "click." + this.widgetName, function( event ) {
				if ( true === $.data( event.target, that.widgetName + ".preventClickEvent" ) ) {
					$.removeData( event.target, that.widgetName + ".preventClickEvent" );
					event.stopImmediatePropagation();
					return false;
				}
			} );

		this.started = false;
	},

	// TODO: make sure destroying one instance of mouse doesn't mess with
	// other instances of mouse
	_mouseDestroy: function() {
		this.element.off( "." + this.widgetName );
		if ( this._mouseMoveDelegate ) {
			this.document
				.off( "mousemove." + this.widgetName, this._mouseMoveDelegate )
				.off( "mouseup." + this.widgetName, this._mouseUpDelegate );
		}
	},

	_mouseDown: function( event ) {

		// don't let more than one widget handle mouseStart
		if ( mouseHandled ) {
			return;
		}

		this._mouseMoved = false;

		// We may have missed mouseup (out of window)
		( this._mouseStarted && this._mouseUp( event ) );

		this._mouseDownEvent = event;

		var that = this,
			btnIsLeft = ( event.which === 1 ),

			// event.target.nodeName works around a bug in IE 8 with
			// disabled inputs (#7620)
			elIsCancel = ( typeof this.options.cancel === "string" && event.target.nodeName ? $( event.target ).closest( this.options.cancel ).length : false );
		if ( !btnIsLeft || elIsCancel || !this._mouseCapture( event ) ) {
			return true;
		}

		this.mouseDelayMet = !this.options.delay;
		if ( !this.mouseDelayMet ) {
			this._mouseDelayTimer = setTimeout( function() {
				that.mouseDelayMet = true;
			}, this.options.delay );
		}

		if ( this._mouseDistanceMet( event ) && this._mouseDelayMet( event ) ) {
			this._mouseStarted = ( this._mouseStart( event ) !== false );
			if ( !this._mouseStarted ) {
				event.preventDefault();
				return true;
			}
		}

		// Click event may never have fired (Gecko & Opera)
		if ( true === $.data( event.target, this.widgetName + ".preventClickEvent" ) ) {
			$.removeData( event.target, this.widgetName + ".preventClickEvent" );
		}

		// These delegates are required to keep context
		this._mouseMoveDelegate = function( event ) {
			return that._mouseMove( event );
		};
		this._mouseUpDelegate = function( event ) {
			return that._mouseUp( event );
		};

		this.document
			.on( "mousemove." + this.widgetName, this._mouseMoveDelegate )
			.on( "mouseup." + this.widgetName, this._mouseUpDelegate );

		event.preventDefault();

		mouseHandled = true;
		return true;
	},

	_mouseMove: function( event ) {

		// Only check for mouseups outside the document if you've moved inside the document
		// at least once. This prevents the firing of mouseup in the case of IE<9, which will
		// fire a mousemove event if content is placed under the cursor. See #7778
		// Support: IE <9
		if ( this._mouseMoved ) {

			// IE mouseup check - mouseup happened when mouse was out of window
			if ( $.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button ) {
				return this._mouseUp( event );

			// Iframe mouseup check - mouseup occurred in another document
			} else if ( !event.which ) {
				return this._mouseUp( event );
			}
		}

		if ( event.which || event.button ) {
			this._mouseMoved = true;
		}

		if ( this._mouseStarted ) {
			this._mouseDrag( event );
			return event.preventDefault();
		}

		if ( this._mouseDistanceMet( event ) && this._mouseDelayMet( event ) ) {
			this._mouseStarted =
				( this._mouseStart( this._mouseDownEvent, event ) !== false );
			( this._mouseStarted ? this._mouseDrag( event ) : this._mouseUp( event ) );
		}

		return !this._mouseStarted;
	},

	_mouseUp: function( event ) {
		this.document
			.off( "mousemove." + this.widgetName, this._mouseMoveDelegate )
			.off( "mouseup." + this.widgetName, this._mouseUpDelegate );

		if ( this._mouseStarted ) {
			this._mouseStarted = false;

			if ( event.target === this._mouseDownEvent.target ) {
				$.data( event.target, this.widgetName + ".preventClickEvent", true );
			}

			this._mouseStop( event );
		}

		if (this._mouseDelayTimer) {
			clearTimeout(this._mouseDelayTimer);
			delete this._mouseDelayTimer;
		}

		mouseHandled = false;
		return false;
	},

	_mouseDistanceMet: function( event ) {
		return ( Math.max(
				Math.abs( this._mouseDownEvent.pageX - event.pageX ),
				Math.abs( this._mouseDownEvent.pageY - event.pageY )
			) >= this.options.distance
		);
	},

	_mouseDelayMet: function( /* event */ ) {
		return this.mouseDelayMet;
	},

	// These are placeholder methods, to be overriden by extending plugin
	_mouseStart: function( /* event */ ) {},
	_mouseDrag: function( /* event */ ) {},
	_mouseStop: function( /* event */ ) {},
	_mouseCapture: function( /* event */ ) { return true; }
} );

} ) );
<commit_msg>Mouse: Style fixes for jscs<commit_after>/*!
 * jQuery UI Mouse @VERSION
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Mouse
//>>group: UI Core
//>>description: Abstracts mouse-based interactions to assist in creating certain widgets.
//>>docs: http://api.jqueryui.com/mouse/

( function( factory ) {
	if ( typeof define === "function" && define.amd ) {

		// AMD. Register as an anonymous module.
		define( [
			"jquery",
			"../ie",
			"../version",
			"../widget"
		], factory );
	} else {

		// Browser globals
		factory( jQuery );
	}
}( function( $ ) {

var mouseHandled = false;
$( document ).on( "mouseup", function() {
	mouseHandled = false;
} );

return $.widget( "ui.mouse", {
	version: "@VERSION",
	options: {
		cancel: "input, textarea, button, select, option",
		distance: 1,
		delay: 0
	},
	_mouseInit: function() {
		var that = this;

		this.element
			.on( "mousedown." + this.widgetName, function( event ) {
				return that._mouseDown( event );
			} )
			.on( "click." + this.widgetName, function( event ) {
				if ( true === $.data( event.target, that.widgetName + ".preventClickEvent" ) ) {
					$.removeData( event.target, that.widgetName + ".preventClickEvent" );
					event.stopImmediatePropagation();
					return false;
				}
			} );

		this.started = false;
	},

	// TODO: make sure destroying one instance of mouse doesn't mess with
	// other instances of mouse
	_mouseDestroy: function() {
		this.element.off( "." + this.widgetName );
		if ( this._mouseMoveDelegate ) {
			this.document
				.off( "mousemove." + this.widgetName, this._mouseMoveDelegate )
				.off( "mouseup." + this.widgetName, this._mouseUpDelegate );
		}
	},

	_mouseDown: function( event ) {

		// don't let more than one widget handle mouseStart
		if ( mouseHandled ) {
			return;
		}

		this._mouseMoved = false;

		// We may have missed mouseup (out of window)
		( this._mouseStarted && this._mouseUp( event ) );

		this._mouseDownEvent = event;

		var that = this,
			btnIsLeft = ( event.which === 1 ),

			// event.target.nodeName works around a bug in IE 8 with
			// disabled inputs (#7620)
			elIsCancel = ( typeof this.options.cancel === "string" && event.target.nodeName ? $( event.target ).closest( this.options.cancel ).length : false );
		if ( !btnIsLeft || elIsCancel || !this._mouseCapture( event ) ) {
			return true;
		}

		this.mouseDelayMet = !this.options.delay;
		if ( !this.mouseDelayMet ) {
			this._mouseDelayTimer = setTimeout( function() {
				that.mouseDelayMet = true;
			}, this.options.delay );
		}

		if ( this._mouseDistanceMet( event ) && this._mouseDelayMet( event ) ) {
			this._mouseStarted = ( this._mouseStart( event ) !== false );
			if ( !this._mouseStarted ) {
				event.preventDefault();
				return true;
			}
		}

		// Click event may never have fired (Gecko & Opera)
		if ( true === $.data( event.target, this.widgetName + ".preventClickEvent" ) ) {
			$.removeData( event.target, this.widgetName + ".preventClickEvent" );
		}

		// These delegates are required to keep context
		this._mouseMoveDelegate = function( event ) {
			return that._mouseMove( event );
		};
		this._mouseUpDelegate = function( event ) {
			return that._mouseUp( event );
		};

		this.document
			.on( "mousemove." + this.widgetName, this._mouseMoveDelegate )
			.on( "mouseup." + this.widgetName, this._mouseUpDelegate );

		event.preventDefault();

		mouseHandled = true;
		return true;
	},

	_mouseMove: function( event ) {

		// Only check for mouseups outside the document if you've moved inside the document
		// at least once. This prevents the firing of mouseup in the case of IE<9, which will
		// fire a mousemove event if content is placed under the cursor. See #7778
		// Support: IE <9
		if ( this._mouseMoved ) {

			// IE mouseup check - mouseup happened when mouse was out of window
			if ( $.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button ) {
				return this._mouseUp( event );

			// Iframe mouseup check - mouseup occurred in another document
			} else if ( !event.which ) {
				return this._mouseUp( event );
			}
		}

		if ( event.which || event.button ) {
			this._mouseMoved = true;
		}

		if ( this._mouseStarted ) {
			this._mouseDrag( event );
			return event.preventDefault();
		}

		if ( this._mouseDistanceMet( event ) && this._mouseDelayMet( event ) ) {
			this._mouseStarted =
				( this._mouseStart( this._mouseDownEvent, event ) !== false );
			( this._mouseStarted ? this._mouseDrag( event ) : this._mouseUp( event ) );
		}

		return !this._mouseStarted;
	},

	_mouseUp: function( event ) {
		this.document
			.off( "mousemove." + this.widgetName, this._mouseMoveDelegate )
			.off( "mouseup." + this.widgetName, this._mouseUpDelegate );

		if ( this._mouseStarted ) {
			this._mouseStarted = false;

			if ( event.target === this._mouseDownEvent.target ) {
				$.data( event.target, this.widgetName + ".preventClickEvent", true );
			}

			this._mouseStop( event );
		}

		if ( this._mouseDelayTimer ) {
			clearTimeout( this._mouseDelayTimer );
			delete this._mouseDelayTimer;
		}

		mouseHandled = false;
		return false;
	},

	_mouseDistanceMet: function( event ) {
		return ( Math.max(
				Math.abs( this._mouseDownEvent.pageX - event.pageX ),
				Math.abs( this._mouseDownEvent.pageY - event.pageY )
			) >= this.options.distance
		);
	},

	_mouseDelayMet: function( /* event */ ) {
		return this.mouseDelayMet;
	},

	// These are placeholder methods, to be overriden by extending plugin
	_mouseStart: function( /* event */ ) {},
	_mouseDrag: function( /* event */ ) {},
	_mouseStop: function( /* event */ ) {},
	_mouseCapture: function( /* event */ ) { return true; }
} );

} ) );
<|endoftext|>


<commit_before>var express = require('express');

var webInterface = function(dbot) {
    var dbot = dbot;

    var pub = '../public';
    var app = express.createServer();

    app.use(express.compiler({ src: pub, enable: ['sass'] }));
    app.use(express.static(pub));
    app.set('view engine', 'jade');

    app.get('/', function(req, res) {
        res.render('index', { });
    });

    app.listen(1337);

    return {
        'onDestroy': function() {
            console.log('on destroy');
            process.exit();
        }
    };
};

exports.fetch = function(dbot) {
    return webInterface(dbot);
};
<commit_msg>app.close() ends the event loop<commit_after>var express = require('express');

var webInterface = function(dbot) {
    var dbot = dbot;

    var pub = '../public';
    var app = express.createServer();

    app.use(express.compiler({ src: pub, enable: ['sass'] }));
    app.use(express.static(pub));
    app.set('view engine', 'jade');

    app.get('/', function(req, res) {
        res.render('index', { });
    });

    app.listen(1337);

    return {
        'onDestroy': function() {
            app.close();
        }
    };
};

exports.fetch = function(dbot) {
    return webInterface(dbot);
};
<|endoftext|>