<commit_before>describe("Command", function() {
  var Command = require("../src/command");
  var Rule = require("../src/rule");
  var ServiceException = require("../src/serviceException");

  describe("constructor", () => {
    it("returns a new instance when invoked directly", function() {
      var command = Command();
      expect(command instanceof Command).toBe(true);
    });

    it("returns a new instance when instantiated", function() {
      var command = new Command();
      expect(command instanceof Command).toBe(true);
    });

    it("sets callbacks.onInitialization to a default if not supplied", () => {
      var command = new Command();
      expect(typeof command._onInitialization).toEqual('function');
    });

    it("sets callbacks.getRules to a default if not supplied", () => {
      var command = new Command();
      expect(typeof command._getRules).toEqual('function');
    });

    it("sets callbacks.onValidationSuccess to a default if not supplied", () => {
      var command = new Command();
      expect(typeof command._onValidationSuccess).toEqual('function');
    });

    it("does not override existing functions if already exists (es6 inheritance support)", (onComplete) => {
      "use strict";
      var val = 0
      class MyCommand extends Command {
        constructor() {
          super();
        }
        _onInitialization(context, done) {
          val += 1;
          done();
        }
        _getRules(context, done) {
          val += 1;
          done(null, []);
        }
        _onValidationSuccess(context, done) {
          val += 1;
          done();
        }
      }
      var command = new MyCommand();
      command.execute((e, r) => {
        expect(val).toEqual(3);
        onComplete();
      });
    });
  });

  describe("execute", () => {
    it("invokes the pipeline methods in the correct order", (onComplete) => {
      var state = "";
      var functions = {
        _onInitialization: (context, done) => {
          state += "1";
          done();
        },
        _getRules: (context, done) => {
          state += "2";
          done(null, []);
        },
        _onValidationSuccess: (context, done) => {
          state += "3";
          done();
        }
      }
      var command = new Command(functions);
      command.execute((e, r) => {
        expect(state).toEqual("123");
        onComplete();
      });
    });

    describe("execution results", () => {
      var TrueRule, FalseRule;
      beforeAll(() => {
        TrueRule = Rule.extend({
          functions: {
            _onValidate: function(done) {
              done();
            }
          }
        });

        FalseRule = Rule.extend({
          params: ['message'],
          functions: {
            _onValidate: function(done) {
              this._invalidate(this.message);
              done();
            }
          }
        });
      });

      describe("when no rules configured", () => {
        it("returns the expected validation result", (onComplete) => {
          var returnValue = { id: 5, data: "abc" };
          var functions = {
            _onValidationSuccess: (context, done) => {
              done(null, returnValue);
            }
          }

          var command = new Command(functions);
          command.execute((err, result) => {
            expect(result.success).toEqual(true);
            expect(result.value).toEqual(returnValue);
            expect(result.errors).toBeNull();
            onComplete();
          });
        });
      });

      describe("when one rule configured", () => {
        it("supports single object literal argument as input to getRules callback", (onComplete) => {
          var returnValue = { id: 5, data: "abc" };
          var functions = {
            _getRules: (context, done) => {
              done(null, new TrueRule());
            },
            _onValidationSuccess: (context, done) => {
              done(null, returnValue);
            }
          }

          var command = new Command(functions);
          command.execute((err, result) => {
            expect(result.success).toEqual(true);
            expect(result.value).toEqual(returnValue);
            expect(result.errors).toBeNull();
            onComplete();
          });
        });

        describe("when validation succeeds", () => {
          it("returns the expected validation result", (onComplete) => {
            var returnValue = { id: 5, data: "abc" };
            var functions = {
              _getRules: (context, done) => {
                done(null, [new TrueRule()]);
              },
              _onValidationSuccess: (context, done) => {
                done(null, returnValue);
              }
            }

            var command = new Command(functions);
            command.execute((err, result) => {
              expect(result.success).toEqual(true);
              expect(result.value).toEqual(returnValue);
              expect(result.errors).toBeNull();
              onComplete();
            });
          });
        });

        describe("when validation fails", () => {
          it("returns the expected validation result", (onComplete) => {
            var returnValue = { id: 5, data: "abc" };
            var functions = {
              _getRules: (context, done) => {
                done(null, [new FalseRule("a")]);
              },
              _onValidationSuccess: (context, done) => {
                done(null, returnValue);
              }
            }

            var command = new Command(functions);
            command.execute((err, result) => {
              expect(result.success).toEqual(false);
              expect(result.value).toBeNull();
              expect(result.errors.length).toEqual(1);
              onComplete();
            });
          });
        });
      });

      describe("when multiple rules configured", () => {
        it("validates each rule", (onComplete) => {
          var functions = {
            _getRules: (context, done) => {
              done(null, [
                new FalseRule("a"),
                new TrueRule(),
                new FalseRule("b"),
                new TrueRule(),
                new FalseRule("c")
              ]);
            },
            _onValidationSuccess: (context, done) => {
              done();
            }
          }

          var command = new Command(functions);
          command.execute((err, result) => {
            expect(result.success).toEqual(false);
            expect(result.value).toBeNull();
            expect(result.errors.length).toEqual(3);
            expect(result.errors[0].message).toEqual("a");
            expect(result.errors[1].message).toEqual("b");
            expect(result.errors[2].message).toEqual("c");
            onComplete();
          });
        });

      });

      describe("when an error is received", () => {
        describe("when the error is an instance of ServiceException", () => {
          it("returns the expected validation result", (onComplete) => {
            var functions = {
              _onValidationSuccess: (context, done) => {
                var ex = new ServiceException("404");
                ex.errors.push({ association: "name", message: "name not supplied"});
                done(ex);
              }
            }

            var command = new Command(functions);
            command.execute((err, result) => {
              expect(result.success).toEqual(false);
              expect(result.value).toBeNull();
              expect(result.errors.length).toEqual(1);
              expect(result.errors[0].message).toEqual("name not supplied");
              onComplete();
            });
          });
        });

        describe("when the error is anything other than ServiceException", () => {
          it("returns the error in the callback", (onComplete) => {
            var functions = {
              _onValidationSuccess: (context, done) => {
                done(new Error("something unexpected happened"));
              }
            }

            var command = new Command(functions);
            command.execute((err, result) => {
              expect(err.message).toEqual("something unexpected happened");
              onComplete();
            });

          });
        });

      });

      describe("when an unhandled exception occurs", () => {
        it("returns the error in the callback", (onComplete) => {
          var functions = {
            _onValidationSuccess: (context, done) => {
              throw new Error("something unexpected happened");
            }
          }

          var command = new Command(functions);
          command.execute((err, result) => {
            expect(err.message).toEqual("something unexpected happened");
            onComplete();
          });
        });
      });
    });
  });

  describe("extend", () => {

    it("returns a constructor function that creates a new Command", () => {
      var TestCommand = Command.extend({});
      var command = new TestCommand();

      expect(command instanceof Command).toBe(true);
    });

    it("creates different instances", () => {
      var Test1Command = Command.extend({});
      var Test2Command = Command.extend({});
      var test1Command = new Test1Command();
      var test2Command = new Test2Command();

      expect(test1Command._onInitialization).not.toEqual(test2Command._onInitialization);
    })

    it("creates default functions if none supplied", () => {
      var TestCommand = Command.extend({});
      var command = new TestCommand();

      expect(command._onInitialization).toBeDefined();
      expect(command._getRules).toBeDefined();
      expect(command._onValidationSuccess).toBeDefined();
    });

    it("contains a reference to the supplied arguments", () => {
      var TestCommand = Command.extend({});
      var command = new TestCommand(1, "my name is");
      expect(command.arguments[0]).toEqual(1);
      expect(command.arguments[1]).toEqual("my name is");
    });

    it("correctly maps params to arguments", () => {
      var TestCommand = Command.extend({
        params: ['id', 'name']
      });
      var command = new TestCommand(1, "my name is");
      expect(command.id).toEqual(1);
      expect(command.name).toEqual("my name is");
    });

    it("creates instances that do not share state", () => {
      var TestCommand = Command.extend({});
      var command1 = new TestCommand(1, "my name is");
      var command2 = new TestCommand(2, "your name is");
      expect(command1.arguments[0]).not.toEqual(command2.arguments[0]);
      expect(command1.arguments[1]).not.toEqual(command2.arguments[1]);
    });

    it("does not override supplied functions", () => {
      function onInitialization(context, done) { }
      function getRules(context, done) { }
      function onValidationSuccess(context, done) { }

      var TestCommand = Command.extend({
        functions: {
          _onInitialization: onInitialization,
          _getRules: getRules,
          _onValidationSuccess: onValidationSuccess
        }
      });

      var command = new TestCommand();
      expect(command._onInitialization).toEqual(onInitialization);
      expect(command._getRules).toEqual(getRules);
      expect(command._onValidationSuccess).toEqual(onValidationSuccess);
    });

  });

  describe("executeAll", () => {

    it("invokes callback immediately if passed empty array", (onComplete) => {
      Command.executeAll([], (err, result) => {
        expect(result).toBe(undefined);
        onComplete();
      })
    });

    it("invokes all commands", (onComplete) => {
      var TestCommand = Command.extend({
        params: ['val'],
        functions: {
          _onValidationSuccess: function(context, done) {
            done(null, this.val);
          }
        }
      });

      var commands = [
        new TestCommand(4),
        new TestCommand(2)
      ];

      Command.executeAll(commands, (err, results) => {
        expect(results[0].value).toEqual(4);
        expect(results[1].value).toEqual(2);
        onComplete();
      });

    });
  });

  describe('Configuration.autoPromiseWrap = true', () => {
    it("invokes each function without an explicit return of a promise", async () => {
    });
  });

});
<commit_msg>commandSpec updates<commit_after>describe("Command", function() {
  var Command = require("../src/command");
  var Rule = require("../src/rule");
  var ServiceException = require("../src/serviceException");
  var Configuration = require("../src/configuration");

  function promisify(command) {
    return {
      execute: function() {
        return new Promise((resolve, reject) => {
          command.execute(function (err, result) {
            if (err) return reject(err);
            resolve(result);
          });
        });
      }
    }
  }

  describe("constructor", () => {
    it("returns a new instance when invoked directly", function() {
      var command = Command();
      expect(command instanceof Command).toBe(true);
    });

    it("returns a new instance when instantiated", function() {
      var command = new Command();
      expect(command instanceof Command).toBe(true);
    });

    it("sets callbacks.onInitialization to a default if not supplied", () => {
      var command = new Command();
      expect(typeof command._onInitialization).toEqual('function');
    });

    it("sets callbacks.getRules to a default if not supplied", () => {
      var command = new Command();
      expect(typeof command._getRules).toEqual('function');
    });

    it("sets callbacks.onValidationSuccess to a default if not supplied", () => {
      var command = new Command();
      expect(typeof command._onValidationSuccess).toEqual('function');
    });

    it("does not override existing functions if already exists (es6 inheritance support)", (onComplete) => {
      "use strict";
      var val = 0
      class MyCommand extends Command {
        constructor() {
          super();
        }
        _onInitialization(context, done) {
          val += 1;
          done();
        }
        _getRules(context, done) {
          val += 1;
          done(null, []);
        }
        _onValidationSuccess(context, done) {
          val += 1;
          done();
        }
      }
      var command = new MyCommand();
      command.execute((e, r) => {
        expect(val).toEqual(3);
        onComplete();
      });
    });
  });

  describe("execute", () => {
    it("invokes the pipeline methods in the correct order", async () => {

      var command1 = new Command({
        _onInitialization: (context, done) => {
          context.stuff = "1";
          done();
        },
        _getRules: (context, done) => {
          context.stuff += "2";
          done(null, []);
        },
        _onValidationSuccess: (context, done) => {
          context.stuff += "3";
          done(null, context);
        }
      });

      var command2 = new Command({
        _onInitialization: (context) => {
          context.stuff = "1";
          return Promise.resolve();
        },
        _getRules: (context) => {
          context.stuff += "2";
          return Promise.resolve([]);
        },
        _onValidationSuccess: (context) => {
          context.stuff += "3";
          return Promise.resolve(context);
        }
      });

      var results = await Promise.all([
        promisify(command1).execute(),
        command2.execute()
      ]);

      expect(results[0].value.stuff).toEqual("123");
      expect(results[1].value.stuff).toEqual("123");
    });

    describe("execution results", () => {
      var TrueRule, FalseRule;
      beforeAll(() => {
        TrueRuleWithCallback = Rule.extend({
          functions: {
            _onValidate: function(done) {
              done();
            }
          }
        });

        FalseRuleWithCallback = Rule.extend({
          params: ['message'],
          functions: {
            _onValidate: function(done) {
              this._invalidate(this.message);
              done();
            }
          }
        });

        TrueRuleWithPromise = Rule.extend({
          functions: {
            _onValidate: function() {
              return Promise.resolve();
            }
          }
        });

        FalseRuleWithPromise = Rule.extend({
          params: ['message'],
          functions: {
            _onValidate: function() {
              this._invalidate(this.message);
              return Promise.resolve();
            }
          }
        });
      });

      describe("when no rules configured", () => {
        it("returns the expected validation result", async () => {
          var returnValue = { id: 5, data: "abc" };
          var functions1 = {
            _onValidationSuccess: (context, done) => {
              done(null, returnValue);
            }
          };
          var functions2 = {
            _onValidationSuccess: (context) => {
              return Promise.resolve(returnValue);
            }
          };

          var command1 = new Command(functions1);
          var command2 = new Command(functions2);

          var results = await Promise.all([
            promisify(command1).execute(),
            command2.execute()
          ]);

          expect(results[0].success).toEqual(true);
          expect(results[0].value).toEqual(returnValue);
          expect(results[0].errors).toBeNull();

          expect(results[1].success).toEqual(true);
          expect(results[1].value).toEqual(returnValue);
          expect(results[1].errors).toBeNull();
        });
      });

      describe("when one rule configured", () => {
        it("supports single object literal argument as input to getRules callback", async () => {
          var returnValue = { id: 5, data: "abc" };

          var command1 = new Command({
            _getRules: (context, done) => {
              done(null, new TrueRuleWithCallback());
            },
            _onValidationSuccess: (context, done) => {
              done(null, returnValue);
            }
          });

          var command2 = new Command({
            _getRules: (context) => {
              return Promise.resolve(new TrueRuleWithPromise());
            },
            _onValidationSuccess: (context) => {
              return Promise.resolve(returnValue);
            }
          });

          var results = await Promise.all([
            promisify(command1).execute(),
            command2.execute()
          ]);

          expect(results[0].success).toEqual(true);
          expect(results[0].value).toEqual(returnValue);
          expect(results[0].errors).toBeNull();

          expect(results[1].success).toEqual(true);
          expect(results[1].value).toEqual(returnValue);
          expect(results[1].errors).toBeNull();
        });

        describe("when validation succeeds", () => {
          it("returns the expected validation result", async () => {
            var returnValue = { id: 5, data: "abc" };
            var command1 = new Command({
              _getRules: (context, done) => {
                done(null, [new TrueRuleWithCallback()]);
              },
              _onValidationSuccess: (context, done) => {
                done(null, returnValue);
              }
            });
            var command2 = new Command({
              _getRules: (context) => {
                return Promise.resolve([new TrueRuleWithPromise()]);
              },
              _onValidationSuccess: (context) => {
                return Promise.resolve(returnValue);
              }
            });

            var results = await Promise.all([
              promisify(command1).execute(),
              command2.execute()
            ]);

            expect(results[0].success).toEqual(true);
            expect(results[0].value).toEqual(returnValue);
            expect(results[0].errors).toBeNull();

            expect(results[1].success).toEqual(true);
            expect(results[1].value).toEqual(returnValue);
            expect(results[1].errors).toBeNull();
          });
        });

        describe("when validation fails", () => {
          it("returns the expected validation result", async () => {
            var returnValue = { id: 5, data: "abc" };
            var command1 = new Command({
              _getRules: (context, done) => {
                done(null, [new FalseRuleWithCallback("a")]);
              },
              _onValidationSuccess: (context, done) => {
                done(null, returnValue);
              }
            });
            var command2 = new Command({
              _getRules: (context) => {
                return Promise.resolve([new FalseRuleWithPromise("a")]);
              },
              _onValidationSuccess: (context) => {
                return Promise.resolve(returnValue);
              }
            });

            var results = await Promise.all([
              promisify(command1).execute(),
              command2.execute()
            ]);

            expect(results[0].success).toEqual(false);
            expect(results[0].value).toBeNull();
            expect(results[0].errors.length).toEqual(1);

            expect(results[1].success).toEqual(false);
            expect(results[1].value).toBeNull();
            expect(results[1].errors.length).toEqual(1);
          });
        });
      });

      describe("when multiple rules configured", () => {
        it("validates each rule", async () => {
          var command1 = new Command({
            _getRules: (context, done) => {
              done(null, [
                new FalseRuleWithCallback("a"),
                new TrueRuleWithCallback(),
                new FalseRuleWithCallback("b"),
                new TrueRuleWithCallback(),
                new FalseRuleWithCallback("c")
              ]);
            }
          });
          var command2 = new Command({
            _getRules: (context) => {
              return Promise.resolve([
                new FalseRuleWithPromise("a"),
                new TrueRuleWithPromise(),
                new FalseRuleWithPromise("b"),
                new TrueRuleWithPromise(),
                new FalseRuleWithPromise("c")
              ]);
            }
          });

          var results = await Promise.all([
            promisify(command1).execute(),
            command2.execute()
          ]);

          expect(results[0].success).toEqual(false);
          expect(results[0].value).toBeNull();
          expect(results[0].errors.length).toEqual(3);
          expect(results[0].errors[0].message).toEqual("a");
          expect(results[0].errors[1].message).toEqual("b");
          expect(results[0].errors[2].message).toEqual("c");

          expect(results[1].success).toEqual(false);
          expect(results[1].value).toBeNull();
          expect(results[1].errors.length).toEqual(3);
          expect(results[1].errors[0].message).toEqual("a");
          expect(results[1].errors[1].message).toEqual("b");
          expect(results[1].errors[2].message).toEqual("c");
        });

      });

      describe("when a promise rejection is encountered", () => {

        describe("when the error is an instance of ServiceException", () => {
          it("returns the expected validation result", (onComplete) => {
            var functions = {
              _onValidationSuccess: (context) => {
                var ex = new ServiceException("404");
                ex.errors.push({ association: "name", message: "name not supplied"});
                return Promise.reject(ex);
              }
            }

            var command = new Command(functions);
            command.execute().then(result => {
              expect(result.success).toEqual(false);
              expect(result.value).toBeNull();
              expect(result.errors.length).toEqual(1);
              expect(result.errors[0].message).toEqual("name not supplied");
              onComplete();
            });
          });
        });

        describe("when the error is anything other than ServiceException", () => {
          it("returns a promise rejection", (onComplete) => {
            var functions = {
              _onValidationSuccess: (context) => {
                return Promise.reject(new Error("something unexpected happened"));
              }
            }

            var command = new Command(functions);
            command.execute().catch(err => {
              expect(err.message).toEqual("something unexpected happened");
              onComplete();
            });
          });
        });

        describe("when an unhandled exception occurs", () => {

          describe("when the error is an instance of ServiceException", () => {
            it("returns the expected validation result when an exception is handled", (onComplete) => {
              var functions = {
                _onValidationSuccess: (context) => {
                  var ex = new ServiceException("404");
                  ex.errors.push({ association: "name", message: "name not supplied"});
                  throw ex;
                }
              }

              var command = new Command(functions);
              command.execute().then(result => {
                expect(result.success).toEqual(false);
                expect(result.value).toBeNull();
                expect(result.errors.length).toEqual(1);
                expect(result.errors[0].message).toEqual("name not supplied");
                onComplete();
              });
            });
          });

          describe("when the error is anything other than ServiceException", () => {
            it("returns a promise rejection", (onComplete) => {
              var functions = {
                _onValidationSuccess: (context) => {
                  throw new Error("something unexpected happened");
                }
              }
              var command = new Command(functions);
              command.execute().catch(err => {
                expect(err.message).toEqual("something unexpected happened");
                onComplete();
              });
            });
          });

        });
      });

      describe("when an error is received", () => {

        describe("when the error is an instance of ServiceException", () => {
          it("returns the expected validation result", (onComplete) => {
            var functions = {
              _onValidationSuccess: (context, done) => {
                var ex = new ServiceException("404");
                ex.errors.push({ association: "name", message: "name not supplied"});
                done(ex);
              }
            }

            var command = new Command(functions);
            command.execute((err, result) => {
              expect(result.success).toEqual(false);
              expect(result.value).toBeNull();
              expect(result.errors.length).toEqual(1);
              expect(result.errors[0].message).toEqual("name not supplied");
              onComplete();
            });
          });
        });

        describe("when the error is anything other than ServiceException", () => {
          it("returns the error in the callback", (onComplete) => {
            var functions = {
              _onValidationSuccess: (context, done) => {
                done(new Error("something unexpected happened"));
              }
            }

            var command = new Command(functions);
            command.execute((err, result) => {
              expect(err.message).toEqual("something unexpected happened");
              onComplete();
            });

          });
        });

      });

      describe("when an unhandled exception occurs", () => {
        it("returns the error in the callback", (onComplete) => {
          var functions = {
            _onValidationSuccess: (context, done) => {
              throw new Error("something unexpected happened");
            }
          }

          var command = new Command(functions);
          command.execute((err, result) => {
            expect(err.message).toEqual("something unexpected happened");
            onComplete();
          });
        });
      });
    });
  });

  describe("extend", () => {

    it("returns a constructor function that creates a new Command", () => {
      var TestCommand = Command.extend({});
      var command = new TestCommand();

      expect(command instanceof Command).toBe(true);
    });

    it("creates different instances", () => {
      var Test1Command = Command.extend({});
      var Test2Command = Command.extend({});
      var test1Command = new Test1Command();
      var test2Command = new Test2Command();

      expect(test1Command._onInitialization).not.toEqual(test2Command._onInitialization);
    })

    it("creates default functions if none supplied", () => {
      var TestCommand = Command.extend({});
      var command = new TestCommand();

      expect(command._onInitialization).toBeDefined();
      expect(command._getRules).toBeDefined();
      expect(command._onValidationSuccess).toBeDefined();
    });

    it("contains a reference to the supplied arguments", () => {
      var TestCommand = Command.extend({});
      var command = new TestCommand(1, "my name is");
      expect(command.arguments[0]).toEqual(1);
      expect(command.arguments[1]).toEqual("my name is");
    });

    it("correctly maps params to arguments", () => {
      var TestCommand = Command.extend({
        params: ['id', 'name']
      });
      var command = new TestCommand(1, "my name is");
      expect(command.id).toEqual(1);
      expect(command.name).toEqual("my name is");
    });

    it("creates instances that do not share state", () => {
      var TestCommand = Command.extend({});
      var command1 = new TestCommand(1, "my name is");
      var command2 = new TestCommand(2, "your name is");
      expect(command1.arguments[0]).not.toEqual(command2.arguments[0]);
      expect(command1.arguments[1]).not.toEqual(command2.arguments[1]);
    });

    it("does not override supplied functions", () => {
      function onInitialization(context, done) { }
      function getRules(context, done) { }
      function onValidationSuccess(context, done) { }

      var TestCommand = Command.extend({
        functions: {
          _onInitialization: onInitialization,
          _getRules: getRules,
          _onValidationSuccess: onValidationSuccess
        }
      });

      var command = new TestCommand();
      expect(command._onInitialization).toEqual(onInitialization);
      expect(command._getRules).toEqual(getRules);
      expect(command._onValidationSuccess).toEqual(onValidationSuccess);
    });

    it('passes constructor parameters to functions when params are not supplied in extend options', (onComplete) => {
      var Command1 = Command.extend({
        functions: {
          _onInitialization: function(a, b, c, context, done) {
            context.a = a; context.b = b; context.c = c;
            done();
          },
          _getRules: function(a, b, c, context, done) {
            context.a += a; context.b += b; context.c += c;
            done(null, []);
          },
          _onValidationSuccess: function(a, b, c, context, done) {
            context.a += a; context.b += b; context.c += c;
            done(null, context);
          }
        }
      });

      // NOTE: this works due to autoPromiseWrap
      var Command2 = Command.extend({
        functions: {
          _onInitialization: function(a, b, c, context) {
            context.a = a; context.b = b; context.c = c;
          },
          _getRules: function(a, b, c, context) {
            context.a += a; context.b += b; context.c += c;
          },
          _onValidationSuccess: function(a, b, c, context) {
            context.a += a; context.b += b; context.c += c;
            return context;
          }
        }
      });

      var command = new Command1(2, 4, 6).execute((err, result) => {
        expect(result.value).toEqual({ a: 6, b: 12, c: 18});
        var command2 = new Command2(2, 4, 6).execute().then(result => {
          expect(result.value).toEqual({ a: 6, b: 12, c: 18});
          onComplete();
        })
      });

    });

  });

  describe("executeAll", () => {

    it("invokes callback immediately if passed empty array", async () => {
      var results = await Promise.all([
        new Promise((resolve, reject) => {
          Command.executeAll([], (err, result) => {
            resolve(result);
          });
        }),
        Command.executeAll([])
      ]);

      expect(results[0]).toBe(undefined);
      expect(results[1]).toBe(undefined);
    });

    it("invokes all commands", async () => {
      var TestCommand1 = Command.extend({
        params: ['val'],
        functions: {
          _onValidationSuccess: function(val, context, done) {
            done(null, this.val);
          }
        }
      });

      var TestCommand2 = Command.extend({
        params: ['val'],
        functions: {
          _onValidationSuccess: function(val, context) {
            return Promise.resolve(this.val);
          }
        }
      });

      var commandsWithCallbacks = [
        new TestCommand1(4),
        new TestCommand1(2)
      ];

      var commandsWithPromises = [
        new TestCommand2(4),
        new TestCommand2(2)
      ];

      var results = await Promise.all([
        new Promise((resolve, reject) => {
          Command.executeAll(commandsWithCallbacks, (err, result) => {
            if (err) return reject(err);
            resolve(result);
          });
        }),
        Command.executeAll(commandsWithPromises)
      ]);

      expect(results[0][0].value).toEqual(4);
      expect(results[0][1].value).toEqual(2);
      expect(results[1][0].value).toEqual(4);
      expect(results[1][1].value).toEqual(2);
    });
  });

  describe('Configuration.autoPromiseWrap = true', () => {
    it("invokes each function without an explicit return of a promise", async () => {
      Configuration.autoPromiseWrap = true;

      var command1 = new Command({
        _onInitialization: function(context) {
        },
        _getRules: function(context) {
        },
        _onValidationSuccess: function(context) {
          return { stuff: "command1 result"};
        }
      });

      var Command2 = Command.extend({
        functions: {
          _onInitialization: function(context) {
          },
          _getRules: function(context) {
          },
          _onValidationSuccess: function(context) {
            return { stuff: "command2 result"};
          }
        }
      });

      var Command3 = Command.extend({
        functions: {
          _onInitialization: function(context) {
          },
          _getRules: function(context) {
          },
          _onValidationSuccess: function(context) {
            return { stuff: "command3 result"};
          }
        }
      });

      class Command4 extends Command {
        _onInitialization() {
        }
        _getRules() {
        }
        _onValidationSuccess() {
          return { stuff: "command4 result"};
        }
      }

      var command2 = new Command2();
      var command3 = new Command3('command3', 'result');
      var command4 = new Command4()

      var results = await Promise.all([
        command1.execute(),
        command2.execute(),
        command3.execute(),
        command4.execute()
      ]);

      expect(results[0].value.stuff).toEqual("command1 result");
      expect(results[1].value.stuff).toEqual("command2 result");
      expect(results[2].value.stuff).toEqual("command3 result");
      expect(results[3].value.stuff).toEqual("command4 result");
    });
  });

});
<|endoftext|>


