<commit_before>// Adapted from https://github.com/asengupta/requirejs-q
function requireQ(modules) {
  var deferred = Q.defer();
  require(modules, function () {
    deferred.resolve(arguments);
  });
  return deferred.promise;
}

var duckCtor = function (_, angular, Q, $) {
  var hackDependencies = {};
  var originalControllerProvider;

  var Container = function Container(injector, app, featureOptions) {
    featureOptions = featureOptions || {}
    if (featureOptions.baseUrl && featureOptions.textPluginPath) {
      require.config({
        baseUrl: featureOptions.baseUrl,
        paths: { text: featureOptions.textPluginPath}
      });
    }

    var self = this;
    self.options = {};
    self.injector = injector;
    self.controllerProvider = self.injector.get("$controller");
    self.rootScope = self.injector.get("$rootScope");
    self.compileService = self.injector.get("$compile");
    self.viewProcessors = [];

    this.addViewProcessor = function(viewProcessor) {
      self.viewProcessors.push(viewProcessor);
    };

    this.addViewProcessors = function(viewProcessors) {
      _.each(viewProcessors, function(viewProcessor) {
        self.viewProcessors.push(viewProcessor);
      });
    };

    this.newScope = function () {
      return self.rootScope.$new();
    };

    this.createElement = function (viewHTML) {
      var wrappingElement = angular.element("

");
      wrappingElement.append(viewHTML);
      return wrappingElement;
    };

    this.removeElementsBelongingToDifferentScope = function (element) {
      element.find("[modal]").removeAttr("modal");
      element.find("[options]").removeAttr("options");
      if (!multipleControllersFeature(featureOptions))
        element.find("[ng-controller]").remove();

      return element;
    };

    this.get = function (dependencyName) {
      return injector.get(dependencyName);
    };

    this.numPartials = function num(element) {
      var includes = element.find("[ng-include]");
      if (includes.length === 0) {
        return Q.fcall(function () {
          return 1;
        });
      }

      var includedTemplateName = function(elementWithNgInclude) {
        var e = angular.element(elementWithNgInclude);
        if (e.attr("src"))
          return e.attr("src").replace("'", "").replace("'", "");
        return e.attr("ng-include").replace("'", "").replace("'", "");
      };

      var promises = _.map(includes, function (include) {
        var includeSource = includedTemplateName(include);
        var includePromise = requireQ(["text!" + includeSource]);
        return includePromise.spread(function (sourceText) {
          var child = self.removeElementsBelongingToDifferentScope(self.createElement(sourceText));
          return num(child);
        });
      });
      return Q.all(promises).then(function (counts) {
        return 1 + _.reduce(counts, function (sum, count) {
          return sum + count;
        }, 0);
      });
    };

    this.compileTemplate = function (viewHTML, scope, preRenderBlock) {
      var wrappingElement = self.removeElementsBelongingToDifferentScope(self.createElement(viewHTML));
      if (preRenderBlock) {
        preRenderBlock(self.injector, scope);
      }
      self.allPartialsLoadedDeferred = Q.defer();
      var c = self.numPartials(wrappingElement);
      return c.then(function (numberOfPartials) {
        self.numberOfPartials = numberOfPartials - 1;
        if (self.options.dontWait || !self.numberOfPartials || self.numberOfPartials === 0) {
          self.allPartialsLoadedDeferred.resolve();
        }
        var counter = 0;
        scope.$on("$includeContentLoaded", function () {
          counter++;
          if (counter === self.numberOfPartials) {
            self.allPartialsLoadedDeferred.resolve();
          }
        });
      }).then(function () {
        var compiledTemplate = self.compileService(wrappingElement)(scope);
        applySafely(scope);
        return compiledTemplate;
      });
    };

    var applySafely = function (scope) {
      if (!scope.$$phase) {
        scope.$apply();
      }
    };

    var processView = function(viewHTML) {
      _.each(self.viewProcessors, function(viewProcessor) {
        viewHTML = viewProcessor(viewHTML);
      });
      return viewHTML;
    };

    this.view = function (viewUrl, scope, preRenderBlock) {
      var deferred = Q.defer();
      require(["text!" + viewUrl], function (viewHTML) {
        // HACK to make sure that ng-controller directives don't cause template to be eaten up
        if (!multipleControllersFeature(featureOptions))
          viewHTML = viewHTML.replace("ng-controller", "no-controller");
        viewHTML = processView(viewHTML);
        viewHTML = viewHTML.replace("ng-app", "no-app");
        self.compileTemplate(viewHTML, scope, preRenderBlock).then(function (compiledTemplate) {
          deferred.resolve(compiledTemplate);
        });
      }, function (err) {
        console.log("Bad things happened");
        console.log(err);
      });
      return deferred.promise;
    };

    this.controller = function (controllerName, dependencies, isAsync, controllerLoadedPromise) {
      var controller;
      dependencies = dependencies || {};
      if (multipleControllersFeature(featureOptions)) {
        hackDependencies = dependencies;
        hackDependencies.rootControllerName = controllerName;
        controller = self.controllerProvider(controllerName, { $scope: dependencies.$scope });
      } else {
        controller = self.controllerProvider(controllerName, dependencies);
      }
      if (!isAsync) {
        return Q({});
      }
      var deferred = Q.defer();
      controllerLoadedPromise =
      controllerLoadedPromise ? controllerLoadedPromise(controller) : controller.loaded;
      controllerLoadedPromise.then(function () {
        deferred.resolve(controller);
      });
      return deferred.promise;
    };

    this.directiveTemplate = function (element) {
      var deferred = Q.defer();
      var scope = self.newScope();
      self.compileTemplate(element, scope).then(function (template) {
        deferred.resolve([scope, template]);
      });
      return deferred.promise;
    };

    this.domMvc = function (controllerName, viewUrl, dependencies, options) {
      dependencies = dependencies || {};
      return self.mvc(controllerName, viewUrl, dependencies,
          options).then(function (scopeViewController) {
            var dom = new DuckDOM(scopeViewController.view, scopeViewController.scope);
            return [dom, scopeViewController];
          });
    };

    this.mvc = function (controllerName, viewUrl, dependencies, options) {
      self.options = options || {dontWait: false, async: false, controllerLoadedPromise: null};
      self.options.preBindHook = self.options.preBindHook || function () {};
      self.options.preRenderHook = self.options.preRenderHook || function () {};
      dependencies = dependencies || {};
      var scope = self.newScope();
      self.options.preBindHook(scope);
      dependencies.$scope = _.extend(scope, dependencies.$scope || {});
      var controller = this.controller(controllerName, dependencies, self.options.async || false,
          self.options.controllerLoadedPromise);
      var template = this.view(viewUrl, scope, self.options.preRenderHook);
      return Q.spread([controller, template], function (controller, template) {
        return self.allPartialsLoadedDeferred.promise.then(function () {
          return { controller: controller, view: template, scope: scope };
        });
      });
    };
  };

  var multipleControllersFeature = function(featureOptions) {
    return featureOptions && featureOptions.multipleControllers;
  };

  var ContainerBuilder = {
    dependencies: {},
    originalDependenciesCache: {},
    originalProvide: null,
    getQ: function (url) {
      var defer = Q.defer();
      var req = new XMLHttpRequest();
      req.open("GET", url, true);
      req.onload = function (e) {
        var result = req.responseText;
        defer.resolve(result);
      };
      req.onerror = function (e) {
        console.error("Putting failed", e);
        defer.reject(e);
      };
      req.send();
      return defer.promise;
    },

    cacheTemplate: function (app, templateUrl, realTemplateUrl) {
      var self = this;
      return self.getQ(realTemplateUrl).then(function (templateText) {
        app.run(function ($templateCache) {
          $templateCache.put(templateUrl, templateText);
        });
        return self;
      });
    },

    cacheTemplates: function(app, templateMap) {
      if (_.isEmpty(templateMap)) return Q(this);
      var self = this;
      return Q.all(_.map(_.pairs(templateMap), function(templateKeyPair) {
        return self.cacheTemplate(app, templateKeyPair[0], templateKeyPair[1]);
      })).spread(function(bldr) {
        return bldr;
      });
    },

    withDependencies: function (appLevelDependencies) {
      this.dependencies = appLevelDependencies;
      return this;
    },

    build: function (moduleName, app, featureOptions) {
      var self = this;

      var mockModule = angular.module("lool", [moduleName, "ng"]);
      mockModule.config(function($provide) {
        $provide.provider("$rootElement", function () {
          this.$get = function () {
            return $("#Moaha");
          };
        });

        if (multipleControllersFeature(featureOptions)) {
          $provide.decorator("$controller", function($delegate) {
            return function(ctrlName, deps) {
              if (ctrlName === hackDependencies.rootControllerName) {
                if (hackDependencies[ctrlName]) return $delegate(ctrlName, _.extend({}, deps, hackDependencies[ctrlName], {$scope: _.extend(deps.$scope, hackDependencies.$scope)}));
                return $delegate(ctrlName, {$scope: _.extend(deps.$scope, hackDependencies.$scope)})
              }
              if (hackDependencies[ctrlName]) return $delegate(ctrlName, _.extend({}, deps, hackDependencies[ctrlName], {$scope: _.extend(deps.$scope, hackDependencies[ctrlName].$scope)}));
              return $delegate(ctrlName, deps);
            };
          });
        }

        _.each(_.keys(self.dependencies), function (appDependencyKey) {
          if (typeof self.dependencies[appDependencyKey] === "function") {
            var v = self.dependencies[appDependencyKey]($provide, mockModule);
          } else {
            $provide.provider(appDependencyKey, function () {
              this.$get = function () {
                return self.dependencies[appDependencyKey];
              };
            });
          }
        });
      });

      var injector = angular.bootstrap($("#null" + new Date().getMilliseconds()), ["lool"]);
      return new Container(injector, mockModule, featureOptions);
    }
  };

  var DuckUIInteraction = function DuckUIInteraction(duckDom) {
    var self = this;
    this.with = function (selector, value) {
      self.interaction = function () {
        duckDom.interactWith(selector, value);
      };
      return self;
    };

    this.run = function () {
      self.interaction();
      return self;
    };

    this.waitFor = function (o, fn) {
      var deferred = Q.defer();
      var originalFn = o[fn];
      o[fn] = function () {
        var originalPromise = originalFn.apply(o, arguments);

        function resolveOriginalFunction() {
          duckDom.apply();
          o[fn] = originalFn;
          deferred.resolve();
        }

        if (originalPromise && originalPromise.then) {
          originalPromise.then(function (result) {
            resolveOriginalFunction();
            return result;
          }, function (errors) {
            duckDom.apply();
            o[fn] = originalFn;
            deferred.reject(errors);
          });
        } else {
          resolveOriginalFunction();
        }
      };
      self.run();
      return deferred.promise;
    };

    this.waitForSync = function (o, fn) {
      var deferred = Q.defer();
      var originalFn = o[fn];
      o[fn] = function () {
        var result = originalFn.apply(o, arguments);
        duckDom.apply();
        deferred.resolve();
        return result;
      };
      self.run();
      return deferred.promise;
    };
  };

  var DuckDOM = function DuckDOM(view, scope) {
    var self = this;
    var applySafely = function () {
      if (!scope.$$phase) {
        try {
          scope.$apply();
        } catch (e) {
          console.log("Apply failed");
          console.log(e);
        }
      }
    };

    this.emit = function(ev, args) {
      scope.$emit(ev, args);
      applySafely();
    };

    this.applyAndDo = function (command) {
      var deferred = Q.defer();
      scope.$apply(function () {
        command();
        deferred.resolve();
      });
      return deferred.promise;
    };

    this.trigger = function(selector, event) {
      var elements = angular.element(selector, view);
      elements.trigger(event);
    };

    this.on = function(selector, ev) {
      var defer = Q.defer();
      self.element(selector).on(ev, function() {
        defer.resolve();
      });
      return defer.promise;
    };

    this.interactWith = function (selector, value, promise) {
      var elements = angular.element(selector, view);

      _.each(elements, function (element) {
        if (element.nodeName === "TEXTAREA" || (element.nodeName === "INPUT" &&
                                                (element.type === "text" ||
                                                 element.type === "password" ||
                                                 element.type === "number" ||
                                                 element.type === "tel" ||
                                                 element.type === "email" ||
                                                 element.type === "date" ))) {
          elements.focus();
          elements.val(value).trigger("input");
        }
        else if (element.nodeName === "FORM") {
          var inputElement = angular.element("input[type='submit']");
          inputElement.submit();
        }
        else if (element.nodeName === "INPUT" && element.type === "button") {
          elements.trigger("click");
        }
        else if (element.nodeName === "INPUT" && element.type === "submit") {
          if (elements.submit) elements.submit();
          elements.trigger("click");
        }
        else if (element.nodeName === "INPUT" && element.type === "checkbox" && value == null) {
          elements.click().trigger("click");
          elements.prop("checked", !elements.prop("checked"));
        }
        else if (element.nodeName === "INPUT" && element.type === "radio") {
          elements.attr("checked", elements.attr("checked") ? null : "checked").click();
        }
        else if (element.nodeName === "INPUT" && element.type === "checkbox" && value != null) {
          while (elements.prop("checked") != value) {
            elements.click().trigger("click");
            elements.prop("checked", !elements.prop("checked"));
          }
        }
        else if (element.nodeName === "SELECT") {
          elements.prop("selectedIndex", value);
          elements.trigger("change");
        }
        else if (element.nodeName === "A" || element.nodeName === "BUTTON") {
          elements.click();
        }
      });
      applySafely();
      if (promise) {
        return promise;
      }
    };

    this.apply = function () {
      applySafely();
    };

    var duckElement = {
      isVisible: function () {
        return !this.hasClass("ng-hide");
      },

      isHidden: function () {
        return this.hasClass("ng-hide");
      },
      isFocused: function () {
        var deferred = Q.defer();
        this.on("focus", function () {
          deferred.resolve();
        });
        return deferred.promise;
      },
      isDisabled: function() {
        return this.attr("disabled") === "disabled" || this.attr("disabled") === "true";
      },
      isEnabled: function() {
        return !this.isDisabled();
      }
    };

    this.element = function (selector) {
      var element = angular.element(selector, view);
      return  _.extend(element, duckElement);
    };
  };
  return { Container: Container, UIInteraction: DuckUIInteraction, DOM: DuckDOM, ContainerBuilder: ContainerBuilder };
};

if (typeof define !== "undefined") {
  console.log("RequireJS is present, defining AMD module");
  define(["underscore", "angular", "Q", "jquery"], duckCtor);
}
else {
  console.log("RequireJS is NOT present, defining globally");
  window.duckCtor = duckCtor;
}
<commit_msg>Re-adding Michael/Emily's fix for isVisible - <Mojo><commit_after>// Adapted from https://github.com/asengupta/requirejs-q
function requireQ(modules) {
  var deferred = Q.defer();
  require(modules, function () {
    deferred.resolve(arguments);
  });
  return deferred.promise;
}

var duckCtor = function (_, angular, Q, $) {
  var hackDependencies = {};
  var originalControllerProvider;

  var Container = function Container(injector, app, featureOptions) {
    featureOptions = featureOptions || {}
    if (featureOptions.baseUrl && featureOptions.textPluginPath) {
      require.config({
        baseUrl: featureOptions.baseUrl,
        paths: { text: featureOptions.textPluginPath}
      });
    }

    var self = this;
    self.options = {};
    self.injector = injector;
    self.controllerProvider = self.injector.get("$controller");
    self.rootScope = self.injector.get("$rootScope");
    self.compileService = self.injector.get("$compile");
    self.viewProcessors = [];

    this.addViewProcessor = function(viewProcessor) {
      self.viewProcessors.push(viewProcessor);
    };

    this.addViewProcessors = function(viewProcessors) {
      _.each(viewProcessors, function(viewProcessor) {
        self.viewProcessors.push(viewProcessor);
      });
    };

    this.newScope = function () {
      return self.rootScope.$new();
    };

    this.createElement = function (viewHTML) {
      var wrappingElement = angular.element("

");
      wrappingElement.append(viewHTML);
      return wrappingElement;
    };

    this.removeElementsBelongingToDifferentScope = function (element) {
      element.find("[modal]").removeAttr("modal");
      element.find("[options]").removeAttr("options");
      if (!multipleControllersFeature(featureOptions))
        element.find("[ng-controller]").remove();

      return element;
    };

    this.get = function (dependencyName) {
      return injector.get(dependencyName);
    };

    this.numPartials = function num(element) {
      var includes = element.find("[ng-include]");
      if (includes.length === 0) {
        return Q.fcall(function () {
          return 1;
        });
      }

      var includedTemplateName = function(elementWithNgInclude) {
        var e = angular.element(elementWithNgInclude);
        if (e.attr("src"))
          return e.attr("src").replace("'", "").replace("'", "");
        return e.attr("ng-include").replace("'", "").replace("'", "");
      };

      var promises = _.map(includes, function (include) {
        var includeSource = includedTemplateName(include);
        var includePromise = requireQ(["text!" + includeSource]);
        return includePromise.spread(function (sourceText) {
          var child = self.removeElementsBelongingToDifferentScope(self.createElement(sourceText));
          return num(child);
        });
      });
      return Q.all(promises).then(function (counts) {
        return 1 + _.reduce(counts, function (sum, count) {
          return sum + count;
        }, 0);
      });
    };

    this.compileTemplate = function (viewHTML, scope, preRenderBlock) {
      var wrappingElement = self.removeElementsBelongingToDifferentScope(self.createElement(viewHTML));
      if (preRenderBlock) {
        preRenderBlock(self.injector, scope);
      }
      self.allPartialsLoadedDeferred = Q.defer();
      var c = self.numPartials(wrappingElement);
      return c.then(function (numberOfPartials) {
        self.numberOfPartials = numberOfPartials - 1;
        if (self.options.dontWait || !self.numberOfPartials || self.numberOfPartials === 0) {
          self.allPartialsLoadedDeferred.resolve();
        }
        var counter = 0;
        scope.$on("$includeContentLoaded", function () {
          counter++;
          if (counter === self.numberOfPartials) {
            self.allPartialsLoadedDeferred.resolve();
          }
        });
      }).then(function () {
        var compiledTemplate = self.compileService(wrappingElement)(scope);
        applySafely(scope);
        return compiledTemplate;
      });
    };

    var applySafely = function (scope) {
      if (!scope.$$phase) {
        scope.$apply();
      }
    };

    var processView = function(viewHTML) {
      _.each(self.viewProcessors, function(viewProcessor) {
        viewHTML = viewProcessor(viewHTML);
      });
      return viewHTML;
    };

    this.view = function (viewUrl, scope, preRenderBlock) {
      var deferred = Q.defer();
      require(["text!" + viewUrl], function (viewHTML) {
        // HACK to make sure that ng-controller directives don't cause template to be eaten up
        if (!multipleControllersFeature(featureOptions))
          viewHTML = viewHTML.replace("ng-controller", "no-controller");
        viewHTML = processView(viewHTML);
        viewHTML = viewHTML.replace("ng-app", "no-app");
        self.compileTemplate(viewHTML, scope, preRenderBlock).then(function (compiledTemplate) {
          deferred.resolve(compiledTemplate);
        });
      }, function (err) {
        console.log("Bad things happened");
        console.log(err);
      });
      return deferred.promise;
    };

    this.controller = function (controllerName, dependencies, isAsync, controllerLoadedPromise) {
      var controller;
      dependencies = dependencies || {};
      if (multipleControllersFeature(featureOptions)) {
        hackDependencies = dependencies;
        hackDependencies.rootControllerName = controllerName;
        controller = self.controllerProvider(controllerName, { $scope: dependencies.$scope });
      } else {
        controller = self.controllerProvider(controllerName, dependencies);
      }
      if (!isAsync) {
        return Q({});
      }
      var deferred = Q.defer();
      controllerLoadedPromise =
      controllerLoadedPromise ? controllerLoadedPromise(controller) : controller.loaded;
      controllerLoadedPromise.then(function () {
        deferred.resolve(controller);
      });
      return deferred.promise;
    };

    this.directiveTemplate = function (element) {
      var deferred = Q.defer();
      var scope = self.newScope();
      self.compileTemplate(element, scope).then(function (template) {
        deferred.resolve([scope, template]);
      });
      return deferred.promise;
    };

    this.domMvc = function (controllerName, viewUrl, dependencies, options) {
      dependencies = dependencies || {};
      return self.mvc(controllerName, viewUrl, dependencies,
          options).then(function (scopeViewController) {
            var dom = new DuckDOM(scopeViewController.view, scopeViewController.scope);
            return [dom, scopeViewController];
          });
    };

    this.mvc = function (controllerName, viewUrl, dependencies, options) {
      self.options = options || {dontWait: false, async: false, controllerLoadedPromise: null};
      self.options.preBindHook = self.options.preBindHook || function () {};
      self.options.preRenderHook = self.options.preRenderHook || function () {};
      dependencies = dependencies || {};
      var scope = self.newScope();
      self.options.preBindHook(scope);
      dependencies.$scope = _.extend(scope, dependencies.$scope || {});
      var controller = this.controller(controllerName, dependencies, self.options.async || false,
          self.options.controllerLoadedPromise);
      var template = this.view(viewUrl, scope, self.options.preRenderHook);
      return Q.spread([controller, template], function (controller, template) {
        return self.allPartialsLoadedDeferred.promise.then(function () {
          return { controller: controller, view: template, scope: scope };
        });
      });
    };
  };

  var multipleControllersFeature = function(featureOptions) {
    return featureOptions && featureOptions.multipleControllers;
  };

  var ContainerBuilder = {
    dependencies: {},
    originalDependenciesCache: {},
    originalProvide: null,
    getQ: function (url) {
      var defer = Q.defer();
      var req = new XMLHttpRequest();
      req.open("GET", url, true);
      req.onload = function (e) {
        var result = req.responseText;
        defer.resolve(result);
      };
      req.onerror = function (e) {
        console.error("Putting failed", e);
        defer.reject(e);
      };
      req.send();
      return defer.promise;
    },

    cacheTemplate: function (app, templateUrl, realTemplateUrl) {
      var self = this;
      return self.getQ(realTemplateUrl).then(function (templateText) {
        app.run(function ($templateCache) {
          $templateCache.put(templateUrl, templateText);
        });
        return self;
      });
    },

    cacheTemplates: function(app, templateMap) {
      if (_.isEmpty(templateMap)) return Q(this);
      var self = this;
      return Q.all(_.map(_.pairs(templateMap), function(templateKeyPair) {
        return self.cacheTemplate(app, templateKeyPair[0], templateKeyPair[1]);
      })).spread(function(bldr) {
        return bldr;
      });
    },

    withDependencies: function (appLevelDependencies) {
      this.dependencies = appLevelDependencies;
      return this;
    },

    build: function (moduleName, app, featureOptions) {
      var self = this;

      var mockModule = angular.module("lool", [moduleName, "ng"]);
      mockModule.config(function($provide) {
        $provide.provider("$rootElement", function () {
          this.$get = function () {
            return $("#Moaha");
          };
        });

        if (multipleControllersFeature(featureOptions)) {
          $provide.decorator("$controller", function($delegate) {
            return function(ctrlName, deps) {
              if (ctrlName === hackDependencies.rootControllerName) {
                if (hackDependencies[ctrlName]) return $delegate(ctrlName, _.extend({}, deps, hackDependencies[ctrlName], {$scope: _.extend(deps.$scope, hackDependencies.$scope)}));
                return $delegate(ctrlName, {$scope: _.extend(deps.$scope, hackDependencies.$scope)})
              }
              if (hackDependencies[ctrlName]) return $delegate(ctrlName, _.extend({}, deps, hackDependencies[ctrlName], {$scope: _.extend(deps.$scope, hackDependencies[ctrlName].$scope)}));
              return $delegate(ctrlName, deps);
            };
          });
        }

        _.each(_.keys(self.dependencies), function (appDependencyKey) {
          if (typeof self.dependencies[appDependencyKey] === "function") {
            var v = self.dependencies[appDependencyKey]($provide, mockModule);
          } else {
            $provide.provider(appDependencyKey, function () {
              this.$get = function () {
                return self.dependencies[appDependencyKey];
              };
            });
          }
        });
      });

      var injector = angular.bootstrap($("#null" + new Date().getMilliseconds()), ["lool"]);
      return new Container(injector, mockModule, featureOptions);
    }
  };

  var DuckUIInteraction = function DuckUIInteraction(duckDom) {
    var self = this;
    this.with = function (selector, value) {
      self.interaction = function () {
        duckDom.interactWith(selector, value);
      };
      return self;
    };

    this.run = function () {
      self.interaction();
      return self;
    };

    this.waitFor = function (o, fn) {
      var deferred = Q.defer();
      var originalFn = o[fn];
      o[fn] = function () {
        var originalPromise = originalFn.apply(o, arguments);

        function resolveOriginalFunction() {
          duckDom.apply();
          o[fn] = originalFn;
          deferred.resolve();
        }

        if (originalPromise && originalPromise.then) {
          originalPromise.then(function (result) {
            resolveOriginalFunction();
            return result;
          }, function (errors) {
            duckDom.apply();
            o[fn] = originalFn;
            deferred.reject(errors);
          });
        } else {
          resolveOriginalFunction();
        }
      };
      self.run();
      return deferred.promise;
    };

    this.waitForSync = function (o, fn) {
      var deferred = Q.defer();
      var originalFn = o[fn];
      o[fn] = function () {
        var result = originalFn.apply(o, arguments);
        duckDom.apply();
        deferred.resolve();
        return result;
      };
      self.run();
      return deferred.promise;
    };
  };

  var DuckDOM = function DuckDOM(view, scope) {
    var self = this;
    var applySafely = function () {
      if (!scope.$$phase) {
        try {
          scope.$apply();
        } catch (e) {
          console.log("Apply failed");
          console.log(e);
        }
      }
    };

    this.emit = function(ev, args) {
      scope.$emit(ev, args);
      applySafely();
    };

    this.applyAndDo = function (command) {
      var deferred = Q.defer();
      scope.$apply(function () {
        command();
        deferred.resolve();
      });
      return deferred.promise;
    };

    this.trigger = function(selector, event) {
      var elements = angular.element(selector, view);
      elements.trigger(event);
    };

    this.on = function(selector, ev) {
      var defer = Q.defer();
      self.element(selector).on(ev, function() {
        defer.resolve();
      });
      return defer.promise;
    };

    this.interactWith = function (selector, value, promise) {
      var elements = angular.element(selector, view);

      _.each(elements, function (element) {
        if (element.nodeName === "TEXTAREA" || (element.nodeName === "INPUT" &&
                                                (element.type === "text" ||
                                                 element.type === "password" ||
                                                 element.type === "number" ||
                                                 element.type === "tel" ||
                                                 element.type === "email" ||
                                                 element.type === "date" ))) {
          elements.focus();
          elements.val(value).trigger("input");
        }
        else if (element.nodeName === "FORM") {
          var inputElement = angular.element("input[type='submit']");
          inputElement.submit();
        }
        else if (element.nodeName === "INPUT" && element.type === "button") {
          elements.trigger("click");
        }
        else if (element.nodeName === "INPUT" && element.type === "submit") {
          if (elements.submit) elements.submit();
          elements.trigger("click");
        }
        else if (element.nodeName === "INPUT" && element.type === "checkbox" && value == null) {
          elements.click().trigger("click");
          elements.prop("checked", !elements.prop("checked"));
        }
        else if (element.nodeName === "INPUT" && element.type === "radio") {
          elements.attr("checked", elements.attr("checked") ? null : "checked").click();
        }
        else if (element.nodeName === "INPUT" && element.type === "checkbox" && value != null) {
          while (elements.prop("checked") != value) {
            elements.click().trigger("click");
            elements.prop("checked", !elements.prop("checked"));
          }
        }
        else if (element.nodeName === "SELECT") {
          elements.prop("selectedIndex", value);
          elements.trigger("change");
        }
        else if (element.nodeName === "A" || element.nodeName === "BUTTON") {
          elements.click();
        }
      });
      applySafely();
      if (promise) {
        return promise;
      }
    };

    this.apply = function () {
      applySafely();
    };

    var duckElement = {
      isVisible: function () {
        if(this.size() <=0){
          throw(new Error("Element does not exist"));
        }
        return !this.hasClass("ng-hide");
      },

      isHidden: function () {
        return this.hasClass("ng-hide");
      },
      isFocused: function () {
        var deferred = Q.defer();
        this.on("focus", function () {
          deferred.resolve();
        });
        return deferred.promise;
      },
      isDisabled: function() {
        return this.attr("disabled") === "disabled" || this.attr("disabled") === "true";
      },
      isEnabled: function() {
        return !this.isDisabled();
      }
    };

    this.element = function (selector) {
      var element = angular.element(selector, view);
      return  _.extend(element, duckElement);
    };
  };
  return { Container: Container, UIInteraction: DuckUIInteraction, DOM: DuckDOM, ContainerBuilder: ContainerBuilder };
};

if (typeof define !== "undefined") {
  console.log("RequireJS is present, defining AMD module");
  define(["underscore", "angular", "Q", "jquery"], duckCtor);
}
else {
  console.log("RequireJS is NOT present, defining globally");
  window.duckCtor = duckCtor;
}
<|endoftext|>


<commit_before>angular
  .module('app')
  .factory('Validator',
  function($location, $anchorScroll) {

    var isEmpty = function(x) {
      for (var y in x) {
        return false;
      }
      return true;
    };

    var Validator = {

      validationMessages: {
        numericality: "Must be a number.",
        required: "This field is required.",
        zip: "A five or nine digit zip code is required.",
        email: "Please enter a valid email address.",
        mask: "Please enter a valid phone number",
        phone: "Please enter a valid phone number."
      },

      setDirty: function(form) {
        for (var i in form) {
          var input = form[i];
          if (input.$pristine) {
            input.$pristine = false;
            input.$dirty = true;
          }
        }
      },

      setDirtyField: function(input) {
        if (input.$pristine) {
          input.$pristine = false;
          input.$dirty = true;
        }
      },

      // The same issue exists with form.setPristine().
      setPristine: function(form) {
        for (var i in form) {
          var input = form[i];
          if (input.$dirty) {
            input.$pristine = true;
            input.$dirty = false;
          }
        }
      },

      validateCash: function(number, field) {
        if (typeof number === 'string') {
            if (!isNaN(Number(number)) && number.length > 0) {
                return true;
            } else {
                number = number.replace(/[,$]/g, "");
                if (!isNaN(Number(number)) && number.length > 0) {
                    field.$modelValue = number;
                    return true;
                }
            }
        }
        return false;
      },

      validateZip: function(zip) {
        if (/(^\d{5}$)|(^\d{5}-\d{4}$)/.test(zip)) return true;
        return false;
      },

      validateState: function(state) {
        if (/^(A[LKSZRAEP]|C[AOT]|D[EC]|F[LM]|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEHINOPST]|N[CDEHJMVY]|O[HKR]|P[ARW]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$/.test(state)) return true;
        return false;
      },

      setErrors: function(field, errors) {
        if (typeof field.$error === 'object') {
          for (var key in field.$error) {
            if (field.$error[key] === true) {
              if (this.validationMessages[key]) {
                errors[field.$name] = this.validationMessages[key];
              } else {
                errors[field.$name] = key.toString();
              }
              break;
            } else if (field.$error[key] === false) {
              errors[field.$name] = null;
            }
          }
        }
      },

      validateField: function(field, form) {
        var errors = form.FacultyErrors = form.FacultyErrors || {};
        this.setErrors(field, errors);
      },

      validateForm: function(form) {
        var errors = form.FacultyErrors = {};
        for (var f in form) {
          var field = form[f];
          this.setErrors(field, errors);
        }
        if (!isEmpty(errors)) {
          form.FacultyErrors = errors;
          // Errors will display when the form fields are
          // dirty and invalid. If a user has missed a field,
          // the input will be invalid, but pristine. If we
          // automatically set the whole field to dirty,
          // the missed fields will be revealed.
          // $location.hash(Object.keys(errors)[0]);
          $anchorScroll();
          form.$setDirty();
          return false;
        }
        return true;
      },

      removeErrors: function(field, errors) {
        for (var key in field.$error) {
          if (field.$error[key] === true) {
            return;
          }
        }
        errors[field.$name] = null;
      },

      alertFieldSuccess: function(form) {
        var errors = form.FacultyErrors = form.FacultyErrors || {};
        for (var f in form) {
          var field = form[f];
          this.removeErrors(field, errors);
        }
      }
    };
    return Validator;
  }
).directive('validatedForm', [
    function() {
    return {
      restrict: 'A',
      link: function(scope, element, attrs) {
        element.bind("keyup", function (event) {
          scope.Validator.alertFieldSuccess(scope[attrs.validatedForm]);
          scope.$apply();
        });
      }
    };
  }]);
<commit_msg>Add formGroup and formField directives; factored out the separate field-based validation directive like I wanted previously. <commit_after>angular
  .module('app')
  .factory('Validator',
  function($location, $anchorScroll) {

    var isEmpty = function(x) {
      for (var y in x) {
        return false;
      }
      return true;
    };

    var Validator = {

      validationMessages: {
        numericality: "Must be a number.",
        required: "This field is required.",
        zip: "A five or nine digit zip code is required.",
        email: "Please enter a valid email address.",
        mask: "Please enter a valid phone number",
        phone: "Please enter a valid phone number."
      },

      setDirty: function(form) {
        for (var i in form) {
          var input = form[i];
          if (input.$pristine) {
            input.$pristine = false;
            input.$dirty = true;
          }
        }
      },

      setDirtyField: function(input) {
        if (input.$pristine) {
          input.$pristine = false;
          input.$dirty = true;
        }
      },

      // The same issue exists with form.setPristine().
      setPristine: function(form) {
        for (var i in form) {
          var input = form[i];
          if (input.$dirty) {
            input.$pristine = true;
            input.$dirty = false;
          }
        }
      },

      validateCash: function(number, field) {
        if (typeof number === 'string') {
            if (!isNaN(Number(number)) && number.length > 0) {
                return true;
            } else {
                number = number.replace(/[,$]/g, "");
                if (!isNaN(Number(number)) && number.length > 0) {
                    field.$modelValue = number;
                    return true;
                }
            }
        }
        return false;
      },

      validateZip: function(zip) {
        if (/(^\d{5}$)|(^\d{5}-\d{4}$)/.test(zip)) return true;
        return false;
      },

      validateState: function(state) {
        if (/^(A[LKSZRAEP]|C[AOT]|D[EC]|F[LM]|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEHINOPST]|N[CDEHJMVY]|O[HKR]|P[ARW]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$/.test(state)) return true;
        return false;
      },

      setErrors: function(field, errors) {
        if (typeof field.$error === 'object') {
          for (var key in field.$error) {
            if (field.$error[key] === true) {
              if (this.validationMessages[key]) {
                errors[field.$name] = this.validationMessages[key];
              } else {
                errors[field.$name] = key.toString();
              }
              break;
            } else if (field.$error[key] === false) {
              errors[field.$name] = null;
            }
          }
        }
      },

      validateField: function(field, form) {
        var errors = form.FacultyErrors = form.FacultyErrors || {};
        this.setErrors(field, errors);
      },

      validateForm: function(form) {
        var errors = form.FacultyErrors = {};
        for (var f in form) {
          var field = form[f];
          this.setErrors(field, errors);
        }
        if (!isEmpty(errors)) {
          form.FacultyErrors = errors;
          // Errors will display when the form fields are
          // dirty and invalid. If a user has missed a field,
          // the input will be invalid, but pristine. If we
          // automatically set the whole field to dirty,
          // the missed fields will be revealed.
          // $location.hash(Object.keys(errors)[0]);
          $anchorScroll();
          form.$setDirty();
          return false;
        }
        return true;
      },

      removeErrors: function(field, errors) {
        for (var key in field.$error) {
          if (field.$error[key] === true) {
            return;
          }
        }
        errors[field.$name] = null;
      },

      alertFieldSuccess: function(form) {
        var errors = form.FacultyErrors = form.FacultyErrors || {};
        for (var f in form) {
          var field = form[f];
          this.removeErrors(field, errors);
        }
      }
    };
    return Validator;
  }
).directive('formGroup',
  function() {
    return {
      restrict: 'EA',
      transclude: true,
      template: "<div ng-transclude ng-class='{formGroupFinished: checkValidity()}'></div>",
      scope: {},
      controller: function($scope) {
        $scope.fields = [];
        this.addField = function(field) {
          $scope.fields.push(field);
        };
      },

      link: function(scope, element, attrs) {
        var form = scope.$parent[attrs.form];
        scope.form = form;

        scope.fields.forEach(function(f) {
          f.bind('keyup', function(event) {
            scope.$parent.Validator.alertFieldSuccess(form);
            scope.$apply();
            scope.checkValidity();
          });
        });

        scope.checkValidity = function() {
          var validity = true;
          scope.fields.forEach(function(field) {
            if (form[field[0].name] && form[field[0].name].$invalid) {
              validity = false;
            }
          });
          return validity;
        };
        scope.checkValidity();
      }
    };
  })
  .directive('formField', [
    function() {
      return {
        restrict: 'EA',
        scope: false,
        require: '^formGroup',
        link: function(scope, element, attrs, formGroupCtrl) {
          formGroupCtrl.addField(element);
          console.log(scope);
        }
      };
    }]);
<|endoftext|>


<commit_before>const
    cheerio          = require('cheerio')
  , request          = require('request')
  , rootURL          = 'http://www.artsandscience.utoronto.ca'
  , winterCoursesURL = '/ofr/timetable/winter'
  , listingsURL      = '/sponsors.htm';

/*
----------------------------------------
Function: getProgramURL
----------------------------------------
  Returns a string of the course listing URL link.
----------------------------------------
Example Usage Calls
----------------------------------------
  getProgramURL('csc, function(err, courseURL) {
    console.log(courseURL);
  });

  'csc.html'

----------------------------------------
Number of Requests
----------------------------------------
  Min/Max: 1 (parses the listings page)
*/

exports.getProgramURL = function(course, callback) {

  request(rootURL + winterCoursesURL + listingsURL, function(error, response, body) {

    /* Scrape the course URLs from the main course listings page */
    if(!error && response.statusCode === 200) {

      var $            = cheerio.load(body)
        , webPageRegex = new RegExp(/[A-Za-z]\.html/);

      /* Grab the course links from the program bullet points */
      $('li a', '#content').each(function(foundLink) {
        var currentLink = $(this);
        /* Every course link has its course code */
        if (
          webPageRegex.test(currentLink.attr('href').toString()) &&
          currentLink.text().toString().indexOf('[' + course.toUpperCase() + ' courses') > -1)
          {
            callback(null, currentLink.attr('href').toString());
            return;
          };
      });
    };
  });
};

/*
----------------------------------------
Function: getProgramURLs
----------------------------------------
  Returns a json object, containing all Arts and Science department URLs.
----------------------------------------
Example Usage Calls
----------------------------------------
  getProgramURLs(function(urls) {
    console.log(urls);
  });

  [ { courseURL: 'asabs.html' },
  { courseURL: 'ana.html' },
  ...
  { courseURL: 'wdw.html' },
  { courseURL: 'wgsi.html' } ]

----------------------------------------
Number of Requests
----------------------------------------
  Min/Max: 1 (parses the listings page)

*/
exports.getProgramURLs = function(callback) {
  request(rootURL + winterCoursesURL + listingsURL, function(error, response, body) {

    // Scrape the course URLs from the main course listings page
    if(!error && response.statusCode === 200) {

      var $            = cheerio.load(body)
        , urls         = []
        , webPageRegex = new RegExp(/[A-Za-z]\.html/);

      // Grab the course links from the program bullet points
      $('li a', '#content').each(function(foundLink) {
        var currentLink = $(this).attr('href').toString();
        if (webPageRegex.test(currentLink)) {
          urls.push(currentLink);
        };
      });
    };
    callback(null, urls);
  });
};

/*
----------------------------------------
Function: getDepartment
----------------------------------------
  Returns a callback with an array of JSON objects,
  containing the parameter department data.
----------------------------------------
Example Usage Calls
----------------------------------------
  getDepartment('csc', function(err, department) {
    console.log(department);
  });

  { ['Computer Science'] }
----------------------------------------
Number of Requests
----------------------------------------
  Min/Max: 0 (makes no requests)

*/
exports.getDepartment = function(body, abbrev, callback) {
    var $             = cheerio.load(body)
      , departments   = []
      , webPageRegex  = new RegExp(/[A-Za-z]\.html/);

    $('li a', '#content').each(function(foundLink) {
      var linkText = $(this).text().toString()
        , linkAttr = $(this).attr('href').toString();

      /* Decide whether to retrieve all
      departments or just the abbrev department */
      var courseToGet = (abbrev === null)
        ? true
        : linkText.indexOf('[' + abbrev.toUpperCase() + ' courses') > -1;

      // If abbrev has a course in that department
      if (webPageRegex.test(linkAttr) && courseToGet) {
        // Push just the program name, not the extra info
        departments.push({
          "department": linkText.slice(0, linkText.indexOf('[')).trim()
        });
      };
    });
    callback(null, departments);
};

/*
----------------------------------------
Function: getCourseData
----------------------------------------
  Returns a callback with an array of JSON objects,
  containing the specified course data.
----------------------------------------
Example Usage Calls
----------------------------------------
  getDepartment('csc', function(err, courseData) {
    console.log(courseData);
  });
----------------------------------------
Number of Requests
----------------------------------------
  Min/Max: 0 (makes no requests)

*/
exports.getCourseData = function(body, getCancelledCourses, callback) {

  /* Load the new markup from this request. */
  var $                   = cheerio.load(body)
    , courseCodeRegex     = new RegExp(/[A-Za-z]{3}[0-9]{3}[A-Za-z]{1}[0-9]{1}/)
    , courseTutorialRegex = new RegExp(/[^L][0-9]{4}/)
    , courseSectionRegex  = new RegExp(/[L][0-9]{4}/)
    , courseTermRegex     = new RegExp(/[F|S|Y]/)
    , courseWaitListRegex = new RegExp(/[Y|N]/)
    , coursesJSON         = [];

  /* Get the course code from the page markup */
  $('tr').each(function(foundCourse) {
    var currentRow = $(this)
      , section      = currentRow.children().last()
      , cellCount    = 0;

    /* Find the course code starting from the end of the table */
    while(!courseSectionRegex.test(section.text().toString()) && cellCount < 9) {
      section = section.prev();
      cellCount++;
    };

    /* Once the lecture section is found, the other important course
    data is relative to its position. */
    courseWait      = section.next();
    courseName      = section.prev();
    courseTerm      = section.prev().prev();
    courseCode      = section.prev().prev().prev();
    courseProfessor = section.next().next().next().next();

    var acceptCancelledCourses = (getCancelledCourses === true)
      ? true
      : courseWait.text().toString().indexOf('Cancel') === -1;

    /* Make sure we have valid course data */
    if(courseSectionRegex.test(section.text().toString())  &&
       courseTermRegex.test(courseTerm.text().toString())  &&
       courseCodeRegex.test(courseCode.text().toString())  &&
       acceptCancelledCourses)
    {

      var courseProfessors = [];

      /* There may be multiple profs for one course
      section, if so, parse and append them */
      (courseProfessor.text().toString().indexOf('/') > -1)
        ? courseProfessors = courseProfessor.text().toString().split('/')
        : courseProfessors.push(courseProfessor.text().toString());

      coursesJSON.push({
        "courseName": courseName.text().toString(),
        "courseCode": courseCode.text().toString(),
        "courseTerm": courseTerm.text().toString(),
        "courseWait": courseWait.text().toString(),
        "courseProf": courseProfessors
      });
    };
  });
  callback(coursesJSON);
};
<commit_msg>Comment cancellation include condition<commit_after>const
    cheerio          = require('cheerio')
  , request          = require('request')
  , rootURL          = 'http://www.artsandscience.utoronto.ca'
  , winterCoursesURL = '/ofr/timetable/winter'
  , listingsURL      = '/sponsors.htm';

/*
----------------------------------------
Function: getProgramURL
----------------------------------------
  Returns a string of the course listing URL link.
----------------------------------------
Example Usage Calls
----------------------------------------
  getProgramURL('csc, function(err, courseURL) {
    console.log(courseURL);
  });

  'csc.html'

----------------------------------------
Number of Requests
----------------------------------------
  Min/Max: 1 (parses the listings page)
*/

exports.getProgramURL = function(course, callback) {

  request(rootURL + winterCoursesURL + listingsURL, function(error, response, body) {

    /* Scrape the course URLs from the main course listings page */
    if(!error && response.statusCode === 200) {

      var $            = cheerio.load(body)
        , webPageRegex = new RegExp(/[A-Za-z]\.html/);

      /* Grab the course links from the program bullet points */
      $('li a', '#content').each(function(foundLink) {
        var currentLink = $(this);
        /* Every course link has its course code */
        if (
          webPageRegex.test(currentLink.attr('href').toString()) &&
          currentLink.text().toString().indexOf('[' + course.toUpperCase() + ' courses') > -1)
          {
            callback(null, currentLink.attr('href').toString());
            return;
          };
      });
    };
  });
};

/*
----------------------------------------
Function: getProgramURLs
----------------------------------------
  Returns a json object, containing all Arts and Science department URLs.
----------------------------------------
Example Usage Calls
----------------------------------------
  getProgramURLs(function(urls) {
    console.log(urls);
  });

  [ { courseURL: 'asabs.html' },
  { courseURL: 'ana.html' },
  ...
  { courseURL: 'wdw.html' },
  { courseURL: 'wgsi.html' } ]

----------------------------------------
Number of Requests
----------------------------------------
  Min/Max: 1 (parses the listings page)

*/
exports.getProgramURLs = function(callback) {
  request(rootURL + winterCoursesURL + listingsURL, function(error, response, body) {

    // Scrape the course URLs from the main course listings page
    if(!error && response.statusCode === 200) {

      var $            = cheerio.load(body)
        , urls         = []
        , webPageRegex = new RegExp(/[A-Za-z]\.html/);

      // Grab the course links from the program bullet points
      $('li a', '#content').each(function(foundLink) {
        var currentLink = $(this).attr('href').toString();
        if (webPageRegex.test(currentLink)) {
          urls.push(currentLink);
        };
      });
    };
    callback(null, urls);
  });
};

/*
----------------------------------------
Function: getDepartment
----------------------------------------
  Returns a callback with an array of JSON objects,
  containing the parameter department data.
----------------------------------------
Example Usage Calls
----------------------------------------
  getDepartment('csc', function(err, department) {
    console.log(department);
  });

  { ['Computer Science'] }
----------------------------------------
Number of Requests
----------------------------------------
  Min/Max: 0 (makes no requests)

*/
exports.getDepartment = function(body, abbrev, callback) {
    var $             = cheerio.load(body)
      , departments   = []
      , webPageRegex  = new RegExp(/[A-Za-z]\.html/);

    $('li a', '#content').each(function(foundLink) {
      var linkText = $(this).text().toString()
        , linkAttr = $(this).attr('href').toString();

      /* Decide whether to retrieve all
      departments or just the abbrev department */
      var courseToGet = (abbrev === null)
        ? true
        : linkText.indexOf('[' + abbrev.toUpperCase() + ' courses') > -1;

      // If abbrev has a course in that department
      if (webPageRegex.test(linkAttr) && courseToGet) {
        // Push just the program name, not the extra info
        departments.push({
          "department": linkText.slice(0, linkText.indexOf('[')).trim()
        });
      };
    });
    callback(null, departments);
};

/*
----------------------------------------
Function: getCourseData
----------------------------------------
  Returns a callback with an array of JSON objects,
  containing the specified course data.
----------------------------------------
Example Usage Calls
----------------------------------------
  getDepartment('csc', function(err, courseData) {
    console.log(courseData);
  });
----------------------------------------
Number of Requests
----------------------------------------
  Min/Max: 0 (makes no requests)

*/
exports.getCourseData = function(body, getCancelledCourses, callback) {

  /* Load the new markup from this request. */
  var $                   = cheerio.load(body)
    , courseCodeRegex     = new RegExp(/[A-Za-z]{3}[0-9]{3}[A-Za-z]{1}[0-9]{1}/)
    , courseTutorialRegex = new RegExp(/[^L][0-9]{4}/)
    , courseSectionRegex  = new RegExp(/[L][0-9]{4}/)
    , courseTermRegex     = new RegExp(/[F|S|Y]/)
    , courseWaitListRegex = new RegExp(/[Y|N]/)
    , coursesJSON         = [];

  /* Get the course code from the page markup */
  $('tr').each(function(foundCourse) {
    var currentRow = $(this)
      , section      = currentRow.children().last()
      , cellCount    = 0;

    /* Find the course code starting from the end of the table */
    while(!courseSectionRegex.test(section.text().toString()) && cellCount < 9) {
      section = section.prev();
      cellCount++;
    };

    /* Once the lecture section is found, the other important course
    data is relative to its position. */
    courseWait      = section.next();
    courseName      = section.prev();
    courseTerm      = section.prev().prev();
    courseCode      = section.prev().prev().prev();
    courseProfessor = section.next().next().next().next();

    /* Determine whether or not to include
    cancelled courses in the callback JSON array */
    var acceptCancelledCourses = (getCancelledCourses === true)
      ? true
      : courseWait.text().toString().indexOf('Cancel') === -1;

    /* Make sure we have valid course data */
    if(courseSectionRegex.test(section.text().toString())  &&
       courseTermRegex.test(courseTerm.text().toString())  &&
       courseCodeRegex.test(courseCode.text().toString())  &&
       acceptCancelledCourses)
    {

      var courseProfessors = [];

      /* There may be multiple profs for one course
      section, if so, parse and append them */
      (courseProfessor.text().toString().indexOf('/') > -1)
        ? courseProfessors = courseProfessor.text().toString().split('/')
        : courseProfessors.push(courseProfessor.text().toString());

      coursesJSON.push({
        "courseName": courseName.text().toString(),
        "courseCode": courseCode.text().toString(),
        "courseTerm": courseTerm.text().toString(),
        "courseWait": courseWait.text().toString(),
        "courseProf": courseProfessors
      });
    };
  });
  callback(coursesJSON);
};
<|endoftext|>


<commit_before>/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

'use strict';

const gulp = require('gulp');
const filter = require('gulp-filter');
const es = require('event-stream');
const gulptslint = require('gulp-tslint');
const gulpeslint = require('gulp-eslint');
const tsfmt = require('typescript-formatter');
const tslint = require('tslint');
const VinylFile = require('vinyl');
const vfs = require('vinyl-fs');
const path = require('path');
const fs = require('fs');
const pall = require('p-all');

/**
 * Hygiene works by creating cascading subsets of all our files and
 * passing them through a sequence of checks. Here are the current subsets,
 * named according to the checks performed on them. Each subset contains
 * the following one, as described in mathematical notation:
 *
 * all ⊃ eol ⊇ indentation ⊃ copyright ⊃ typescript
 */

const all = [
	'*',
	'build/**/*',
	'extensions/**/*',
	'scripts/**/*',
	'src/**/*',
	'test/**/*',
	'!**/node_modules/**'
];

const indentationFilter = [
	'**',

	// except specific files
	'!ThirdPartyNotices.txt',
	'!LICENSE.txt',
	'!src/vs/nls.js',
	'!src/vs/css.js',
	'!src/vs/loader.js',
	'!src/vs/base/common/marked/marked.js',
	'!src/vs/base/common/winjs.base.js',
	'!src/vs/base/node/terminateProcess.sh',
	'!test/assert.js',

	// except specific folders
	'!test/smoke/out/**',
	'!extensions/vscode-api-tests/testWorkspace/**',
	'!extensions/vscode-api-tests/testWorkspace2/**',
	'!build/monaco/**',
	'!build/win32/**',

	// except multiple specific files
	'!**/package.json',
	'!**/yarn.lock',

	// except multiple specific folders
	'!**/octicons/**',
	'!**/fixtures/**',
	'!**/lib/**',
	'!extensions/**/out/**',
	'!extensions/**/snippets/**',
	'!extensions/**/syntaxes/**',
	'!extensions/**/themes/**',
	'!extensions/**/colorize-fixtures/**',

	// except specific file types
	'!src/vs/*/**/*.d.ts',
	'!src/typings/**/*.d.ts',
	'!extensions/**/*.d.ts',
	'!**/*.{svg,exe,png,bmp,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe}',
	'!build/{lib,tslintRules}/**/*.js',
	'!build/**/*.sh',
	'!build/tfs/**/*.js',
	'!**/Dockerfile',
	'!extensions/markdown-language-features/media/*.js'
];

const copyrightFilter = [
	'**',
	'!**/*.desktop',
	'!**/*.json',
	'!**/*.html',
	'!**/*.template',
	'!**/*.md',
	'!**/*.bat',
	'!**/*.cmd',
	'!**/*.xml',
	'!**/*.sh',
	'!**/*.txt',
	'!**/*.xpm',
	'!**/*.opts',
	'!**/*.disabled',
	'!**/*.code-workspace',
	'!build/**/*.init',
	'!resources/linux/snap/snapcraft.yaml',
	'!resources/win32/bin/code.js',
	'!extensions/markdown-language-features/media/tomorrow.css',
	'!extensions/html-language-features/server/src/modes/typescript/*',
	'!extensions/*/server/bin/*'
];

const eslintFilter = [
	'src/**/*.js',
	'build/gulpfile.*.js',
	'!src/vs/loader.js',
	'!src/vs/css.js',
	'!src/vs/nls.js',
	'!src/vs/css.build.js',
	'!src/vs/nls.build.js',
	'!src/**/winjs.base.js',
	'!src/**/marked.js',
	'!**/test/**'
];

const tslintFilter = [
	'src/**/*.ts',
	'test/**/*.ts',
	'extensions/**/*.ts',
	'!**/fixtures/**',
	'!**/typings/**',
	'!**/node_modules/**',
	'!extensions/typescript/test/colorize-fixtures/**',
	'!extensions/vscode-api-tests/testWorkspace/**',
	'!extensions/vscode-api-tests/testWorkspace2/**',
	'!extensions/**/*.test.ts',
	'!extensions/html-language-features/server/lib/jquery.d.ts'
];

const copyrightHeaderLines = [
	'/*---------------------------------------------------------------------------------------------',
	' *  Copyright (c) Microsoft Corporation. All rights reserved.',
	' *  Licensed under the MIT License. See License.txt in the project root for license information.',
	' *--------------------------------------------------------------------------------------------*/'
];

gulp.task('eslint', () => {
	return vfs.src(all, { base: '.', follow: true, allowEmpty: true })
		.pipe(filter(eslintFilter))
		.pipe(gulpeslint('src/.eslintrc'))
		.pipe(gulpeslint.formatEach('compact'))
		.pipe(gulpeslint.failAfterError());
});

gulp.task('tslint', () => {
	const options = { emitError: true };

	return vfs.src(all, { base: '.', follow: true, allowEmpty: true })
		.pipe(filter(tslintFilter))
		.pipe(gulptslint.default({ rulesDirectory: 'build/lib/tslint' }))
		.pipe(gulptslint.default.report(options));
});

function hygiene(some) {
	let errorCount = 0;

	const indentation = es.through(function (file) {
		const lines = file.contents.toString('utf8').split(/\r\n|\r|\n/);
		file.__lines = lines;

		lines
			.forEach((line, i) => {
				if (/^\s*$/.test(line)) {
					// empty or whitespace lines are OK
				} else if (/^[\t]*[^\s]/.test(line)) {
					// good indent
				} else if (/^[\t]* \*/.test(line)) {
					// block comment using an extra space
				} else {
					console.error(file.relative + '(' + (i + 1) + ',1): Bad whitespace indentation');
					errorCount++;
				}
			});

		this.emit('data', file);
	});

	const copyrights = es.through(function (file) {
		const lines = file.__lines;

		for (let i = 0; i < copyrightHeaderLines.length; i++) {
			if (lines[i] !== copyrightHeaderLines[i]) {
				console.error(file.relative + ': Missing or bad copyright statement');
				errorCount++;
				break;
			}
		}

		this.emit('data', file);
	});

	const formatting = es.map(function (file, cb) {
		tsfmt.processString(file.path, file.contents.toString('utf8'), {
			verify: false,
			tsfmt: true,
			// verbose: true
			// keep checkJS happy
			editorconfig: undefined,
			replace: undefined,
			tsconfig: undefined,
			tslint: undefined
		}).then(result => {
			let original = result.src.replace(/\r\n/gm, '\n');
			let formatted = result.dest.replace(/\r\n/gm, '\n');

			if (original !== formatted) {
				console.error('File not formatted:', file.relative);
				errorCount++;
			}
			cb(null, file);

		}, err => {
			cb(err);
		});
	});

	const tslintConfiguration = tslint.Configuration.findConfiguration('tslint.json', '.');
	const tslintOptions = { fix: false, formatter: 'json' };
	const tsLinter = new tslint.Linter(tslintOptions);

	const tsl = es.through(function (file) {
		const contents = file.contents.toString('utf8');
		tsLinter.lint(file.relative, contents, tslintConfiguration.results);
		this.emit('data', file);
	});

	let input;

	if (Array.isArray(some) || typeof some === 'string' || !some) {
		input = vfs.src(some || all, { base: '.', follow: true, allowEmpty: true });
	} else {
		input = some;
	}

	const result = input
		.pipe(filter(f => !f.stat.isDirectory()))
		.pipe(filter(indentationFilter))
		.pipe(indentation)
		.pipe(filter(copyrightFilter))
		.pipe(copyrights);

	const typescript = result
		.pipe(filter(tslintFilter))
		.pipe(formatting)
		.pipe(tsl);

	const javascript = result
		.pipe(filter(eslintFilter))
		.pipe(gulpeslint('src/.eslintrc'))
		.pipe(gulpeslint.formatEach('compact'))
		.pipe(gulpeslint.failAfterError());

	let count = 0;
	return es.merge(typescript, javascript)
		.pipe(es.through(function (data) {
			count++;
			if (process.env['TRAVIS'] && count % 10 === 0) {
				process.stdout.write('.');
			}
			this.emit('data', data);
		}, function () {
			process.stdout.write('\n');

			const tslintResult = tsLinter.getResult();
			if (tslintResult.failures.length > 0) {
				for (const failure of tslintResult.failures) {
					const name = failure.getFileName();
					const position = failure.getStartPosition();
					const line = position.getLineAndCharacter().line;
					const character = position.getLineAndCharacter().character;

					console.error(`${name}:${line + 1}:${character + 1}:${failure.getFailure()}`);
				}
				errorCount += tslintResult.failures.length;
			}

			if (errorCount > 0) {
				this.emit('error', 'Hygiene failed with ' + errorCount + ' errors. Check \'build/gulpfile.hygiene.js\'.');
			} else {
				this.emit('end');
			}
		}));
}

function createGitIndexVinyls(paths) {
	const cp = require('child_process');
	const repositoryPath = process.cwd();

	const fns = paths.map(relativePath => () => new Promise((c, e) => {
		const fullPath = path.join(repositoryPath, relativePath);

		fs.stat(fullPath, (err, stat) => {
			if (err && err.code === 'ENOENT') { // ignore deletions
				return c(null);
			} else if (err) {
				return e(err);
			}

			cp.exec(`git show :${relativePath}`, { maxBuffer: 2000 * 1024, encoding: 'buffer' }, (err, out) => {
				if (err) {
					return e(err);
				}

				c(new VinylFile({
					path: fullPath,
					base: repositoryPath,
					contents: out,
					stat
				}));
			});
		});
	}));

	return pall(fns, { concurrency: 4 })
		.then(r => r.filter(p => !!p));
}

gulp.task('hygiene', () => hygiene());

// this allows us to run hygiene as a git pre-commit hook
if (require.main === module) {
	const cp = require('child_process');

	process.on('unhandledRejection', (reason, p) => {
		console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
		process.exit(1);
	});

	if (process.argv.length > 2) {
		hygiene(process.argv.slice(2)).on('error', err => {
			console.error();
			console.error(err);
			process.exit(1);
		});
	} else {
		cp.exec('git diff --cached --name-only', { maxBuffer: 2000 * 1024 }, (err, out) => {
			if (err) {
				console.error();
				console.error(err);
				process.exit(1);
				return;
			}

			const some = out
				.split(/\r?\n/)
				.filter(l => !!l);

			if (some.length > 0) {
				console.log('Reading git index versions...');

				createGitIndexVinyls(some)
					.then(vinyls => new Promise((c, e) => hygiene(es.readArray(vinyls))
						.on('end', () => c())
						.on('error', e)))
					.catch(err => {
						console.error();
						console.error(err);
						process.exit(1);
					});
			}
		});
	}
}
<commit_msg>Exclude yarn-error from hygiene<commit_after>/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

'use strict';

const gulp = require('gulp');
const filter = require('gulp-filter');
const es = require('event-stream');
const gulptslint = require('gulp-tslint');
const gulpeslint = require('gulp-eslint');
const tsfmt = require('typescript-formatter');
const tslint = require('tslint');
const VinylFile = require('vinyl');
const vfs = require('vinyl-fs');
const path = require('path');
const fs = require('fs');
const pall = require('p-all');

/**
 * Hygiene works by creating cascading subsets of all our files and
 * passing them through a sequence of checks. Here are the current subsets,
 * named according to the checks performed on them. Each subset contains
 * the following one, as described in mathematical notation:
 *
 * all ⊃ eol ⊇ indentation ⊃ copyright ⊃ typescript
 */

const all = [
	'*',
	'build/**/*',
	'extensions/**/*',
	'scripts/**/*',
	'src/**/*',
	'test/**/*',
	'!**/node_modules/**'
];

const indentationFilter = [
	'**',

	// except specific files
	'!ThirdPartyNotices.txt',
	'!LICENSE.txt',
	'!src/vs/nls.js',
	'!src/vs/css.js',
	'!src/vs/loader.js',
	'!src/vs/base/common/marked/marked.js',
	'!src/vs/base/common/winjs.base.js',
	'!src/vs/base/node/terminateProcess.sh',
	'!test/assert.js',

	// except specific folders
	'!test/smoke/out/**',
	'!extensions/vscode-api-tests/testWorkspace/**',
	'!extensions/vscode-api-tests/testWorkspace2/**',
	'!build/monaco/**',
	'!build/win32/**',

	// except multiple specific files
	'!**/package.json',
	'!**/yarn.lock',
	'!**/yarn-error.log',

	// except multiple specific folders
	'!**/octicons/**',
	'!**/fixtures/**',
	'!**/lib/**',
	'!extensions/**/out/**',
	'!extensions/**/snippets/**',
	'!extensions/**/syntaxes/**',
	'!extensions/**/themes/**',
	'!extensions/**/colorize-fixtures/**',

	// except specific file types
	'!src/vs/*/**/*.d.ts',
	'!src/typings/**/*.d.ts',
	'!extensions/**/*.d.ts',
	'!**/*.{svg,exe,png,bmp,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe}',
	'!build/{lib,tslintRules}/**/*.js',
	'!build/**/*.sh',
	'!build/tfs/**/*.js',
	'!**/Dockerfile',
	'!extensions/markdown-language-features/media/*.js'
];

const copyrightFilter = [
	'**',
	'!**/*.desktop',
	'!**/*.json',
	'!**/*.html',
	'!**/*.template',
	'!**/*.md',
	'!**/*.bat',
	'!**/*.cmd',
	'!**/*.xml',
	'!**/*.sh',
	'!**/*.txt',
	'!**/*.xpm',
	'!**/*.opts',
	'!**/*.disabled',
	'!**/*.code-workspace',
	'!build/**/*.init',
	'!resources/linux/snap/snapcraft.yaml',
	'!resources/win32/bin/code.js',
	'!extensions/markdown-language-features/media/tomorrow.css',
	'!extensions/html-language-features/server/src/modes/typescript/*',
	'!extensions/*/server/bin/*'
];

const eslintFilter = [
	'src/**/*.js',
	'build/gulpfile.*.js',
	'!src/vs/loader.js',
	'!src/vs/css.js',
	'!src/vs/nls.js',
	'!src/vs/css.build.js',
	'!src/vs/nls.build.js',
	'!src/**/winjs.base.js',
	'!src/**/marked.js',
	'!**/test/**'
];

const tslintFilter = [
	'src/**/*.ts',
	'test/**/*.ts',
	'extensions/**/*.ts',
	'!**/fixtures/**',
	'!**/typings/**',
	'!**/node_modules/**',
	'!extensions/typescript/test/colorize-fixtures/**',
	'!extensions/vscode-api-tests/testWorkspace/**',
	'!extensions/vscode-api-tests/testWorkspace2/**',
	'!extensions/**/*.test.ts',
	'!extensions/html-language-features/server/lib/jquery.d.ts'
];

const copyrightHeaderLines = [
	'/*---------------------------------------------------------------------------------------------',
	' *  Copyright (c) Microsoft Corporation. All rights reserved.',
	' *  Licensed under the MIT License. See License.txt in the project root for license information.',
	' *--------------------------------------------------------------------------------------------*/'
];

gulp.task('eslint', () => {
	return vfs.src(all, { base: '.', follow: true, allowEmpty: true })
		.pipe(filter(eslintFilter))
		.pipe(gulpeslint('src/.eslintrc'))
		.pipe(gulpeslint.formatEach('compact'))
		.pipe(gulpeslint.failAfterError());
});

gulp.task('tslint', () => {
	const options = { emitError: true };

	return vfs.src(all, { base: '.', follow: true, allowEmpty: true })
		.pipe(filter(tslintFilter))
		.pipe(gulptslint.default({ rulesDirectory: 'build/lib/tslint' }))
		.pipe(gulptslint.default.report(options));
});

function hygiene(some) {
	let errorCount = 0;

	const indentation = es.through(function (file) {
		const lines = file.contents.toString('utf8').split(/\r\n|\r|\n/);
		file.__lines = lines;

		lines
			.forEach((line, i) => {
				if (/^\s*$/.test(line)) {
					// empty or whitespace lines are OK
				} else if (/^[\t]*[^\s]/.test(line)) {
					// good indent
				} else if (/^[\t]* \*/.test(line)) {
					// block comment using an extra space
				} else {
					console.error(file.relative + '(' + (i + 1) + ',1): Bad whitespace indentation');
					errorCount++;
				}
			});

		this.emit('data', file);
	});

	const copyrights = es.through(function (file) {
		const lines = file.__lines;

		for (let i = 0; i < copyrightHeaderLines.length; i++) {
			if (lines[i] !== copyrightHeaderLines[i]) {
				console.error(file.relative + ': Missing or bad copyright statement');
				errorCount++;
				break;
			}
		}

		this.emit('data', file);
	});

	const formatting = es.map(function (file, cb) {
		tsfmt.processString(file.path, file.contents.toString('utf8'), {
			verify: false,
			tsfmt: true,
			// verbose: true
			// keep checkJS happy
			editorconfig: undefined,
			replace: undefined,
			tsconfig: undefined,
			tslint: undefined
		}).then(result => {
			let original = result.src.replace(/\r\n/gm, '\n');
			let formatted = result.dest.replace(/\r\n/gm, '\n');

			if (original !== formatted) {
				console.error('File not formatted:', file.relative);
				errorCount++;
			}
			cb(null, file);

		}, err => {
			cb(err);
		});
	});

	const tslintConfiguration = tslint.Configuration.findConfiguration('tslint.json', '.');
	const tslintOptions = { fix: false, formatter: 'json' };
	const tsLinter = new tslint.Linter(tslintOptions);

	const tsl = es.through(function (file) {
		const contents = file.contents.toString('utf8');
		tsLinter.lint(file.relative, contents, tslintConfiguration.results);
		this.emit('data', file);
	});

	let input;

	if (Array.isArray(some) || typeof some === 'string' || !some) {
		input = vfs.src(some || all, { base: '.', follow: true, allowEmpty: true });
	} else {
		input = some;
	}

	const result = input
		.pipe(filter(f => !f.stat.isDirectory()))
		.pipe(filter(indentationFilter))
		.pipe(indentation)
		.pipe(filter(copyrightFilter))
		.pipe(copyrights);

	const typescript = result
		.pipe(filter(tslintFilter))
		.pipe(formatting)
		.pipe(tsl);

	const javascript = result
		.pipe(filter(eslintFilter))
		.pipe(gulpeslint('src/.eslintrc'))
		.pipe(gulpeslint.formatEach('compact'))
		.pipe(gulpeslint.failAfterError());

	let count = 0;
	return es.merge(typescript, javascript)
		.pipe(es.through(function (data) {
			count++;
			if (process.env['TRAVIS'] && count % 10 === 0) {
				process.stdout.write('.');
			}
			this.emit('data', data);
		}, function () {
			process.stdout.write('\n');

			const tslintResult = tsLinter.getResult();
			if (tslintResult.failures.length > 0) {
				for (const failure of tslintResult.failures) {
					const name = failure.getFileName();
					const position = failure.getStartPosition();
					const line = position.getLineAndCharacter().line;
					const character = position.getLineAndCharacter().character;

					console.error(`${name}:${line + 1}:${character + 1}:${failure.getFailure()}`);
				}
				errorCount += tslintResult.failures.length;
			}

			if (errorCount > 0) {
				this.emit('error', 'Hygiene failed with ' + errorCount + ' errors. Check \'build/gulpfile.hygiene.js\'.');
			} else {
				this.emit('end');
			}
		}));
}

function createGitIndexVinyls(paths) {
	const cp = require('child_process');
	const repositoryPath = process.cwd();

	const fns = paths.map(relativePath => () => new Promise((c, e) => {
		const fullPath = path.join(repositoryPath, relativePath);

		fs.stat(fullPath, (err, stat) => {
			if (err && err.code === 'ENOENT') { // ignore deletions
				return c(null);
			} else if (err) {
				return e(err);
			}

			cp.exec(`git show :${relativePath}`, { maxBuffer: 2000 * 1024, encoding: 'buffer' }, (err, out) => {
				if (err) {
					return e(err);
				}

				c(new VinylFile({
					path: fullPath,
					base: repositoryPath,
					contents: out,
					stat
				}));
			});
		});
	}));

	return pall(fns, { concurrency: 4 })
		.then(r => r.filter(p => !!p));
}

gulp.task('hygiene', () => hygiene());

// this allows us to run hygiene as a git pre-commit hook
if (require.main === module) {
	const cp = require('child_process');

	process.on('unhandledRejection', (reason, p) => {
		console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
		process.exit(1);
	});

	if (process.argv.length > 2) {
		hygiene(process.argv.slice(2)).on('error', err => {
			console.error();
			console.error(err);
			process.exit(1);
		});
	} else {
		cp.exec('git diff --cached --name-only', { maxBuffer: 2000 * 1024 }, (err, out) => {
			if (err) {
				console.error();
				console.error(err);
				process.exit(1);
				return;
			}

			const some = out
				.split(/\r?\n/)
				.filter(l => !!l);

			if (some.length > 0) {
				console.log('Reading git index versions...');

				createGitIndexVinyls(some)
					.then(vinyls => new Promise((c, e) => hygiene(es.readArray(vinyls))
						.on('end', () => c())
						.on('error', e)))
					.catch(err => {
						console.error();
						console.error(err);
						process.exit(1);
					});
			}
		});
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

var argscheck = cordova.require('cordova/argscheck');
var Event = require('org.chromium.chrome-common.events');
var stubs = require('org.chromium.chrome-common.stubs');
try {
var mobile = require('org.chromium.chrome-bootstrap.mobile.impl');
} catch(e) {}

var manifestJson = null;

exports.onSuspend = new Event('onSuspend');
exports.onInstalled = new Event('onInstalled');
exports.onStartup = new Event('onStartup');
exports.onSuspendCanceled = new Event('onSuspendCanceled');
exports.onUpdateAvailable = new Event('onUpdateAvailable');
//exports.onBrowserUpdateAvailable = new Event('onBrowserUpdateAvailable');
//exports.onConnect = new Event('onConnect');
//exports.onConnectExternal = new Event('onConnectExternal');
//exports.onMessage = new Event('onMessage');
//exports.onMessageExternal = new Event('onMessageExternal');

// Uses a trampoline to bind the Cordova pause event on the first call.
var original_onSuspend_addListener = exports.onSuspend.addListener;
exports.onSuspend.addListener = function(f) {
  window.document.addEventListener('pause', function() {
    retval = exports.onSuspend.fire.apply(exports.onSuspend, arguments);
    chrome.storage.internal.set({"shutdownClean":true});
    return retval;
  }, false);
  exports.onSuspend.addListener = original_onSuspend_addListener;
  exports.onSuspend.addListener(f);
};

// Uses a trampoline to bind the Cordova resume event on the first call.
var original_onSuspendCanceled_addListener = exports.onSuspendCanceled.addListener;
exports.onSuspendCanceled.addListener = function(f) {
  window.document.addEventListener('resume', function() {
    chrome.storage.internal.remove("shutdownClean");
    return exports.onSuspendCanceled.fire.apply(exports.onSuspendCanceled, arguments);
  }, false);
  exports.onSuspendCanceled.addListener = original_onSuspendCanceled_addListener;
  exports.onSuspendCanceled.addListener(f);
};

exports.getManifest = function() {
  if (!manifestJson) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'manifest.json', false /* sync */);
    xhr.send(null);
    manifestJson = eval('(' + xhr.responseText + ')'); //JSON.parse(xhr.responseText);
  }
  return manifestJson;
};

// This is an extension for vanilla cordova apps
exports.setManifest = function(manifest) {
  manifestJson = manifest;
}

exports.getBackgroundPage = function(callback) {
  argscheck.checkArgs('f', 'chrome.runtime.getBackgroundPage', arguments);
  if (!mobile)
    return;
  setTimeout(function() {
    callback(mobile.bgWindow);
  }, 0);
};

exports.getURL = function(subResource) {
  argscheck.checkArgs('s', 'chrome.runtime.getURL', arguments);
  if (subResource.charAt(0) == '/') {
    subResource = subResource.slice(1);
  }
  var prefix = location.href.replace(/[^\/]*$/, '');
  return prefix + subResource;
};

exports.reload = function() {
  location.reload();
};

exports.id = exports.getManifest()['key'];

stubs.createStub(exports, 'requestUpdateCheck', function(){});
<commit_msg>Resolving circular dependancy in bootstrap<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

var argscheck = cordova.require('cordova/argscheck');
var Event = require('org.chromium.chrome-common.events');
var stubs = require('org.chromium.chrome-common.stubs');

var manifestJson = null;

exports.onSuspend = new Event('onSuspend');
exports.onInstalled = new Event('onInstalled');
exports.onStartup = new Event('onStartup');
exports.onSuspendCanceled = new Event('onSuspendCanceled');
exports.onUpdateAvailable = new Event('onUpdateAvailable');
//exports.onBrowserUpdateAvailable = new Event('onBrowserUpdateAvailable');
//exports.onConnect = new Event('onConnect');
//exports.onConnectExternal = new Event('onConnectExternal');
//exports.onMessage = new Event('onMessage');
//exports.onMessageExternal = new Event('onMessageExternal');

// Uses a trampoline to bind the Cordova pause event on the first call.
var original_onSuspend_addListener = exports.onSuspend.addListener;
exports.onSuspend.addListener = function(f) {
  window.document.addEventListener('pause', function() {
    retval = exports.onSuspend.fire.apply(exports.onSuspend, arguments);
    chrome.storage.internal.set({"shutdownClean":true});
    return retval;
  }, false);
  exports.onSuspend.addListener = original_onSuspend_addListener;
  exports.onSuspend.addListener(f);
};

// Uses a trampoline to bind the Cordova resume event on the first call.
var original_onSuspendCanceled_addListener = exports.onSuspendCanceled.addListener;
exports.onSuspendCanceled.addListener = function(f) {
  window.document.addEventListener('resume', function() {
    chrome.storage.internal.remove("shutdownClean");
    return exports.onSuspendCanceled.fire.apply(exports.onSuspendCanceled, arguments);
  }, false);
  exports.onSuspendCanceled.addListener = original_onSuspendCanceled_addListener;
  exports.onSuspendCanceled.addListener(f);
};

exports.getManifest = function() {
  if (!manifestJson) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'manifest.json', false /* sync */);
    xhr.send(null);
    manifestJson = eval('(' + xhr.responseText + ')'); //JSON.parse(xhr.responseText);
  }
  return manifestJson;
};

// This is an extension for vanilla cordova apps
exports.setManifest = function(manifest) {
  manifestJson = manifest;
}

exports.getBackgroundPage = function(callback) {
  try {
  var mobile = require('org.chromium.chrome-bootstrap.mobile.impl');
  } catch(e) {}
  argscheck.checkArgs('f', 'chrome.runtime.getBackgroundPage', arguments);
  setTimeout(function() {
    callback(typeof mobile !== 'undefined' ? mobile.bgWindow : undefined);
  }, 0);
};

exports.getURL = function(subResource) {
  argscheck.checkArgs('s', 'chrome.runtime.getURL', arguments);
  if (subResource.charAt(0) == '/') {
    subResource = subResource.slice(1);
  }
  var prefix = location.href.replace(/[^\/]*$/, '');
  return prefix + subResource;
};

exports.reload = function() {
  location.reload();
};

exports.id = exports.getManifest()['key'];

stubs.createStub(exports, 'requestUpdateCheck', function(){});
<|endoftext|>