<commit_before>export function init() {
    $(".swipemod").each((index, elem) => {
        new SrfSwiper(elem);
    });
}

const animationSpeed = 500;
const DIRECTION = {LEFT: "left", RIGHT: "right"};
const BREAKPOINT_2 = 768;

export class SrfSwiper {

    constructor(element) {
        this.$element = $(element);
        this.$swipeContainer = this.$element.find(".swipemod-swipecontainer");
        this.$container = this.$element.find(".swipemod-container");
        this.containerPadding = parseInt(this.$container.css("paddingLeft"));

        this.$items = this.$element.find(".swipemod-item");
        this.$prevBtn = this.$element.find(".swipemod-button[data-direction='left']");
        this.$nextBtn = this.$element.find(".swipemod-button[data-direction='right']");

        this.registerListeners();

        this.showHidePrevNextButtons();
    }

    registerListeners() {
        this.$element.on("click", ".swipemod-button", event => this.onButtonClick(event) );

        this.$element.on("click", ".swipemod-item", event => this.onItemClick(event) );
    }

    onButtonClick(event) {
        let $btn = $(event.currentTarget),
            direction = $btn.data("direction");

        if (direction === DIRECTION.LEFT) {
            this.scrollLeft();
        } else if (direction === DIRECTION.RIGHT) {
            this.scrollRight();
        }
    }

    /**
     * If an item is completely visible, let the browser handle the link click.
     * Otherwise, prevent that and scroll the item into view.
     * @param event
     */
    onItemClick(event) {
        let $itemElem = $(event.currentTarget);

        if ( !this.isItemCompletelyInView($itemElem) ) {
            event.preventDefault();
            this.scrollItemIntoView($itemElem);
        }
    }

    /**
     * We can scroll to the left as long as the first item is "too far left".
     *
     * @return {Boolean}
     */
    canScrollLeft() {
        return this.isOutOfBoundsLeft( this.$items.first() );
    }

    /**
     * We can scroll to the right as long as the last item is "too far right".
     *
     * @return {Boolean}
     */
    canScrollRight() {
        return this.isOutOfBoundsRight( this.$items.last() );
    }

    showHidePrevNextButtons() {
        let showLeft = false,
            showRight = false;

        if (this.isSize2()) {
            showLeft = this.canScrollLeft();
            showRight = this.canScrollRight();
        }

        this.$prevBtn.toggle( showLeft );
        this.$nextBtn.toggle( showRight );
    };

    /**
     * An element is inside its parent's boundaries if it doesn't go over the left or right side.
     * @param $itemElem
     * @return {boolean}
     */
    isItemCompletelyInView($itemElem) {
        return !this.isOutOfBoundsLeft($itemElem) && !this.isOutOfBoundsRight($itemElem);
    }

    isOutOfBoundsLeft($itemElem) {
        return $itemElem.offset().left < this.$container.offset().left;
    }

    isOutOfBoundsRight($itemElem) {
        let rightEdgeItem = $itemElem.offset().left + $itemElem.outerWidth(),
            rightEdgeContainer = this.$container.offset().left + this.$container.outerWidth();

        return rightEdgeItem > rightEdgeContainer;
    }

    scrollLeft() {
        $(this.$items.get().reverse()).each( (index, el) => {
            if ( this.isOutOfBoundsLeft( $(el) ) ) {
                this.scrollItemIntoView( $(el) );
                return false;
            }
        });

        this.showHidePrevNextButtons();
    }

    scrollRight() {
        this.$items.each( (index, el) => {
            if ( this.isOutOfBoundsRight( $(el) ) ) {
                this.scrollItemIntoView( $(el) );
                return false;
            }
        });

        this.showHidePrevNextButtons();
    }

    scrollItemIntoView($itemElem) {
        let currentScrollPos = this.$swipeContainer.scrollLeft(),
            newScrollPos = currentScrollPos;

        if (this.isOutOfBoundsLeft($itemElem)) {
            newScrollPos -= this.$container.offset().left - $itemElem.offset().left;
            newScrollPos -= this.containerPadding;
        } else {
            let rightEdgeItem = $itemElem.offset().left + $itemElem.outerWidth(),
                rightEdgeContainer = this.$container.offset().left + this.$container.outerWidth();

            newScrollPos += rightEdgeItem - rightEdgeContainer;
            newScrollPos += this.containerPadding;
        }

        this.$swipeContainer.animate({
            scrollLeft: newScrollPos
        }, animationSpeed, () => {
            this.showHidePrevNextButtons();
        });
    }

    isSize2() {
        return window.innerWidth >= BREAKPOINT_2;
    }
}
<commit_msg>debounced afterScroll<commit_after>export function init() {
    $(".swipemod").each((index, elem) => {
        new SrfSwiper(elem);
    });
}

const animationSpeed = 500;
const DIRECTION = {LEFT: "left", RIGHT: "right"};
const BREAKPOINT_2 = 768;
const isSize2 = () => {
    return window.innerWidth >= BREAKPOINT_2;
};
const debounce = (fn, time) => {
    let timeout;

    return function() {
        const functionCall = () => fn.apply(this, arguments);

        clearTimeout(timeout);
        timeout = setTimeout(functionCall, time);
    }
};

export class SrfSwiper {

    constructor(element) {
        this.$element = $(element);
        this.$swipeContainer = this.$element.find(".swipemod-swipecontainer");
        this.$container = this.$element.find(".swipemod-container");
        this.containerPadding = parseInt(this.$container.css("paddingLeft"));

        this.$items = this.$element.find(".swipemod-item");
        this.$prevBtn = this.$element.find(".swipemod-button[data-direction='left']");
        this.$nextBtn = this.$element.find(".swipemod-button[data-direction='right']");

        this.registerListeners();

        this.showHidePrevNextButtons();
    }

    registerListeners() {
        this.$element.on("click", ".swipemod-button", event => this.onButtonClick(event) );

        this.$element.on("click", ".swipemod-item", event => this.onItemClick(event) );

        this.$swipeContainer.on("scroll", debounce(() => this.afterScroll(), 100) );
    }

    afterScroll() {
        this.showHidePrevNextButtons();
    }

    onButtonClick(event) {
        let $btn = $(event.currentTarget),
            direction = $btn.data("direction");

        if (direction === DIRECTION.LEFT) {
            this.scrollLeft();
        } else if (direction === DIRECTION.RIGHT) {
            this.scrollRight();
        }
    }

    /**
     * If an item is completely visible, let the browser handle the link click.
     * Otherwise, prevent that and scroll the item into view.
     * @param event
     */
    onItemClick(event) {
        let $itemElem = $(event.currentTarget);

        if ( !this.isItemCompletelyInView($itemElem) ) {
            event.preventDefault();
            this.scrollItemIntoView($itemElem);
        }
    }

    /**
     * We can scroll to the left as long as the first item is "too far left".
     *
     * @return {Boolean}
     */
    canScrollLeft() {
        return this.isOutOfBoundsLeft( this.$items.first() );
    }

    /**
     * We can scroll to the right as long as the last item is "too far right".
     *
     * @return {Boolean}
     */
    canScrollRight() {
        return this.isOutOfBoundsRight( this.$items.last() );
    }

    showHidePrevNextButtons() {
        let showLeft = false,
            showRight = false;

        if (isSize2()) {
            showLeft = this.canScrollLeft();
            showRight = this.canScrollRight();
        }

        this.$prevBtn.toggle( showLeft );
        this.$nextBtn.toggle( showRight );
    };

    /**
     * An element is inside its parent's boundaries if it doesn't go over the left or right side.
     * @param $itemElem
     * @return {boolean}
     */
    isItemCompletelyInView($itemElem) {
        return !this.isOutOfBoundsLeft($itemElem) && !this.isOutOfBoundsRight($itemElem);
    }

    isOutOfBoundsLeft($itemElem) {
        return $itemElem.offset().left < this.$container.offset().left;
    }

    isOutOfBoundsRight($itemElem) {
        let rightEdgeItem = $itemElem.offset().left + $itemElem.outerWidth(),
            rightEdgeContainer = this.$container.offset().left + this.$container.outerWidth();

        return rightEdgeItem > rightEdgeContainer;
    }

    scrollLeft() {
        $(this.$items.get().reverse()).each( (index, el) => {
            if ( this.isOutOfBoundsLeft( $(el) ) ) {
                this.scrollItemIntoView( $(el) );
                return false;
            }
        });

        this.showHidePrevNextButtons();
    }

    scrollRight() {
        this.$items.each( (index, el) => {
            if ( this.isOutOfBoundsRight( $(el) ) ) {
                this.scrollItemIntoView( $(el) );
                return false;
            }
        });

        this.showHidePrevNextButtons();
    }

    scrollItemIntoView($itemElem) {
        let currentScrollPos = this.$swipeContainer.scrollLeft(),
            newScrollPos = currentScrollPos;

        if (this.isOutOfBoundsLeft($itemElem)) {
            newScrollPos -= this.$container.offset().left - $itemElem.offset().left;
            newScrollPos -= this.containerPadding;
        } else {
            let rightEdgeItem = $itemElem.offset().left + $itemElem.outerWidth(),
                rightEdgeContainer = this.$container.offset().left + this.$container.outerWidth();

            newScrollPos += rightEdgeItem - rightEdgeContainer;
            newScrollPos += this.containerPadding;
        }

        this.$swipeContainer.animate({
            scrollLeft: newScrollPos
        }, animationSpeed, () => {
            this.showHidePrevNextButtons();
        });
    }
}
<|endoftext|>


<commit_before>/*jslint sloppy: true, browser: true */
/*global enyo, moboreader*/

enyo.singleton({
	name: "ArticleContentHandler",
	published: {
		dbActivities: 0,
		isWebos: true
	},
	activityId: 0,
	gettingToStore: false,
	gettingToDL: false,
	storage: {}, //used if not webOS.
	needDL: [],
	checkQueue: [],

	//signals:
	//onArticleOpReturned => success true/false, activityId, content { web, spritz} (optional), id = articleId

	components: [
		{
			kind: "enyo.Signals",
			onArticleOpReturned: "gotContent",
			onArticleDownloaded: "contentDownloaded"
		}
	],

	debugOut: function () {
		//noop or console.error for on device debugging.
		return undefined;
	},

	create: function () {
		this.inherited(arguments);
		this.log("Have PalmSystem: " + (!!window.PalmSystem));
		this.setIsWebos(!!window.PalmSystem);
	},

	storeArticle: function (articleModel, webContent, spritzContent, images) {
		this.activityId += 1;

		if (!webContent || !spritzContent) {
			this.gettingToStore[this.activityId] = {
				model: articleModel,
				webContent: webContent,
				images: images,
				spritzContent: spritzContent,
				getId: this.getContent(articleModel) //getting content from DB to augment it.
			};
		} else {
			this.setDbActivities(this.dbActivities + 1);
			this.privateGenericSend(articleModel, "storeArticleContent", {
				content: {
					web: webContent,
					spritz: spritzContent,
					images: images
				},
				activityId: this.activityId
			});
		}
		return this.activityId;
	},
	gotContent: function (inSender, inEvent) {
		/*jslint unparam: true */
		if (this.gettingToStore) {
			Object.keys(this.gettingToStore).forEach(function (activityId) {
				var obj = this.gettingToStore[activityId],
					content = inEvent.content || {};
				if (obj.getId === inEvent.activityId) {
					this.debugOut("Got article content!");
					obj.model.set("contentAvailable",  true);
					content.web = obj.webContent || content.web;
					content.spritz = obj.spritzContent || content.spritz;
					content.images = obj.images || content.images;
					this.setDbActivities(this.dbActivities + 1);

					this.privateGenericSend(obj.model, "storeArticleContent", {
						content: content,
						activityId: activityId
					});
					delete this.gettingToStore[activityId];
				}
			}.bind(this));
		}

		if (this.gettingToDL) {
			if (this.gettingToDL.activityId === inEvent.activityId) {
				if (!inEvent.success) {
					this.debugOut("Need download: " + this.gettingToDL.model.attributes.item_id);
					this.gettingToDL.model.set("contentAvailable",  false);
					if (!this.downloading || this.downloading === this.gettingToDL.model.attributes.item_id) {
						this.debugOut("Doing direct.");
						this.gettingToDL.api.getArticleContent(this.gettingToDL.model);
						this.downloading = this.gettingToDL.model.attributes.item_id;
					} else {
						this.debugOut("Planing for later. Current DL: " + this.downloading);
						this.needDL.push(this.gettingToDL);
					}
				} else {
					this.log("Article ok, don't download.");
					this.gettingToDL.model.set("contentAvailable",  true);
				}
				delete this.gettingToDL;
			}
		}
	},

	privateGenericSend: function (articleModel, method, inParams) {
		var params = inParams || {}, req;

		if (articleModel) {
			if (articleModel.get) {
				params.id = articleModel.get(articleModel.primaryKey);
			} else {
				params.id = articleModel.item_id;
			}
		}

		if (!params.activityId) {
			this.activityId += 1;
			params.activityId = this.activityId;
			this.setDbActivities(this.dbActivities + 1);
		}

		if (this.isWebos) {
			req = new enyo.ServiceRequest({
				service: "info.mobo.moboreader.service",
				method: method
			});
			req.response(this.dbActivityComplete.bind(this, params.activityId, params.id, method));
			req.error(this.dbError.bind(this, params.activityId, params.id, method));

			req.go(params);
		} else {
			if (params.content) {
				this.storage[params.id] = params.content;
			}
			setTimeout(function (activityId) {
				this.dbActivityComplete(activityId, params.id, method, this, {
					success: method === "articleContentExists" ? !!(params.id && this.storage[params.id]) : true,
					id: params.id,
					content: params.id ? this.storage[params.id] : undefined,
					activityId: activityId
				});
			}.bind(this, this.activityId), 100);
		}
		return this.activityId;
	},
	getContent: function (articleModel) {
		return this.privateGenericSend(articleModel, "getArticleContent");
	},
	articleContentExists: function (articleModel) {
		return this.privateGenericSend(articleModel, "articleContentExists", {requireSpritz: moboreader.Prefs.downloadSpritzOnUpdate});
	},
	deleteContent: function (articleModel) {
		return this.privateGenericSend(articleModel, "deleteArticleContent");
	},
	wipe: function () {
		return this.privateGenericSend(null, "wipeStorage");
	},

	checkAndDownload: function (articleModel, api) {
		if (!this.gettingToDL) {
			this.debugOut("Checking: " + articleModel.attributes.item_id);
			var activityId = this.articleContentExists(articleModel);
			this.gettingToDL = {
				model: articleModel,
				api: api,
				activityId: activityId
			};
		} else {
			this.debugOut("Checking LATER: " + articleModel.attributes.item_id);
			this.checkQueue.unshift({
				model: articleModel,
				api: api
			});
		}
	},

	contentDownloaded: function (inSender, inEvent) {
		/*jslint unparam: true */
		this.debugOut("Download finished: " + inEvent.model.attributes.item_id);
		inEvent.model.set("contentAvailable", !!(inEvent.content && inEvent.content.web && (inEvent.content.spritz || !moboreader.Prefs.downloadSpritzOnUpdate)));
		this.storeArticle(inEvent.model, inEvent.content.web, inEvent.content.spritz, inEvent.content.images);

		if (this.needDL.length > 0) {
			var obj = this.needDL.shift();
			obj.api.getArticleContent(obj.model);
			this.downloading = obj.model.attributes.item_id;
			this.debugOut("Download started: " + this.downloading);
		} else {
			this.debugOut("all downloads finished.");
			this.downloading = false;
		}
	},

	//this will mix queue a bit up.. hm..
	sendNext: function () {
		var req = this.checkQueue.shift();
		if (req) {
			this.checkAndDownload(req.model, req.api);
		}
	},
	dbActivityComplete: function (activityId, id, method, inSender, inEvent) {
		/*jslint unparam: true */
		this.debugOut("Incomming response: " + inEvent.success + " for id " + activityId);
		this.setDbActivities(this.dbActivities - 1);
		inEvent.activityId = activityId;
		inEvent.id = id;
		inEvent.method = method;
		enyo.Signals.send("onArticleOpReturned", inEvent);
		this.sendNext();
	},
	dbError: function (activityId, id, method, inSender, inEvent) {
		/*jslint unparam: true */
		this.log("dbFailed: " + JSON.stringify(inEvent));
		this.setDbActivities(this.dbActivities - 1);
		enyo.Signals.send("onArticleOpReturned", {
			id: id,
			success: false,
			method: method,
			activityId: activityId
		});
		this.sendNext();
	}
});
<commit_msg>fixed bug in storing content<commit_after>/*jslint sloppy: true, browser: true, devel: true */
/*global enyo, moboreader*/

enyo.singleton({
	name: "ArticleContentHandler",
	published: {
		dbActivities: 0,
		isWebos: true
	},
	activityId: 0,
	gettingToStore: {},
	gettingToDL: false,
	storage: {}, //used if not webOS.
	needDL: [],
	checkQueue: [],

	//signals:
	//onArticleOpReturned => success true/false, activityId, content { web, spritz} (optional), id = articleId

	components: [
		{
			kind: "enyo.Signals",
			onArticleOpReturned: "gotContent",
			onArticleDownloaded: "contentDownloaded"
		}
	],

	debugOut: function (msg) {
		//noop or console.error for on device debugging.
		console.log(msg);
		return undefined;
	},

	create: function () {
		this.inherited(arguments);
		this.log("Have PalmSystem: " + (!!window.PalmSystem));
		this.setIsWebos(!!window.PalmSystem);
	},

	storeArticle: function (articleModel, webContent, spritzContent, images) {
		this.activityId += 1;

		if (!webContent || !spritzContent) {
			this.debugOut("Not all content there ( mobo: " + (!!webContent) + ", spritz: " + (!!spritzContent) + "). See what service has for us.");
			this.gettingToStore[this.activityId] = {
				model: articleModel,
				webContent: webContent,
				images: images,
				spritzContent: spritzContent,
				getId: this.getContent(articleModel) //getting content from DB to augment it.
			};
		} else {
			this.setDbActivities(this.dbActivities + 1);
			this.privateGenericSend(articleModel, "storeArticleContent", {
				content: {
					web: webContent,
					spritz: spritzContent,
					images: images
				},
				activityId: this.activityId
			});
		}
		return this.activityId;
	},
	gotContent: function (inSender, inEvent) {
		/*jslint unparam: true */
		if (this.gettingToStore) {
			Object.keys(this.gettingToStore).forEach(function (activityId) {
				var obj = this.gettingToStore[activityId],
					content = inEvent.content || {};
				if (obj.getId === inEvent.activityId) {
					this.debugOut("Got article content from activity " + obj.getId + " for " + obj.model.get("item_id"));
					obj.model.set("contentAvailable",  true);
					content.web = obj.webContent || content.web;
					content.spritz = obj.spritzContent || content.spritz;
					content.images = obj.images || content.images;
					this.setDbActivities(this.dbActivities + 1);

					this.privateGenericSend(obj.model, "storeArticleContent", {
						content: content,
						activityId: activityId
					});
					delete this.gettingToStore[activityId];
				} else {
					this.debugOut("Wrong activity id: " + inEvent.activityId + " for " + obj.getId);
				}
			}.bind(this));
		}

		if (this.gettingToDL) {
			if (this.gettingToDL.activityId === inEvent.activityId) {
				if (!inEvent.success) {
					this.debugOut("Need download: " + this.gettingToDL.model.attributes.item_id);
					this.gettingToDL.model.set("contentAvailable",  false);
					if (!this.downloading || this.downloading === this.gettingToDL.model.attributes.item_id) {
						this.debugOut("Doing direct.");
						this.gettingToDL.api.getArticleContent(this.gettingToDL.model);
						this.downloading = this.gettingToDL.model.attributes.item_id;
					} else {
						this.debugOut("Planing for later. Current DL: " + this.downloading);
						this.needDL.push(this.gettingToDL);
					}
				} else {
					this.log("Article ok, don't download.");
					this.gettingToDL.model.set("contentAvailable",  true);
				}
				delete this.gettingToDL;
			}
		}
	},

	privateGenericSend: function (articleModel, method, inParams) {
		var params = inParams || {}, req;

		if (articleModel) {
			if (articleModel.get) {
				params.id = articleModel.get(articleModel.primaryKey);
			} else {
				params.id = articleModel.item_id;
			}
		}

		if (!params.activityId) {
			this.activityId += 1;
			params.activityId = this.activityId;
			this.setDbActivities(this.dbActivities + 1);
		}

		if (this.isWebos) {
			req = new enyo.ServiceRequest({
				service: "palm://info.mobo.moboreader.service",
				method: method
			});
			req.response(this.dbActivityComplete.bind(this, params.activityId, params.id, method));
			req.error(this.dbError.bind(this, params.activityId, params.id, method));

			req.go(params);
		} else {
			if (params.content) {
				this.storage[params.id] = params.content;
			}
			setTimeout(function (activityId) {
				this.dbActivityComplete(activityId, params.id, method, this, {
					success: method === "articleContentExists" ? !!(params.id && this.storage[params.id]) : true,
					id: params.id,
					content: params.id ? this.storage[params.id] : undefined,
					activityId: activityId
				});
			}.bind(this, this.activityId), 100);
		}
		return this.activityId;
	},
	getContent: function (articleModel) {
		return this.privateGenericSend(articleModel, "getArticleContent");
	},
	articleContentExists: function (articleModel) {
		return this.privateGenericSend(articleModel, "articleContentExists", {requireSpritz: moboreader.Prefs.downloadSpritzOnUpdate});
	},
	deleteContent: function (articleModel) {
		return this.privateGenericSend(articleModel, "deleteArticleContent");
	},
	wipe: function () {
		return this.privateGenericSend(null, "wipeStorage");
	},

	checkAndDownload: function (articleModel, api) {
		if (!this.gettingToDL) {
			this.debugOut("Checking: " + articleModel.attributes.item_id);
			var activityId = this.articleContentExists(articleModel);
			this.gettingToDL = {
				model: articleModel,
				api: api,
				activityId: activityId
			};
		} else {
			this.debugOut("Checking LATER: " + articleModel.attributes.item_id);
			this.checkQueue.unshift({
				model: articleModel,
				api: api
			});
		}
	},

	contentDownloaded: function (inSender, inEvent) {
		/*jslint unparam: true */
		this.debugOut("Download finished: " + inEvent.model.attributes.item_id);
		inEvent.model.set("contentAvailable", !!(inEvent.content && inEvent.content.web && (inEvent.content.spritz || !moboreader.Prefs.downloadSpritzOnUpdate)));
		this.storeArticle(inEvent.model, inEvent.content.web, inEvent.content.spritz, inEvent.content.images);

		if (this.needDL.length > 0) {
			var obj = this.needDL.shift();
			obj.api.getArticleContent(obj.model);
			this.downloading = obj.model.attributes.item_id;
			this.debugOut("Download started: " + this.downloading);
		} else {
			this.debugOut("all downloads finished.");
			this.downloading = false;
		}
	},

	//this will mix queue a bit up.. hm..
	sendNext: function () {
		var req = this.checkQueue.shift();
		if (req) {
			this.checkAndDownload(req.model, req.api);
		}
	},
	dbActivityComplete: function (activityId, id, method, inSender, inEvent) {
		/*jslint unparam: true */
		this.debugOut("Incomming response: " + inEvent.success + " for id " + activityId);
		this.setDbActivities(this.dbActivities - 1);
		inEvent.activityId = activityId;
		inEvent.id = id;
		inEvent.method = method;
		enyo.Signals.send("onArticleOpReturned", inEvent);
		this.sendNext();
	},
	dbError: function (activityId, id, method, inSender, inEvent) {
		/*jslint unparam: true */
		this.log("dbFailed: " + JSON.stringify(inEvent));
		this.setDbActivities(this.dbActivities - 1);
		enyo.Signals.send("onArticleOpReturned", {
			id: id,
			success: false,
			method: method,
			activityId: activityId
		});
		this.sendNext();
	}
});
<|endoftext|>


<commit_before>var isInScrollBar = function(canvas, context, x, y){
	var height = 400;
	var width = 30;
	var distanceFromTop = 100;

	return x > canvas.width - width && y > distanceFromTop && y < distanceFromTop + height;
}

var getScrollPositionFromScrollBar = function(canvas, context, y, maxScroll){
	var height = 400;
	var width = 20;
	var distanceFromTop = 100;

	return (y - distanceFromTop) * maxScroll / height;
}

var drawScrollBar = function(canvas, context, scrollPosition, maxScroll){
	var height = 400;
	var width = 30;
	var distanceFromTop = 100;

	context.beginPath();
	context.strokeStyle=staveColour;
	context.rect(canvas.width - width, distanceFromTop, width, height);
	context.stroke();

	context.beginPath();
	context.strokeStyle=noteColour;
	context.lineWidth =4;
	context.moveTo(canvas.width - width, scrollPosition * height / maxScroll + distanceFromTop);
	context.lineTo(canvas.width, scrollPosition * height / maxScroll + distanceFromTop);
	context.stroke();
	context.lineWidth = 1;
}

function getCurrentLine(y, lines, maxScroll){
	var height = 400;
	var distanceFromTop = 100;

	for(var i = 0; i < lines.length; i++){
		if(lines[i].y > (y - distanceFromTop) * maxScroll / height )
		return lines[i].barNumber;
	}
	return false;
}

var drawScrollPreview = function(canvas, context, y, lines, maxScroll){
	var rightMargin = 30;
	var width = 100;
	var height = 100;
	var farLeft = canvas.width - (rightMargin + width) - 15;

	context.beginPath();
	context.moveTo(farLeft, y);
	context.lineTo(farLeft + width + 10, y);
	context.lineTo(farLeft + width, y + 5)
	context.lineTo(farLeft + width, y + height);
	context.lineTo(farLeft, y + height);
	context.lineTo(farLeft, y);
	context.strokeStyle = "black";
	context.lineWidth = 4;
	context.stroke();
	context.fillStyle="white";
	context.fill();
	context.lineWidth = 1;

	var tempCurrentLine = getCurrentLine(y, lines, maxScroll);
	if(tempCurrentLine != false){
		context.font = "bold 12px Arial";
		context.fillStyle="black";
		context.fillText(tempCurrentLine + "", farLeft + 5, y + 15 );
	}

	 drawSingleBar(canvas, context, farLeft + 10, 30, tempCurrentLine.bars[0], width - 20, height - 60);
}
<commit_msg>Update scrollbar.js<commit_after>var isInScrollBar = function(canvas, context, x, y){
	var height = 400;
	var width = 30;
	var distanceFromTop = 100;

	return x > canvas.width - width && y > distanceFromTop && y < distanceFromTop + height;
}

var getScrollPositionFromScrollBar = function(canvas, context, y, maxScroll){
	var height = 400;
	var width = 20;
	var distanceFromTop = 100;

	return (y - distanceFromTop) * maxScroll / height;
}

var drawScrollBar = function(canvas, context, scrollPosition, maxScroll){
	var height = 400;
	var width = 30;
	var distanceFromTop = 100;

	context.beginPath();
	context.strokeStyle=staveColour;
	context.rect(canvas.width - width, distanceFromTop, width, height);
	context.stroke();

	context.beginPath();
	context.strokeStyle=noteColour;
	context.lineWidth =4;
	context.moveTo(canvas.width - width, scrollPosition * height / maxScroll + distanceFromTop);
	context.lineTo(canvas.width, scrollPosition * height / maxScroll + distanceFromTop);
	context.stroke();
	context.lineWidth = 1;
}

function getCurrentLine(y, lines, maxScroll){
	var height = 400;
	var distanceFromTop = 100;

	for(var i = 0; i < lines.length; i++){
		if(lines[i].y > (y - distanceFromTop) * maxScroll / height )
		return lines[i].barNumber;
	}
	return false;
}

var drawScrollPreview = function(canvas, context, y, lines, maxScroll){
	var rightMargin = 30;
	var width = 100;
	var height = 100;
	var farLeft = canvas.width - (rightMargin + width) - 15;

	context.beginPath();
	context.moveTo(farLeft, y);
	context.lineTo(farLeft + width + 10, y);
	context.lineTo(farLeft + width, y + 5)
	context.lineTo(farLeft + width, y + height);
	context.lineTo(farLeft, y + height);
	context.lineTo(farLeft, y);
	context.strokeStyle = "black";
	context.lineWidth = 4;
	context.stroke();
	context.fillStyle="white";
	context.fill();
	context.lineWidth = 1;

	var tempCurrentLine = getCurrentLine(y, lines, maxScroll);
	if(tempCurrentLine != false){
		context.font = "bold 12px Arial";
		context.fillStyle="black";
		context.fillText(tempCurrentLine + "", farLeft + 5, y + 15 );
	}

	 drawSingleBar(canvas, context, farLeft + 10, 30, tempCurrentLine.instruments[0].bars[0], width - 20, height - 60);
}
<|endoftext|>


<commit_before>var request = require('request');
var logger = require('./../../utils/logger');
var moment = require('moment');
var util = require('util');
var scheduleTo = require('../scheduleTo');
var handleUnexpected = require('../handleUnexpected');
var helpers = require('../../utils/helpers');

var API = 'https://graph.facebook.com';
var FIELDS = 'links.fields(id,caption,from,icon,message,name,link,created_time,picture),likes.fields(link,name,website,description,id,created_time,picture),name,username';

function connector(state, callback) {
	var accessToken = state.accessToken;
	var log = logger.connector('facebook');

	if (!accessToken) {
		return callback('missing accessToken for user: ' + state.user);
	}

	initState(state);

	log.info('prepearing request in (' + state.mode + ') mode for user: ' + state.user);

	var uri = formatRequestUri(accessToken, state);
	var headers = { 'Content-Type': 'application/json', 'User-Agent': 'likeastore/collector'};

	request({uri: uri, headers: headers, json: true}, function (err, response, body) {
		if (err) {
			return handleUnexpected(response, body, state, err, function (err) {
				callback (err, state);
			});
		}

		return handleResponse(response, body);
	});

	// TODO: move to common function, seems the same for all collectors?
	function initState(state) {
		if (!state.mode) {
			state.mode = 'initial';
		}

		if (!state.errors) {
			state.errors = 0;
		}

		if (state.mode === 'initial' && !state.page) {
			state.page = 0;
		}

		if (state.mode === 'rateLimit') {
			state.mode = state.prevMode;
		}
	}

	function formatRequestUri(accessToken, state) {
		var limit = 500;
		var base = util.format(
			'%s/me?fields=%s&access_token=%s&limit=%s', API, FIELDS, accessToken, limit);

		return state.mode === 'initial' || state.page ?
			util.format('%s&offset=%s', base, state.page * limit) :
			base;
	}

	function handleResponse(response, body) {
		var rateLimit = 100;
		log.info('rate limit remaining: ' +  rateLimit + ' for user: ' + state.user);

		var likesData = body.likes && body.likes.data;
		var linksData = body.links && body.links.data;
		var error = body.error;
		var authorName = body.username || body.name;

		if (!error && Array.isArray(likesData) && Array.isArray(linksData)) {
			var likes = likesData.map(function (r) {
				return {
					itemId: r.id.toString(),
					idInt: r.id,
					user: state.user,
					name: r.name,
					source: r.link,
					avatarUrl: r.picture.data.url,
					authorName: authorName,
					created: moment(r.created_time).toDate(),
					date: moment().toDate(),
					description: r.description || r.name,
					kind: 'like',
					type: 'facebook'
				};
			});

			var links = linksData.map(function (r) {
				return {
					itemId: r.id.toString(),
					idInt: r.id,
					user: state.user,
					name: r.from.name,
					source: r.link,
					avatarUrl: r.picture,
					authorName: authorName,
					created: moment(r.created_time).toDate(),
					date: moment().toDate(),
					description: r.message || r.name,
					kind: 'link',
					type: 'facebook'
				};
			});

			likes = likes.concat(links);

			log.info('retrieved ' + likes.length + ' likes and links for user: ' + state.user);

			return callback(null, scheduleTo(updateState(state, likes, rateLimit)), likes);
		}

		handleUnexpected(response, body, state, error, function (err) {
			callback(err, scheduleTo(updateState(state, [], rateLimit)));
		});
	}

	function updateState(state, data, rateLimit) {
		state.lastExecution = moment().toDate();

		if (state.mode === 'initial' && data.length > 0) {
			state.page += 1;
		}

		if (state.mode === 'initial' && data.length === 0) {
			state.mode = 'normal';
			delete state.page;
		}

		if (rateLimit <= 1) {
			var currentState = state.mode;
			state.mode = 'rateLimit';
			state.prevMode = currentState;
		}

		return state;
	}
}

module.exports = connector;<commit_msg>facebook fixes<commit_after>var request = require('request');
var logger = require('./../../utils/logger');
var moment = require('moment');
var util = require('util');
var scheduleTo = require('../scheduleTo');
var handleUnexpected = require('../handleUnexpected');
var helpers = require('../../utils/helpers');

var API = 'https://graph.facebook.com';
var FIELDS = 'links.limit(500).offset(%s).fields(id,caption,from,icon,message,name,link,created_time,picture),likes.limit(500).offset(%s).fields(link,name,website,description,id,created_time,picture),name,username';

function connector(state, callback) {
	var accessToken = state.accessToken;
	var log = logger.connector('facebook');

	if (!accessToken) {
		return callback('missing accessToken for user: ' + state.user);
	}

	initState(state);

	log.info('prepearing request in (' + state.mode + ') mode for user: ' + state.user);

	var uri = formatRequestUri(accessToken, state);
	var headers = { 'Content-Type': 'application/json', 'User-Agent': 'likeastore/collector'};

	console.log(uri);

	request({uri: uri, headers: headers, json: true}, function (err, response, body) {
		if (err) {
			return handleUnexpected(response, body, state, err, function (err) {
				callback (err, state);
			});
		}

		return handleResponse(response, body);
	});

	function initState(state) {
		if (!state.mode) {
			state.mode = 'initial';
		}

		if (state.mode === 'initial' && !state.page) {
			state.page = 0;
		}

		if (!state.errors) {
			state.errors = 0;
		}

		if (state.mode === 'rateLimit') {
			state.mode = state.prevMode;
		}
	}

	function formatRequestUri(accessToken, state) {
		var offset = state.mode === 'initial' ? state.page * 500 : 0;
		var fields = util.format(FIELDS, offset, offset);

		return util.format('%s/me?fields=%s&access_token=%s', API, fields, accessToken);
	}

	function formatDescription (message, name) {
		return message ? util.format('%s - %s', message, name) : name;
	}

	function handleResponse(response, body) {
		var likesData = body.likes && body.likes.data || [];
		var linksData = body.links && body.links.data || [];
		var error = body.error;
		var authorName = body.username || body.name;

		if (!error) {
			var likes = likesData.map(function (r) {
				return {
					itemId: r.id.toString(),
					idInt: r.id,
					user: state.user,
					name: r.name,
					source: r.link,
					avatarUrl: r.picture.data.url || 'https://www.gravatar.com/avatar?d=mm',
					authorName: authorName,
					created: moment(r.created_time).toDate(),
					date: moment().toDate(),
					description: formatDescription(r.description, r.name),
					kind: 'like',
					type: 'facebook'
				};
			});

			var links = linksData.map(function (r) {
				return {
					itemId: r.id.toString(),
					idInt: r.id,
					user: state.user,
					name: r.from.name,
					source: r.link,
					avatarUrl: r.picture || 'https://www.gravatar.com/avatar?d=mm',
					authorName: authorName,
					created: moment(r.created_time).toDate(),
					date: moment().toDate(),
					description: formatDescription(r.message, r.name),
					kind: 'link',
					type: 'facebook'
				};
			});

			likes = likes.concat(links);

			log.info('retrieved ' + likes.length + ' likes and links for user: ' + state.user);

			return callback(null, scheduleTo(updateState(state, likes, 9999)), likes);
		}

		handleUnexpected(response, body, state, error, function (err) {
			callback(err, state);
		});
	}

	function updateState(state, data, rateLimit) {
		state.lastExecution = moment().toDate();

		if (state.mode === 'initial' && data.length > 0) {
			state.page += 1;
		}

		if (state.mode === 'initial' && data.length === 0) {
			state.mode = 'normal';
			delete state.page;
		}

		if (rateLimit <= 1) {
			var currentState = state.mode;
			state.mode = 'rateLimit';
			state.prevMode = currentState;
		}

		return state;
	}
}

module.exports = connector;<|endoftext|>


<commit_before>'use strict';
// jshint node: true
/* global describe, beforeEach, afterEach, it */

var util = require('./util')();

var chartName = 'helloworld.scxml',
  instanceId = chartName + '/test';

describe('SCXMLD', function () {
  beforeEach(function (done) { util.beforeEach(done); });
  afterEach(function (done) {
    util.deleteStatechart(chartName, function(){
      util.afterEach(done);
    });
  });

  it('should save helloworld.scxml', function (done) {
    util.saveStatechart(chartName, util.statechart, done);
  });

  it('should run helloworld.scxml', function (done) {
    util.saveStatechart(chartName, util.statechart, function () {
      util.runInstance(instanceId, ['a'], done);
    });
  });

  it('should send event "t"', function (done) {
    util.saveStatechart(chartName, util.statechart, function () {
      util.runInstance(instanceId, ['a'], function () {
        util.send(instanceId, { name: 't' }, ['b'], done);
      });
    });
  });

  it('should subscribe to changes and send event "t"', function (done) {
    util.saveStatechart(chartName, util.statechart, function () {
      util.runInstance(instanceId, ['a'], function () {
        util.subscribeInstance(instanceId, function (stopListening) {
          util.send(instanceId, { name: 't' }, ['b'], function () {
            var results = [ { type: 'onExit', data: 'a' },
                            { type: 'onEntry', data: 'b' }];

            stopListening(results, done);
          });
        });
      });
    });
  });

  it('should end up at "b" state', function (done) {
    util.saveStatechart(chartName, util.statechart, function () {
      util.runInstance(instanceId, ['a'], function () {
        util.subscribeInstanceUntilState(instanceId, 'b', 'c', done, function () {
          util.send(instanceId, { name: 't' }, ['b']);
        });
      });
    });
  });
});
<commit_msg>Logs for harnessSpec<commit_after>'use strict';
// jshint node: true
/* global describe, beforeEach, afterEach, it */

var util = require('./util')();

var chartName = 'helloworld.scxml',
  instanceId = chartName + '/test';

describe('SCXMLD', function () {
  beforeEach(function (done) { util.beforeEach(done); });
  afterEach(function (done) {
    util.deleteStatechart(chartName, function(){
      util.afterEach(done);
    });
  });

  it('should save helloworld.scxml', function (done) {
    console.log('\n\u001b[34mshould save helloworld.scxml\u001b[0m');

    util.saveStatechart(chartName, util.statechart, done);
  });

  it('should run helloworld.scxml', function (done) {
    console.log('\n\u001b[34mshould run helloworld.scxml\u001b[0m');

    util.saveStatechart(chartName, util.statechart, function () {
      util.runInstance(instanceId, ['a'], done);
    });
  });

  it('should send event "t"', function (done) {
    console.log('\n\u001b[34mshould send event "t"\u001b[0m');

    util.saveStatechart(chartName, util.statechart, function () {
      util.runInstance(instanceId, ['a'], function () {
        util.send(instanceId, { name: 't' }, ['b'], done);
      });
    });
  });

  it('should subscribe to changes and send event "t"', function (done) {
    console.log('\n\u001b[34mshould subscribe to changes and send event "t"\u001b[0m');

    util.saveStatechart(chartName, util.statechart, function () {
      util.runInstance(instanceId, ['a'], function () {
        util.subscribeInstance(instanceId, function (stopListening) {
          util.send(instanceId, { name: 't' }, ['b'], function () {
            var results = [ { type: 'onExit', data: 'a' },
                            { type: 'onEntry', data: 'b' }];

            stopListening(results, done);
          });
        });
      });
    });
  });

  it('should end up at "b" state', function (done) {
    console.log('\n\u001b[34mshould end up at "b" state\u001b[0m');

    util.saveStatechart(chartName, util.statechart, function () {
      util.runInstance(instanceId, ['a'], function () {
        util.subscribeInstanceUntilState(instanceId, 'b', 'c', done, function () {
          util.send(instanceId, { name: 't' }, ['b']);
        });
      });
    });
  });
});
<|endoftext|>