<commit_before>/**
 * winch
 *
 * This is designed to lazy load images in an AngularJS application
 */
angular.module('winch', [])
/**
 * This is the master directive.  It attaches all with windows listeners
 * Ex:
 *

 */
  .directive('winchMaster', ['$window', '$timeout', 'winchFactory', 'Throttle',
    function($window, $timeout, winchFactory, Throttle) {
      return {
        restrict: 'AE',
        link: function(scope, elem, attr) {
          var window = angular.element($window),
            elems, index;

          scope._scrollObjects = [window];

          //If a list is provided of selectors setup additional scroll events.
          if (attr.winchMaster) {
            attr.winchMaster.split(',').forEach(function(selector) {
              elems = $window.document.querySelectorAll(selector);
              for (index = 0; index < elems.length; index += 1) {
                scope._scrollObjects.push(angular.element(elems[index]))
              }
            });
          }

          //Function to setup scrollers based on scrollObjects
          scope.setupScrollers = function() {
            //Setup scroll events
            scope._scrollObjects.forEach(function(scrollTarget) {
              scrollTarget.on('scroll', function() {
                scope.triggerValidation();
              });
            });
          };

          scope.setupScrollers();

          //Setup window listener on resize
          window.on('resize', function() {
            setWindowView();
            scope.triggerValidation();
          });

          //setup destroy listener to remove bindings
          scope.$on('$destroy', function() {
            scope._scrollObjects.forEach(function(scrollTarget) {
              scrollTarget.off('scroll');
            });
          });

          /**
           * Stores the current window dimensions
           */
          function setWindowView() {
            winchFactory.setWindowView(
              $window.innerHeight || $window.document.documentElement.clientHeight,
              $window.innerWidth || $window.document.documentElement.clientWidth
            );
          }

          /**
           * Trigger a page validation, throttled
           */
          scope.triggerValidation = function() {
            Throttle.throttle(winchFactory.triggerValidation, 100);
          };

          //Set window view on load
          setWindowView();

          $timeout(function() {
            scope.triggerValidation();
          }, 100);
        }
      };
    }])
/**
 * Attach this directive to any element that the scroll should cause a re-validation
 * Ex:
 *

 */
  .directive('winchScrollTrigger', ['winchFactory', 'Throttle', '$timeout',
    function(winchFactory, Throttle, $timeout) {
      return {
        restrict: 'A',
        scope: {
          winchScrollTrigger: '@'
        },
        link: function(scope, elem) {
          var elems, selectors, index, timerRef;

          scope._scrollObjects = [elem];

          /**
           * add Event listeners to target
           * @param target
           */
          function setup(target) {
            //Setup scroll listener to trigger validation
            target.on('scroll', function() {
              scope.triggerValidation();
            });

            //Listen for end of transition to trigger recheck
            target.on('transitionend webkitTransitionEnd oTransitionEnd', function() {
              scope.triggerValidation();
            });
            //Listen for start of transition to trigger recheck
            target.on('transitionstart webkitTransitionStart oTransitionStart', function() {
              scope.triggerValidation();
            });
          }

          /**
           * Remove event listeners from target
           * @param target
           */
          function tearDown(target) {
            target.off('scroll');
            target.off('transionend webkitTransitionEnd oTransitionEnd');
            target.off('transitionstart webkitTransitionStart oTransitionStart');
          }

          //If selector defined also watch on that
          if (scope.winchScrollTrigger) {
            selectors = scope.winchScrollTrigger.split(',');

            //Wait 300ms before setup event watchers to make sure paint occurs
            timerRef = $timeout(function() {
              try {
                selectors.forEach(function(selector) {
                  elems = elem[0].querySelectorAll(selector);
                  for (index = 0; index < elems.length; index += 1) {
                    scope._scrollObjects.push(angular.element(elems[index]))
                  }
                });
              } catch (e) {
                throw new Error('Error during scroller selection, most likely a bad selector:',
                  scope.winchScrollTrigger);
              }
              //Loop through all additional scroll objects and setup event listeners
              for (index = 1; index < scope._scrollObjects.length; index += 1) {
                setup(scope._scrollObjects[index]);
              }
            }, 300);
          }

          setup(scope._scrollObjects[0]);

          /**
           * Trigger a page validation, throttled
           */
          scope.triggerValidation = function() {
            Throttle.throttle(winchFactory.triggerValidation, 100);
          };

          //setup destroy listener to remove bindings
          scope.$on('$destroy', function() {
            $timeout.cancel(timerRef);
            for (index = 0; index < scope._scrollObjects.length; index += 1) {
              tearDown(scope._scrollObjects[index]);
            }
          });
        }
      };
    }
  ])
/**
 * Replace img tag with winch-img
 * Ex:
 * <winch-img src="http://placehold.it/350x150"></winch-img>
 * <winch-img data-src="awesome.jpg"></winch-img>
 * <winch-img img-src="awesome2.jpg"></winch-img>
 * <winch-img data-img-src="awesome3.jpg" data-img-loaded="imageLoadedCallback"></winch-img>
 */
  .directive('winchImg', ['$compile', '$timeout', 'winchFactory',
    function($compile, $timeout, winchFactory) {
      return {
        priority: 0,
        restrict: 'AE',
        scope: {
          imgUrl: '&',
          imgLoaded: '&'
        },
        link: function(scope, elem, attr) {
          var img;
          //Add a not loaded class
          scope._isLoaded = false;
          elem.addClass('winch-img-not-loaded');

          /**
           * Register self in factory, if it fails 5 times load self
           */
          scope.registerImg = function(attempt) {
            if (!winchFactory.registerImg(
                scope.getImgURL(),
                scope.loadSelf,
                scope.isVisible)) {
              if (attempt < 5) {
                $timeout(function() {
                  scope.registerImg(attempt + 1);
                }, 1000);
              } else {
                scope.loadSelf();
              }
            }
          };

          /**
           * Check if image bottom is bellow window top
           * @param image
           * @param window
           * @returns {boolean}
           */
          function checkImageTop(image, window) {
            //Image bottom is below window top
            return ((image.bottom + window.vOffset) >= window.top);
          }

          /**
           * Check if image top is above window bottom
           * @param image
           * @param window
           * @returns {boolean}
           */
          function checkImageBottom(image, window) {
            //Image top is above bottom
            return ((image.top - window.vOffset) <= window.bottom);
          }

          /**
           * Check if the image is in view based on left box bounds
           * @param image
           * @param window
           * @returns {boolean}
           */
          function checkImageLeft(image, window) {
            //Image right is left of window left
            return ((image.right + window.hOffset) >= window.left);
          }

          /**
           * Check if the image is in view based on right box bounds
           * @param image
           * @param window
           * @returns {boolean}
           */
          function checkImageRight(image, window) {
            //Image left is right of window right
            return ((image.right - window.hOffset) <= window.right);
          }

          /**
           * Check if the image is bigger than the view port
           * @param image
           * @param window
           */
          function checkImageBiggerThanScreen(image, window) {
            return (image.left <= window.left - window.hOffset && image.right >= window.right + window.hOffset) ||
              (image.bottom >= window.bottom + window.vOffset && image.top <= window.top - window.vOffset);
          }

          /**
           * If image is visible return true.
           */
          scope.isVisible = function() {
            var imageBox = scope.boundingBox(),
              windowBox = winchFactory.getMasterBox();

            return checkImageBiggerThanScreen(imageBox, windowBox) ||
              (checkImageTop(imageBox, windowBox) &&
              checkImageBottom(imageBox, windowBox) &&
              checkImageLeft(imageBox, windowBox) &&
              checkImageRight(imageBox, windowBox));
          };

          /**
           * Load self trigger
           */
          scope.loadSelf = function() {
            if (!scope._isLoaded) {
              elem.addClass('winch-img-loaded').removeClass('winch-img-not-loaded');
              img = $compile('')(scope);
              if (attr.winchImgClass) {
                angular.element(img).addClass(attr.winchImgClass);
              }
              elem.empty().append(img);
              scope._isLoaded = true;
              if (scope.imgLoaded && typeof scope.imgLoaded === 'function') {
                scope.imgLoaded();
              }
              $timeout(function() {
                scope.$destroy();
              }, 100);
            }
          };

          /**
           * Get Bounding box for image
           * Used for testing
           */
          scope.boundingBox = function() {
            return elem[0].getBoundingClientRect();
          };

          /**
           * Get image URL
           */
          scope.getImgURL = function() {
            if (scope.imgUrl && typeof scope.imgUrl() !== 'undefined') {
              return scope.imgUrl();
            }
            return attr.src || attr.imgSrc;
          };

          /**
           * If 'winch:validate' is received check if visible.  If visible
           * trigger URL load for all.
           */
          scope.$on('winch:validate', function() {
            if (scope.isVisible()) {
              scope.loadSelf();
              winchFactory.loadImage(scope.getImgURL());
            }
          });

          //Register image to start loading process
          scope.registerImg(1);
        }
      };
    }])
/**
 * Maintain state of winch
 */
  .factory('winchFactory', [function() {
    //Basic API structure
    var API = {
      _img: {}
    };

    /**
     * Register an image
     * @param URL
     * @param load
     * @param validate
     * @returns {boolean}
     */
    API.registerImg = function(URL, load, validate) {
      if (typeof load !== 'function') {
        return false;
      }
      if (typeof validate !== 'function') {
        return false;
      }
      if (typeof URL !== 'string' || URL.length < 1) {
        return false;
      }

      if (API._img.hasOwnProperty(URL)) {
        //If already loaded once (cached) trigger load immediately
        if (API._img[URL].loaded) {
          load();
        } else {
          API._img[URL].fnLoad.push(load);
          API._img[URL].fnValidate.push(validate);
        }
      } else {
        API._img[URL] = {
          fnLoad: [load],
          fnValidate: [validate],
          loaded: false
        };
      }
      return true;
    };

    /**
     * Load all images with the same URL
     * @param URL
     */
    API.loadImage = function(URL) {
      var index = 0, length;
      if (API._img.hasOwnProperty(URL)) {
        length = API._img[URL].fnLoad.length;
        for (index = 0; index < length; index += 1) {
          API._img[URL].fnLoad[index]();
        }
        API._img[URL] = {
          loaded: true,
          fnValidate: [],
          fnLoad: []
        };
      }
    };

    /**
     * Trigger validation of images
     * @param URL - optional
     */
    API.triggerValidation = function(URL) {
      var imageIndex, imageKeys, validationIndex, validationLength;
      if (URL && API._img[URL]) {
        for (validationIndex = 0; validationIndex < API._img[URL].fnValidate.length; validationIndex += 1) {
          if (API._img[URL].fnValidate[validationIndex]()) {
            API.loadImage(URL);
          }
        }
      } else if (!URL) {
        imageKeys = Object.keys(API._img);
        for (imageIndex = 0; imageIndex < imageKeys.length; imageIndex += 1) {
          validationLength = API._img[imageKeys[imageIndex]].fnValidate.length;
          for (validationIndex = 0; validationIndex < validationLength; validationIndex += 1) {
            if (API._img[imageKeys[imageIndex]].fnValidate[validationIndex] &&
              API._img[imageKeys[imageIndex]].fnValidate[validationIndex]()) {
              API.loadImage(imageKeys[imageIndex]);
            }
          }
        }
      }
    };

    /**
     * Store the current window Height and Width
     * @param height
     * @param width
     */
    API.setWindowView = function(height, width) {
      API.windowWidth = width;
      API.windowHeight = height;
    };

    /**
     * Return window width
     * @returns number
     */
    API.getWindowWidth = function() {
      return API.windowWidth;
    };

    /**
     * Return window height
     * @returns number
     */
    API.getWindowHeight = function() {
      return API.windowHeight;
    };

    /**
     * Returns an object of window bounding
     * @returns {{top: number, bottom: number, left: number, right: number, vOffset: number, hOffset: number}}
     */
    API.getMasterBox = function() {
      return {
        top: 0,
        bottom: API.windowHeight,
        left: 0,
        right: API.windowWidth,
        vOffset: 100,
        hOffset: 100
      };
    };
    return API;
  }])
/**
 * Throttle service
 */
  .service('Throttle', ['$q', '$timeout', function($q, $timeout) {
    var pending = {};

    /**
     * Is function currently pending
     * @param fn
     * @returns {boolean}
     */
    function isPending(fn) {
      return pending.hasOwnProperty(fn);
    }

    /**
     * Clear a function
     * @param fn
     */
    function clearPending(fn) {
      delete pending[fn];
    }

    /**
     * Get a currently pending function
     * @param fn
     * @returns promise
     */
    function getPending(fn) {
      return pending[fn];
    }

    /**
     * Store function
     * @param fn
     * @param defer
     */
    function storePending(fn, defer) {
      pending[fn] = defer;
    }

    return {
      /**
       * Function that merges all the same calls into the first for the delay specified
       * @param fn
       * @param delay
       * @returns promise
       */
      throttle: function(fn, delay) {
        var deferred = $q.defer();

        //Check if Delay is valid, if not default to 100ms
        if (!(parseInt(delay, 10) > 0)) {
          delay = 100;
        }

        if (typeof fn === 'function') {
          if (isPending(fn)) {
            //return existing promise
            return getPending(fn).promise;
          } else {
            $timeout(function() {
              fn();
              deferred.resolve();
              //remove it
              clearPending(fn);
            }, delay);
            //store promise
            storePending(fn, deferred);
            return deferred.promise;
          }
        } else {
          deferred.reject('Not a Function');
          return deferred.promise;
        }
      }
    };
  }])
/**
 * winch Filter to convert HTML img tags to winch-img directives
 */
  .filter('winchify', function() {
    return function(html) {
      var openTag = '<img', openRgx,
        closeTag = '', closeRgx,
        ngSrcAttr = 'ng-src',
        ngSrcRgx;
      openRgx = new RegExp(openTag, 'gi');
      closeRgx = new RegExp(closeTag, 'gi');
      ngSrcRgx = new RegExp(ngSrcAttr, 'gi');

      //Recursive functions for adding  tag after  tag
      function addCloseImg(string) {
        if (string.search(openRgx) > -1) {
          var location = string.indexOf('>', string.search(openRgx) + openTag.length);
          //Add Close Image tag
          return string.substr(0, location + 1) + '' + addCloseImg(string.substr(location + 1));
        } else {
          //no image found - return remaining content
          return string;
        }
      }

      if (html.search(openRgx) > -1) { //Verify there is an image tag
        html = addCloseImg(html)
          .replace(openRgx, '<winch-img')
          .replace(ngSrcRgx, 'img-src')
          .replace(closeRgx, '</winch-img>');

        return html;
      } else {
        return html;
      }
    };
  });
<commit_msg>Remove IE8 support<commit_after>/**
 * winch
 *
 * This is designed to lazy load images in an AngularJS application
 */
angular.module('winch', [])
/**
 * This is the master directive.  It attaches all with windows listeners
 * Ex:
 *

 */
  .directive('winchMaster', ['$window', '$timeout', 'winchFactory', 'Throttle',
    function($window, $timeout, winchFactory, Throttle) {
      return {
        restrict: 'AE',
        link: function(scope, elem, attr) {
          var window = angular.element($window),
            elems, index;

          scope._scrollObjects = [window];

          //If a list is provided of selectors setup additional scroll events.
          if (attr.winchMaster) {
            attr.winchMaster.split(',').forEach(function(selector) {
              elems = $window.document.querySelectorAll(selector);
              for (index = 0; index < elems.length; index += 1) {
                scope._scrollObjects.push(angular.element(elems[index]))
              }
            });
          }

          //Function to setup scrollers based on scrollObjects
          scope.setupScrollers = function() {
            //Setup scroll events
            scope._scrollObjects.forEach(function(scrollTarget) {
              scrollTarget.on('scroll', function() {
                scope.triggerValidation();
              });
            });
          };

          scope.setupScrollers();

          //Setup window listener on resize
          window.on('resize', function() {
            setWindowView();
            scope.triggerValidation();
          });

          //setup destroy listener to remove bindings
          scope.$on('$destroy', function() {
            scope._scrollObjects.forEach(function(scrollTarget) {
              scrollTarget.off('scroll');
            });
          });

          /**
           * Stores the current window dimensions
           */
          function setWindowView() {
            winchFactory.setWindowView(
              $window.innerHeight,
              $window.innerWidth
            );
          }

          /**
           * Trigger a page validation, throttled
           */
          scope.triggerValidation = function() {
            Throttle.throttle(winchFactory.triggerValidation, 100);
          };

          //Set window view on load
          setWindowView();

          $timeout(function() {
            scope.triggerValidation();
          }, 100);
        }
      };
    }])
/**
 * Attach this directive to any element that the scroll should cause a re-validation
 * Ex:
 *

 */
  .directive('winchScrollTrigger', ['winchFactory', 'Throttle', '$timeout',
    function(winchFactory, Throttle, $timeout) {
      return {
        restrict: 'A',
        scope: {
          winchScrollTrigger: '@'
        },
        link: function(scope, elem) {
          var elems, selectors, index, timerRef;

          scope._scrollObjects = [elem];

          /**
           * add Event listeners to target
           * @param target
           */
          function setup(target) {
            //Setup scroll listener to trigger validation
            target.on('scroll', function() {
              scope.triggerValidation();
            });

            //Listen for end of transition to trigger recheck
            target.on('transitionend webkitTransitionEnd oTransitionEnd', function() {
              scope.triggerValidation();
            });
            //Listen for start of transition to trigger recheck
            target.on('transitionstart webkitTransitionStart oTransitionStart', function() {
              scope.triggerValidation();
            });
          }

          /**
           * Remove event listeners from target
           * @param target
           */
          function tearDown(target) {
            target.off('scroll');
            target.off('transionend webkitTransitionEnd oTransitionEnd');
            target.off('transitionstart webkitTransitionStart oTransitionStart');
          }

          //If selector defined also watch on that
          if (scope.winchScrollTrigger) {
            selectors = scope.winchScrollTrigger.split(',');

            //Wait 300ms before setup event watchers to make sure paint occurs
            timerRef = $timeout(function() {
              try {
                selectors.forEach(function(selector) {
                  elems = elem[0].querySelectorAll(selector);
                  for (index = 0; index < elems.length; index += 1) {
                    scope._scrollObjects.push(angular.element(elems[index]))
                  }
                });
              } catch (e) {
                throw new Error('Error during scroller selection, most likely a bad selector:',
                  scope.winchScrollTrigger);
              }
              //Loop through all additional scroll objects and setup event listeners
              for (index = 1; index < scope._scrollObjects.length; index += 1) {
                setup(scope._scrollObjects[index]);
              }
            }, 300);
          }

          setup(scope._scrollObjects[0]);

          /**
           * Trigger a page validation, throttled
           */
          scope.triggerValidation = function() {
            Throttle.throttle(winchFactory.triggerValidation, 100);
          };

          //setup destroy listener to remove bindings
          scope.$on('$destroy', function() {
            $timeout.cancel(timerRef);
            for (index = 0; index < scope._scrollObjects.length; index += 1) {
              tearDown(scope._scrollObjects[index]);
            }
          });
        }
      };
    }
  ])
/**
 * Replace img tag with winch-img
 * Ex:
 * <winch-img src="http://placehold.it/350x150"></winch-img>
 * <winch-img data-src="awesome.jpg"></winch-img>
 * <winch-img img-src="awesome2.jpg"></winch-img>
 * <winch-img data-img-src="awesome3.jpg" data-img-loaded="imageLoadedCallback"></winch-img>
 */
  .directive('winchImg', ['$compile', '$timeout', 'winchFactory',
    function($compile, $timeout, winchFactory) {
      return {
        priority: 0,
        restrict: 'AE',
        scope: {
          imgUrl: '&',
          imgLoaded: '&'
        },
        link: function(scope, elem, attr) {
          var img;
          //Add a not loaded class
          scope._isLoaded = false;
          elem.addClass('winch-img-not-loaded');

          /**
           * Register self in factory, if it fails 5 times load self
           */
          scope.registerImg = function(attempt) {
            if (!winchFactory.registerImg(
                scope.getImgURL(),
                scope.loadSelf,
                scope.isVisible)) {
              if (attempt < 5) {
                $timeout(function() {
                  scope.registerImg(attempt + 1);
                }, 1000);
              } else {
                scope.loadSelf();
              }
            }
          };

          /**
           * Check if image bottom is bellow window top
           * @param image
           * @param window
           * @returns {boolean}
           */
          function checkImageTop(image, window) {
            //Image bottom is below window top
            return ((image.bottom + window.vOffset) >= window.top);
          }

          /**
           * Check if image top is above window bottom
           * @param image
           * @param window
           * @returns {boolean}
           */
          function checkImageBottom(image, window) {
            //Image top is above bottom
            return ((image.top - window.vOffset) <= window.bottom);
          }

          /**
           * Check if the image is in view based on left box bounds
           * @param image
           * @param window
           * @returns {boolean}
           */
          function checkImageLeft(image, window) {
            //Image right is left of window left
            return ((image.right + window.hOffset) >= window.left);
          }

          /**
           * Check if the image is in view based on right box bounds
           * @param image
           * @param window
           * @returns {boolean}
           */
          function checkImageRight(image, window) {
            //Image left is right of window right
            return ((image.right - window.hOffset) <= window.right);
          }

          /**
           * Check if the image is bigger than the view port
           * @param image
           * @param window
           */
          function checkImageBiggerThanScreen(image, window) {
            return (image.left <= window.left - window.hOffset && image.right >= window.right + window.hOffset) ||
              (image.bottom >= window.bottom + window.vOffset && image.top <= window.top - window.vOffset);
          }

          /**
           * If image is visible return true.
           */
          scope.isVisible = function() {
            var imageBox = scope.boundingBox(),
              windowBox = winchFactory.getMasterBox();

            return checkImageBiggerThanScreen(imageBox, windowBox) ||
              (checkImageTop(imageBox, windowBox) &&
              checkImageBottom(imageBox, windowBox) &&
              checkImageLeft(imageBox, windowBox) &&
              checkImageRight(imageBox, windowBox));
          };

          /**
           * Load self trigger
           */
          scope.loadSelf = function() {
            if (!scope._isLoaded) {
              elem.addClass('winch-img-loaded').removeClass('winch-img-not-loaded');
              img = $compile('')(scope);
              if (attr.winchImgClass) {
                angular.element(img).addClass(attr.winchImgClass);
              }
              elem.empty().append(img);
              scope._isLoaded = true;
              if (scope.imgLoaded && typeof scope.imgLoaded === 'function') {
                scope.imgLoaded();
              }
              $timeout(function() {
                scope.$destroy();
              }, 100);
            }
          };

          /**
           * Get Bounding box for image
           * Used for testing
           */
          scope.boundingBox = function() {
            return elem[0].getBoundingClientRect();
          };

          /**
           * Get image URL
           */
          scope.getImgURL = function() {
            if (scope.imgUrl && typeof scope.imgUrl() !== 'undefined') {
              return scope.imgUrl();
            }
            return attr.src || attr.imgSrc;
          };

          /**
           * If 'winch:validate' is received check if visible.  If visible
           * trigger URL load for all.
           */
          scope.$on('winch:validate', function() {
            if (scope.isVisible()) {
              scope.loadSelf();
              winchFactory.loadImage(scope.getImgURL());
            }
          });

          //Register image to start loading process
          scope.registerImg(1);
        }
      };
    }])
/**
 * Maintain state of winch
 */
  .factory('winchFactory', [function() {
    //Basic API structure
    var API = {
      _img: {}
    };

    /**
     * Register an image
     * @param URL
     * @param load
     * @param validate
     * @returns {boolean}
     */
    API.registerImg = function(URL, load, validate) {
      if (typeof load !== 'function') {
        return false;
      }
      if (typeof validate !== 'function') {
        return false;
      }
      if (typeof URL !== 'string' || URL.length < 1) {
        return false;
      }

      if (API._img.hasOwnProperty(URL)) {
        //If already loaded once (cached) trigger load immediately
        if (API._img[URL].loaded) {
          load();
        } else {
          API._img[URL].fnLoad.push(load);
          API._img[URL].fnValidate.push(validate);
        }
      } else {
        API._img[URL] = {
          fnLoad: [load],
          fnValidate: [validate],
          loaded: false
        };
      }
      return true;
    };

    /**
     * Load all images with the same URL
     * @param URL
     */
    API.loadImage = function(URL) {
      var index = 0, length;
      if (API._img.hasOwnProperty(URL)) {
        length = API._img[URL].fnLoad.length;
        for (index = 0; index < length; index += 1) {
          API._img[URL].fnLoad[index]();
        }
        API._img[URL] = {
          loaded: true,
          fnValidate: [],
          fnLoad: []
        };
      }
    };

    /**
     * Trigger validation of images
     * @param URL - optional
     */
    API.triggerValidation = function(URL) {
      var imageIndex, imageKeys, validationIndex, validationLength;
      if (URL && API._img[URL]) {
        for (validationIndex = 0; validationIndex < API._img[URL].fnValidate.length; validationIndex += 1) {
          if (API._img[URL].fnValidate[validationIndex]()) {
            API.loadImage(URL);
          }
        }
      } else if (!URL) {
        imageKeys = Object.keys(API._img);
        for (imageIndex = 0; imageIndex < imageKeys.length; imageIndex += 1) {
          validationLength = API._img[imageKeys[imageIndex]].fnValidate.length;
          for (validationIndex = 0; validationIndex < validationLength; validationIndex += 1) {
            if (API._img[imageKeys[imageIndex]].fnValidate[validationIndex] &&
              API._img[imageKeys[imageIndex]].fnValidate[validationIndex]()) {
              API.loadImage(imageKeys[imageIndex]);
            }
          }
        }
      }
    };

    /**
     * Store the current window Height and Width
     * @param height
     * @param width
     */
    API.setWindowView = function(height, width) {
      API.windowWidth = width;
      API.windowHeight = height;
    };

    /**
     * Return window width
     * @returns number
     */
    API.getWindowWidth = function() {
      return API.windowWidth;
    };

    /**
     * Return window height
     * @returns number
     */
    API.getWindowHeight = function() {
      return API.windowHeight;
    };

    /**
     * Returns an object of window bounding
     * @returns {{top: number, bottom: number, left: number, right: number, vOffset: number, hOffset: number}}
     */
    API.getMasterBox = function() {
      return {
        top: 0,
        bottom: API.windowHeight,
        left: 0,
        right: API.windowWidth,
        vOffset: 100,
        hOffset: 100
      };
    };
    return API;
  }])
/**
 * Throttle service
 */
  .service('Throttle', ['$q', '$timeout', function($q, $timeout) {
    var pending = {};

    /**
     * Is function currently pending
     * @param fn
     * @returns {boolean}
     */
    function isPending(fn) {
      return pending.hasOwnProperty(fn);
    }

    /**
     * Clear a function
     * @param fn
     */
    function clearPending(fn) {
      delete pending[fn];
    }

    /**
     * Get a currently pending function
     * @param fn
     * @returns promise
     */
    function getPending(fn) {
      return pending[fn];
    }

    /**
     * Store function
     * @param fn
     * @param defer
     */
    function storePending(fn, defer) {
      pending[fn] = defer;
    }

    return {
      /**
       * Function that merges all the same calls into the first for the delay specified
       * @param fn
       * @param delay
       * @returns promise
       */
      throttle: function(fn, delay) {
        var deferred = $q.defer();

        //Check if Delay is valid, if not default to 100ms
        if (!(parseInt(delay, 10) > 0)) {
          delay = 100;
        }

        if (typeof fn === 'function') {
          if (isPending(fn)) {
            //return existing promise
            return getPending(fn).promise;
          } else {
            $timeout(function() {
              fn();
              deferred.resolve();
              //remove it
              clearPending(fn);
            }, delay);
            //store promise
            storePending(fn, deferred);
            return deferred.promise;
          }
        } else {
          deferred.reject('Not a Function');
          return deferred.promise;
        }
      }
    };
  }])
/**
 * winch Filter to convert HTML img tags to winch-img directives
 */
  .filter('winchify', function() {
    return function(html) {
      var openTag = '<img', openRgx,
        closeTag = '', closeRgx,
        ngSrcAttr = 'ng-src',
        ngSrcRgx;
      openRgx = new RegExp(openTag, 'gi');
      closeRgx = new RegExp(closeTag, 'gi');
      ngSrcRgx = new RegExp(ngSrcAttr, 'gi');

      //Recursive functions for adding  tag after  tag
      function addCloseImg(string) {
        if (string.search(openRgx) > -1) {
          var location = string.indexOf('>', string.search(openRgx) + openTag.length);
          //Add Close Image tag
          return string.substr(0, location + 1) + '' + addCloseImg(string.substr(location + 1));
        } else {
          //no image found - return remaining content
          return string;
        }
      }

      if (html.search(openRgx) > -1) { //Verify there is an image tag
        html = addCloseImg(html)
          .replace(openRgx, '<winch-img')
          .replace(ngSrcRgx, 'img-src')
          .replace(closeRgx, '</winch-img>');

        return html;
      } else {
        return html;
      }
    };
  });
<|endoftext|>


<commit_before>var winston = require('winston');
winston.emitErrs = true;

var logger = new winston.Logger({
    transports: [
        new winston.transports.File({
            level: 'info',
            filename: './logs/server_logs.log',
            handleExceptions: true,
            json: true,
            maxsize: 5242880, //5MB
            maxFiles: 5,
            colorize: false
        }),
        new winston.transports.Console({
            level: 'debug',
            handleExceptions: true,
            json: false,
            colorize: true
        })
    ],
    exitOnError: false
});

module.exports = logger;
module.exports.stream = {
    write: function(message, encoding){
        logger.info(message);
    }
};
<commit_msg>Refactor based on eslint.<commit_after>var winston = require('winston');
winston.emitErrs = true;

var logger = new winston.Logger({
  transports: [
    new winston.transports.File({
      level: 'info',
      filename: './logs/server_logs.log',
      handleExceptions: true,
      json: true,
      maxsize: 5242880, // 5MB
      maxFiles: 5,
      colorize: false
    }),
    new winston.transports.Console({
      level: 'debug',
      handleExceptions: true,
      json: false,
      colorize: true
    })
  ],
  exitOnError: false
});

module.exports = logger;
module.exports.stream = {
  write: function(message, encoding) {
    logger.info(message);
  }
};
<|endoftext|>


<commit_before>'use strict'

window.onload = function() {
  clock();
  loadOptions();
}

var SETTINGS = JSON.parse(localStorage.getItem('userOptions'));

var commands = [
  { command: 'g', url: 'https://www.google.com', search: '/search?q=' },
  { command: 'r', url: 'https://www.reddit.com', search: '/r/' },
  { command: 'y', url: 'https://www.youtube.com', search: '/results?search_query=' },
  { command: 'a', url: 'https://smile.amazon.com', search: '/s/?field-keywords=' },
  { command: 'w', url: 'https://www.wikipedia.org', search: '/w/index.php?title=Special:Search&search=' },
  { command: 'wa', url: 'https://www.wolframalpha.com', search: '/input/?i=' },
  { command: 'imdb', url: 'https://www.imdb.com', search: '/find?s=all&q=' },
  { command: 'img', url: 'https://www.google.com', search: '/search?tbm=isch&q=' },
  { command: 't', url: '', search: '' }
];

// Input
function verifyKey(e) {
  var keycode;
  if (window.event) {
    keycode = window.event.keyCode;
  } else if (e) {
    keycode = e.which;
  }
  if (keycode == 13) { // Enter/return key
    clearContent();
    interpret();
  }
  checkInputLength();
}

function checkInputLength() {
  var input = document.getElementById('input-box');
  if (input.value.length >= (input.size - 1)) {
     input.size = input.value.length + 1;
  }
}

function getFullCommand(c) {
  for (var i=0; i < commands.length; i++) {
    if (c === commands[i].command) {
      return commands[i];
    }
  }
  return null;
}

function interpret() {
  var inputBox = document.getElementById('input-box');
  inputBox.select();
  var input = inputBox.value.trim();
  if (input === '') { return; }

  if (input === 'help' || input === '?') {
    displayHelp(); // TODO implement help
    return;
  } else if (input === 'options') {
    displayOptionsMenu();
    return;
  }

  var inputArr = input.split(';');
  var newtab = (inputArr[inputArr.length-1] === 'n');
  var command; var query;

  var validCommand = false;
  command = getFullCommand(inputArr[0]);
  if (command !== null) {
    validCommand = true;
  }

  if (validCommand) {
    query = (command.command === 'w' || command.command === 'wa') // wikipedia & wolframalpha fix
      ? inputArr[1].trim().replace(/ /g, '+')
      : inputArr[1].trim();
    switch(inputArr.length) {
      case 1:
        redirect(command.url, newtab);
        return false;
        break;
      case 2:
        if (inputArr[1] === 'n') {
          redirect(command.url, newtab);
          return false;
        } else {
          redirect(command.url + command.search + query, newtab);
          return false;
        }
        break;
      default:
        redirect(command.url + command.search + query, newtab);
        return false;
    }
    redirect(command.url + command.search + query, newtab);
    return false;
  } else { // TODO check query: wikipedia & wolframalpha fix
    command = SETTINGS.defaultCommand;
    query = inputArr[0].trim();
    redirect(command.url + command.search + query, newtab);
    return false;
  }
}

function redirect(url, newtab) {
  url = (!url.startsWith('http'))
    ? 'https://' + url
    : url;
  if (newtab) {
    var win = window.open(url);
    win.focus();
    return false;
  } else {
    window.location.href = url;
    return false;
  }
}

// Options
function saveOptions() {
  if (typeof(Storage) == "undefined") {
    alert("Browser does not support local storage: your settings won't be saved (sorry)");
  } else {
    // Get default command
    var radios = document.getElementById('defaultCommandForm');
    var defaultCommand = null;
    for (var i=0; i < radios.length; i++) {
      if (radios[i].checked) {
        defaultCommand = radios[i].value;
        break;
      }
    }
    defaultCommand = getFullCommand(defaultCommand);
    if (defaultCommand !== null) {
      SETTINGS.defaultCommand = defaultCommand;
    }

    // Tab open style
    SETTINGS.alwaysNewTab = document.getElementById('openStyleCheckbox');

    // Background color
    var color = document.getElementById('bgColorInput').value;
    if (!color.startsWith('#')) {
      color = '#' + color;
    }
    if (/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(color)) { // check if valid hex value
      SETTINGS.color = color;
      setColor(color);
      localStorage.setItem('userOptions', JSON.stringify(SETTINGS));
    } else {
      alert('not a valid hex value\n(valid example: #A1C0C0)');
    }
  }
  var inputBox = document.getElementById('input-box');
  inputBox.value = "";
  inputBox.select();
}

function loadOptions() {
  if (typeof(Storage) !== 'undefined') {
    if (localStorage.getItem('userOptions') !== null) {
      SETTINGS = JSON.parse(localStorage.getItem('userOptions'));
      setColor(SETTINGS.color);
    } else {
      var defaultSettings = {
        defaultCommand: {
          command: 'g',
          url: 'https://www.google.com',
          search: '/search?q='
        },
        alwaysNewtab: false,
        color: "#A1C0C0"
      };
      localStorage.setItem('userOptions', JSON.stringify(defaultSettings));
      SETTINGS = JSON.parse(localStorage.getItem('userOptions'));
    }
  }
}

function setColor(color) {
  document.body.style.backgroundColor = color;
}

// Displayed content
function clearContent() {
  document.getElementById('content').innerHTML = '';
}

function displayContent(content) {
  clearContent();
  document.getElementById('content').innerHTML = content;
}

function displayOptionsMenu() {
  var html = "\

 \

 \

 \
       Default Command
executes if no command was specified	 \

 \

 \
        \
          <form id='defaultCommandForm'> \
             <input type='radio' name='defaultCommandRadio' value='t'>Go to website
 \
             <input type='radio' name='defaultCommandRadio' value='g'>Google search
 \
             <input type='radio' name='defaultCommandRadio' value='w'>Wikipedia search
 \
             <input type='radio' name='defaultCommandRadio' value='y'>YouTube search
 \
             <input type='radio' name='defaultCommandRadio' value='a'>Amazon search
 \
             <input type='radio' name='defaultCommandRadio' value='imdb'>IMDB search
 \
             <input type='radio' name='defaultCommandRadio' value='img'>Google Images search
 \
             <input type='radio' name='defaultCommandRadio' value='wa'>Wolfram Alpha search
 \
             <input type='radio' name='defaultCommandRadio' value='r'>Go to subreddit \
          </form> \
       	 \

 \

 \
       Open Style	 \

 \

 \
        \
          <input type='checkbox' id='openStyleCheckbox' value='newTab'>Always open in new tab
 \
       	 \

 \

 \
       Background Color	 \

 \

 \
        \
          Color: <input type='text' id='bgColorInput' placeholder='#A1C0C0' size='7' spellcheck='false'> \
          Color Picker \
       	 \

 \

 \
        \
          <button type='button' id='saveOptionsBtn'>Done</button> \
       	 \

 \

";

  var wrapper = document.getElementById('wrapper');
  wrapper.style.top = "10%";

  displayContent(html);
  document.getElementById('saveOptionsBtn').onclick = function() {
    wrapper.style.top = "35%";
    saveOptions();
    clearContent();
    displayMessage('settings saved', 2000);
  };

  // Display saved options
  var radios = document.getElementById('defaultCommandForm');
  for (var i=0; i < radios.length; i++) {
    if (SETTINGS.defaultCommand.command === radios[i].value) {
      radios[i].checked = true;
      break;
    }
  }
  document.getElementById('openStyleCheckbox').checked = SETTINGS.alwaysNewTab;
  document.getElementById('bgColorInput').value = SETTINGS.color;
}

// Clock
function clock() {
  var date = new Date();
  var h = date.getHours();
  h = (h > 12) ? (checkTime(h -= 12)) : checkTime(h); // 12hr clock
  var m = checkTime(date.getMinutes());
  document.getElementById('clock').innerHTML = h + ':' + m;
  setTimeout(clock, 1000);
}
function checkTime(n) {
  n = (n < 10) ? (n = '0' + n) : n;
  return n;
}
<commit_msg>Add clear and display functions for message div<commit_after>'use strict'

window.onload = function() {
  clock();
  loadOptions();
}

var SETTINGS = JSON.parse(localStorage.getItem('userOptions'));

var commands = [
  { command: 'g', url: 'https://www.google.com', search: '/search?q=' },
  { command: 'r', url: 'https://www.reddit.com', search: '/r/' },
  { command: 'y', url: 'https://www.youtube.com', search: '/results?search_query=' },
  { command: 'a', url: 'https://smile.amazon.com', search: '/s/?field-keywords=' },
  { command: 'w', url: 'https://www.wikipedia.org', search: '/w/index.php?title=Special:Search&search=' },
  { command: 'wa', url: 'https://www.wolframalpha.com', search: '/input/?i=' },
  { command: 'imdb', url: 'https://www.imdb.com', search: '/find?s=all&q=' },
  { command: 'img', url: 'https://www.google.com', search: '/search?tbm=isch&q=' },
  { command: 't', url: '', search: '' }
];

// Input
function verifyKey(e) {
  var keycode;
  if (window.event) {
    keycode = window.event.keyCode;
  } else if (e) {
    keycode = e.which;
  }
  if (keycode == 13) { // Enter/return key
    clearContent();
    interpret();
  }
  checkInputLength();
}

function checkInputLength() {
  var input = document.getElementById('input-box');
  if (input.value.length >= (input.size - 1)) {
     input.size = input.value.length + 1;
  }
}

function getFullCommand(c) {
  for (var i=0; i < commands.length; i++) {
    if (c === commands[i].command) {
      return commands[i];
    }
  }
  return null;
}

function interpret() {
  var inputBox = document.getElementById('input-box');
  inputBox.select();
  var input = inputBox.value.trim();
  if (input === '') { return; }

  if (input === 'help' || input === '?') {
    displayHelp(); // TODO implement help
    return;
  } else if (input === 'options') {
    displayOptionsMenu();
    return;
  }

  var inputArr = input.split(';');
  var newtab = (inputArr[inputArr.length-1] === 'n');
  var command; var query;

  var validCommand = false;
  command = getFullCommand(inputArr[0]);
  if (command !== null) {
    validCommand = true;
  }

  if (validCommand) {
    query = (command.command === 'w' || command.command === 'wa') // wikipedia & wolframalpha fix
      ? inputArr[1].trim().replace(/ /g, '+')
      : inputArr[1].trim();
    switch(inputArr.length) {
      case 1:
        redirect(command.url, newtab);
        return false;
        break;
      case 2:
        if (inputArr[1] === 'n') {
          redirect(command.url, newtab);
          return false;
        } else {
          redirect(command.url + command.search + query, newtab);
          return false;
        }
        break;
      default:
        redirect(command.url + command.search + query, newtab);
        return false;
    }
    redirect(command.url + command.search + query, newtab);
    return false;
  } else { // TODO check query: wikipedia & wolframalpha fix
    command = SETTINGS.defaultCommand;
    query = inputArr[0].trim();
    redirect(command.url + command.search + query, newtab);
    return false;
  }
}

function redirect(url, newtab) {
  url = (!url.startsWith('http'))
    ? 'https://' + url
    : url;
  if (newtab) {
    var win = window.open(url);
    win.focus();
    return false;
  } else {
    window.location.href = url;
    return false;
  }
}

// Options
function saveOptions() {
  if (typeof(Storage) == "undefined") {
    alert("Browser does not support local storage: your settings won't be saved (sorry)");
  } else {
    // Get default command
    var radios = document.getElementById('defaultCommandForm');
    var defaultCommand = null;
    for (var i=0; i < radios.length; i++) {
      if (radios[i].checked) {
        defaultCommand = radios[i].value;
        break;
      }
    }
    defaultCommand = getFullCommand(defaultCommand);
    if (defaultCommand !== null) {
      SETTINGS.defaultCommand = defaultCommand;
    }

    // Tab open style
    SETTINGS.alwaysNewTab = document.getElementById('openStyleCheckbox');

    // Background color
    var color = document.getElementById('bgColorInput').value;
    if (!color.startsWith('#')) {
      color = '#' + color;
    }
    if (/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(color)) { // check if valid hex value
      SETTINGS.color = color;
      setColor(color);
      localStorage.setItem('userOptions', JSON.stringify(SETTINGS));
    } else {
      alert('not a valid hex value\n(valid example: #A1C0C0)');
    }
  }
  var inputBox = document.getElementById('input-box');
  inputBox.value = "";
  inputBox.select();
}

function loadOptions() {
  if (typeof(Storage) !== 'undefined') {
    if (localStorage.getItem('userOptions') !== null) {
      SETTINGS = JSON.parse(localStorage.getItem('userOptions'));
      setColor(SETTINGS.color);
    } else {
      var defaultSettings = {
        defaultCommand: {
          command: 'g',
          url: 'https://www.google.com',
          search: '/search?q='
        },
        alwaysNewtab: false,
        color: "#A1C0C0"
      };
      localStorage.setItem('userOptions', JSON.stringify(defaultSettings));
      SETTINGS = JSON.parse(localStorage.getItem('userOptions'));
    }
  }
}

function setColor(color) {
  document.body.style.backgroundColor = color;
}

// Displayed content
function clearMessage() {
  document.getElementById('message').innerHTML = '';
}

function displayMessage(message, timeMs) {
  clearMessage();
  document.getElementById('message').innerHTML = message;
  setTimeout(clearMessage, timeMs);
}

function clearContent() {
  document.getElementById('content').innerHTML = '';
}

function displayContent(content) {
  clearContent();
  document.getElementById('content').innerHTML = content;
}

function displayOptionsMenu() {
  var html = "\

 \

 \

 \
       Default Command
executes if no command was specified	 \

 \

 \
        \
          <form id='defaultCommandForm'> \
             <input type='radio' name='defaultCommandRadio' value='t'>Go to website
 \
             <input type='radio' name='defaultCommandRadio' value='g'>Google search
 \
             <input type='radio' name='defaultCommandRadio' value='w'>Wikipedia search
 \
             <input type='radio' name='defaultCommandRadio' value='y'>YouTube search
 \
             <input type='radio' name='defaultCommandRadio' value='a'>Amazon search
 \
             <input type='radio' name='defaultCommandRadio' value='imdb'>IMDB search
 \
             <input type='radio' name='defaultCommandRadio' value='img'>Google Images search
 \
             <input type='radio' name='defaultCommandRadio' value='wa'>Wolfram Alpha search
 \
             <input type='radio' name='defaultCommandRadio' value='r'>Go to subreddit \
          </form> \
       	 \

 \

 \
       Open Style	 \

 \

 \
        \
          <input type='checkbox' id='openStyleCheckbox' value='newTab'>Always open in new tab
 \
       	 \

 \

 \
       Background Color	 \

 \

 \
        \
          Color: <input type='text' id='bgColorInput' placeholder='#A1C0C0' size='7' spellcheck='false'> \
          Color Picker \
       	 \

 \

 \
        \
          <button type='button' id='saveOptionsBtn'>Done</button> \
       	 \

 \

";

  var wrapper = document.getElementById('wrapper');
  wrapper.style.top = "10%";

  displayContent(html);
  document.getElementById('saveOptionsBtn').onclick = function() {
    wrapper.style.top = "35%";
    saveOptions();
    clearContent();
    displayMessage('settings saved', 2000);
  };

  // Display saved options
  var radios = document.getElementById('defaultCommandForm');
  for (var i=0; i < radios.length; i++) {
    if (SETTINGS.defaultCommand.command === radios[i].value) {
      radios[i].checked = true;
      break;
    }
  }
  document.getElementById('openStyleCheckbox').checked = SETTINGS.alwaysNewTab;
  document.getElementById('bgColorInput').value = SETTINGS.color;
}

// Clock
function clock() {
  var date = new Date();
  var h = date.getHours();
  h = (h > 12) ? (checkTime(h -= 12)) : checkTime(h); // 12hr clock
  var m = checkTime(date.getMinutes());
  document.getElementById('clock').innerHTML = h + ':' + m;
  setTimeout(clock, 1000);
}
function checkTime(n) {
  n = (n < 10) ? (n = '0' + n) : n;
  return n;
}
<|endoftext|>


<commit_before>var NFA = function() {
    this.nodes = [];
    this.initialNode = null;
    var state = [];

    this.add = function(node, isInitial) {
        this.nodes.push(node);
        return node;
    };

    this.begin = function() {
        var initials = [];
        for (var i = 0; i < this.nodes.length; i++) {
            var node = this.nodes[i];
            if (node.isInitial) {
                initials.push(node);
            }
        }
        state = closure(initials);
    };

    this.consume = function(string) {
        for (var i = 0; i < string.length; i++) {
            consumeToken(this, string[i]);
        }
    };

    var consumeToken = function(self, token) {
        var newState = [];
        for (var i = 0; i < state.length; i++) {
            var s = state[i];
            for (var j = 0; j < s.edges.length; j++) {
                var edge = s.edges[j];
                if (edge.consumes(token) && newState.indexOf(edge.to) == -1) {
                    newState.push(edge.to);
                }
            }
        }

        if (newState.length == 0) {
            var evt = $.Event("not-accept");
            evt.token = token;
            $(self).triggerHandler(evt);
            if (evt.isDefaultPrevented()) {
                return;
            }
        }

        state = closure(newState);

        var evt = $.Event("consume");
        evt.token = token;
        $(self).triggerHandler(evt);
    };

    this.isFinal = function() {
        for (var i = 0; i < state.length; i++) {
            if (state[i].isFinal) {
                return true;
            }
        }
        return false;
    };

    this.clone = function() {
        var result = new NFA();
        for (var i = 0; i < this.nodes.length; i++) {
            var node = this.nodes[i];
            var nodeClone = result.add(new NFA.Node());
            nodeClone.isInitial = node.isInitial;
            nodeClone.isFinal = node.isFinal;
        }
        for (var i = 0; i < this.nodes.length; i++) {
            var node = this.nodes[i];
            for (var j = 0; j < node.edges.length; j++) {
                var edge = node.edges[j];
                result.nodes[i].link(result.nodes[this.nodes.indexOf(edge.to)], edge.value);
            }
        }
        return result;
    };

    this.follow = function() {
        var follow = [];
        for (var i = 0; i < state.length; i++) {
            var s = state[i];
            for (var j = 0; j < s.edges.length; j++) {
                var edge = s.edges[j];
                if (edge.value !== NFA.EPSILON && follow.indexOf(edge.value) == -1) {
                    follow.push(edge.value);
                }
            }
        }
        return follow;
    };

    var closure = function(nodes) {
        var changed;
        do {
            changed = false;
            for (var i = 0; i < nodes.length; i++) {
                var node = nodes[i];
                for (var j = 0; j < node.edges.length; j++) {
                    var edge = node.edges[j];
                    if (edge.value == NFA.EPSILON && nodes.indexOf(edge.to) == -1) {
                        nodes.push(edge.to);
                        changed = true;
                    }
                }
            }
        } while (changed);

        return nodes;
    };

    this.toString = function() {
        var nodesString = "";
        for (var i = 0; i < this.nodes.length; i++) {
            var node = this.nodes[i];
            nodesString += (nodesString.length == 0? "" : ", ") + nodeToString(this, node);
        }

        var edgesString = "";
        for (var i = 0; i < this.nodes.length; i++) {
            var node = this.nodes[i];
            for (var j = 0; j < node.edges.length; j++) {
                var edge = node.edges[j];
                edgesString += (edgesString.length == 0? "" : ", ") + nodeToString(this, node) + " -" + edge.value + "-> " + nodeToString(this, edge.to);
            }
        }

        return "NODES = {" + nodesString + "}, EDGES = {" + edgesString + "}";
    };

    var nodeToString = function(nfa, node) {
        return nfa.nodes.indexOf(node) + (node.isInitial? "i" : "") + (node.isFinal? "f" : "");
    };
};

NFA.EPSILON = null;

NFA.Node = function(kind) {
    this.edges = [];
    this.isInitial = (kind || {}).isInitial || false;
    this.isFinal = (kind || {}).isFinal || false;

    this.link = function(to, value) {
        this.edges.push(new NFA.Edge(this, to, value));
    };
};

NFA.Node.INITIAL = { isInitial: true };
NFA.Node.FINAL = { isFinal: true };
NFA.Node.INITIAL_AND_FINAL = { isInitial: true, isFinal: true };

NFA.Edge = function(from, to, value) {
    this.from = from;
    this.to = to;
    this.value = value;

    this.consumes = function(token) {
        if (this.value instanceof Function) {
            return this.value(token);
        } else {
            return this.value === token;
        }
    };
};

NFA.empty = function() {
    var result = new NFA();
    result.add(new NFA.Node(NFA.Node.INITIAL_AND_FINAL));
    return result;
};

NFA.forToken = function(token) {
    var result = new NFA();
    result.add(new NFA.Node(NFA.Node.INITIAL)).link(result.add(new NFA.Node(NFA.Node.FINAL)), token);
    return result;
};

NFA.concatenation = function() {
    if (arguments.length == 0) {
        return NFA.empty();
    } else {
        var result = arguments[0];

        var concatenate = function(other) {
            var finals = [];
            var initials = [];

            for (var i = 0; i < result.nodes.length; i++) {
                var node = result.nodes[i];
                if (node.isFinal) {
                    finals.push(node);
                    node.isFinal = false;
                }
            }

            for (var j = 0; j < other.nodes.length; j++) {
                var node = other.nodes[j];
                result.add(node);
                if (node.isInitial) {
                    initials.push(node);
                    node.isInitial = false;
                }
            }

            for (var i = 0; i < finals.length; i++) {
                var nodeFinal = finals[i];
                for (var j = 0; j < initials.length; j++) {
                    var nodeInitial = initials[j];
                    nodeFinal.link(nodeInitial, NFA.EPSILON);
                }
            }
        };

        for (var i = 1; i < arguments.length; i++) {
            concatenate(arguments[i]);
        }

        return result;
    }
};

NFA.union = function() {
    if (arguments.length == 0) {
        return NFA.empty();
    } else {
        var result = new NFA();
        var initial = result.add(new NFA.Node(NFA.Node.INITIAL));

        var unite = function(other) {
            for (var i = 0; i < other.nodes.length; i++) {
                var node = other.nodes[i];
                result.add(node);
                if (node.isInitial) {
                    initial.link(node, NFA.EPSILON);
                    node.isInitial = false;
                }
            }
        };

        for (var i = 0; i < arguments.length; i++) {
            unite(arguments[i]);
        }

        return result;
    }
};

NFA.kleene = function(nfa) {
    var initials = [];
    var finals = [];

    for (var i = 0; i < nfa.nodes.length; i++) {
        var node = nfa.nodes[i];
        if (node.isInitial) {
            initials.push(node);
            node.isInitial = false;
        }
        if (node.isFinal) {
            finals.push(node);
        }
    }

    var initial = nfa.add(new NFA.Node(NFA.Node.INITIAL_AND_FINAL));

    for (var i = 0; i < initials.length; i++) {
        initial.link(initials[i], NFA.EPSILON);
        for (var j = 0; j < finals.length; j++) {
            finals[j].link(initials[i], NFA.EPSILON);
        }
    }

    return nfa;
};

NFA.quantify = function(nfa, min, max) {
    var result = NFA.empty();

    for (var i = 0; i < min; i++) {
        result = NFA.concatenation(result, nfa.clone());
    }

    for (var j = min; j < max; j++) {
        var optional = NFA.union(NFA.empty(), nfa.clone());
        result = NFA.concatenation(result, optional);
    }

    return result;
};

var PatternParser = function(pattern) {
    var i = -1;
    var next = pattern[0];

    /*
     * union         = concatenation ("|" concatenation)*
     * concatenation = ("(" union ")" quantifier? / terminal quantifier?)*
     * quantifier    = "*" / "?" / "{" min "," max "}" / "{" exactly "}"
     * terminal      = "[:digit:]" / "[:alpha:]" / "[:alnum:]" / [^()[]{}*?|]
     */

    var isEof = function() {
        return i+1 >= pattern.length;
    };

    var peek = function(string) {
        if (string) {
            return pattern.substr(i+1, string.length) == string;
        } else {
            return pattern[i+1];
        }
    };
    var read = function(string) {
        for (var i = 0; i < string.length; i++) {
            readToken(string[i]);
        }
        return true;
    };
    var readToken = function(expect) {
        if (isEof() || expect && expect != peek()) {
            if (isEof()) {
                throw "Unexpected end of input (expected " + expect + ")";
            } else {
                throw "Unexpected symbol " + peek() + " at position " + (i+1) + " (expected " + expect + ")";
            }
        } else {
            return pattern[++i];
        }
    };
    var peekAndRead = function(string) {
        return peek(string) && read(string);
    };

    var union = function() {
        var nfa = concatenation();
        while (peekAndRead("|")) {
            nfa = NFA.union(nfa, concatenation());
        }
        return nfa;
    };

    var concatenation = function() {
        var nfa = NFA.empty();
        while (!isEof()) {
            var nfaToken;
            if (peekAndRead("(")) {
                nfaToken = union();
                read(")");
            } else if (peekAndRead("\\")) {
                if (escapes.indexOf(peek()) != -1) {
                    nfaToken = NFA.forToken(readToken());
                } else {
                    throw "Invalid escape sequence at position " + (i+1);
                }
            } else if (peekAndRead("[")) {
                nfaToken = charclass();
                read("]");
            } else if (peekAndRead(".")) {
                nfaToken = NFA.forToken(function(token) { return true; });
            } else if (peek(")") || peek("|")) {
                break;
            } else if (peekQuantifier()) {
                throw "Invalid quantifier at position " + (i+1);
            } else {
                nfaToken = NFA.forToken(readToken());
            }

            if (peekQuantifier()) {
                nfaToken = quantifier(nfaToken);
            }
            nfa = NFA.concatenation(nfa, nfaToken);
        }
        return nfa;
    };

    var peekQuantifier = function() {
        if (peek("*") || peek("+") || peek("?")) {
            return true;
        } else if (peek("{")) {
            return pattern.substr(i+1).search(/\{\d+(,\d+)?\}/) == 0;
        } else {
            return false;
        }
    };

    var quantifier = function(nfa) {
        if (peekAndRead("*")) {
            return NFA.kleene(nfa);
        } else if (peekAndRead("+")) {
            return NFA.concatenation(nfa, NFA.kleene(nfa.clone()));
        } else if (peekAndRead("?")) {
            return NFA.quantify(nfa, 0, 1);
        } else if (peekAndRead("{")) {
            var min = integer();
            var max = min;
            if (peekAndRead(",")) {
                max = integer();
            }
            read("}");
            return NFA.quantify(nfa, min, max);
        } else {
            throw "Invalid quantifier at position " + (i+1);
        }
    };

    var charclass = function() {
        if (peekAndRead(":digit:")) {
            return NFA.forToken(function(token) { return /[0-9]/.test(token); });
        } else if (peekAndRead(":alpha:")) {
            return NFA.forToken(function(token) { return /[a-zA-Z]/.test(token); });
        } else if (peekAndRead(":alnum:")) {
            return NFA.forToken(function(token) { return /[a-zA-Z0-9]/.test(token); });
        } else {
            throw "Invalid character class at position " + (i+1);
        }
    };

    var integer = function() {
        var string = "";
        while (/[0-9]/.test(peek())) {
            string = string + readToken();
        }
        return Number(string);
    };

    var escapes = ["(", ")", "[", "]", "{", "}", "*", "+", "?", "|", "\\", "."];

    this.nfa = union();
    if (!isEof()) {
        throw "Expected end of input at position " + (i+1) + " (found " + peek() + ")";
    }

    this.nfa.begin();
};

/* vim: set et ts=4 sw=4: */
<commit_msg>Rename the not-accept event to error<commit_after>var NFA = function() {
    this.nodes = [];
    this.initialNode = null;
    var state = [];

    this.add = function(node, isInitial) {
        this.nodes.push(node);
        return node;
    };

    this.begin = function() {
        var initials = [];
        for (var i = 0; i < this.nodes.length; i++) {
            var node = this.nodes[i];
            if (node.isInitial) {
                initials.push(node);
            }
        }
        state = closure(initials);
    };

    this.consume = function(string) {
        for (var i = 0; i < string.length; i++) {
            consumeToken(this, string[i]);
        }
    };

    var consumeToken = function(self, token) {
        var newState = [];
        for (var i = 0; i < state.length; i++) {
            var s = state[i];
            for (var j = 0; j < s.edges.length; j++) {
                var edge = s.edges[j];
                if (edge.consumes(token) && newState.indexOf(edge.to) == -1) {
                    newState.push(edge.to);
                }
            }
        }

        if (newState.length == 0) {
            var evt = $.Event("error");
            evt.token = token;
            $(self).triggerHandler(evt);
            if (evt.isDefaultPrevented()) {
                return;
            }
        }

        state = closure(newState);

        var evt = $.Event("consume");
        evt.token = token;
        $(self).triggerHandler(evt);
    };

    this.isFinal = function() {
        for (var i = 0; i < state.length; i++) {
            if (state[i].isFinal) {
                return true;
            }
        }
        return false;
    };

    this.isError = function() {
        return state.length == 0;
    };

    this.clone = function() {
        var result = new NFA();
        for (var i = 0; i < this.nodes.length; i++) {
            var node = this.nodes[i];
            var nodeClone = result.add(new NFA.Node());
            nodeClone.isInitial = node.isInitial;
            nodeClone.isFinal = node.isFinal;
        }
        for (var i = 0; i < this.nodes.length; i++) {
            var node = this.nodes[i];
            for (var j = 0; j < node.edges.length; j++) {
                var edge = node.edges[j];
                result.nodes[i].link(result.nodes[this.nodes.indexOf(edge.to)], edge.value);
            }
        }
        return result;
    };

    this.follow = function() {
        var follow = [];
        for (var i = 0; i < state.length; i++) {
            var s = state[i];
            for (var j = 0; j < s.edges.length; j++) {
                var edge = s.edges[j];
                if (edge.value !== NFA.EPSILON && follow.indexOf(edge.value) == -1) {
                    follow.push(edge.value);
                }
            }
        }
        return follow;
    };

    var closure = function(nodes) {
        var changed;
        do {
            changed = false;
            for (var i = 0; i < nodes.length; i++) {
                var node = nodes[i];
                for (var j = 0; j < node.edges.length; j++) {
                    var edge = node.edges[j];
                    if (edge.value == NFA.EPSILON && nodes.indexOf(edge.to) == -1) {
                        nodes.push(edge.to);
                        changed = true;
                    }
                }
            }
        } while (changed);

        return nodes;
    };

    this.toString = function() {
        var nodesString = "";
        for (var i = 0; i < this.nodes.length; i++) {
            var node = this.nodes[i];
            nodesString += (nodesString.length == 0? "" : ", ") + nodeToString(this, node);
        }

        var edgesString = "";
        for (var i = 0; i < this.nodes.length; i++) {
            var node = this.nodes[i];
            for (var j = 0; j < node.edges.length; j++) {
                var edge = node.edges[j];
                edgesString += (edgesString.length == 0? "" : ", ") + nodeToString(this, node) + " -" + edge.value + "-> " + nodeToString(this, edge.to);
            }
        }

        return "NODES = {" + nodesString + "}, EDGES = {" + edgesString + "}";
    };

    var nodeToString = function(nfa, node) {
        return nfa.nodes.indexOf(node) + (node.isInitial? "i" : "") + (node.isFinal? "f" : "");
    };
};

NFA.EPSILON = null;

NFA.Node = function(kind) {
    this.edges = [];
    this.isInitial = (kind || {}).isInitial || false;
    this.isFinal = (kind || {}).isFinal || false;

    this.link = function(to, value) {
        this.edges.push(new NFA.Edge(this, to, value));
    };
};

NFA.Node.INITIAL = { isInitial: true };
NFA.Node.FINAL = { isFinal: true };
NFA.Node.INITIAL_AND_FINAL = { isInitial: true, isFinal: true };

NFA.Edge = function(from, to, value) {
    this.from = from;
    this.to = to;
    this.value = value;

    this.consumes = function(token) {
        if (this.value instanceof Function) {
            return this.value(token);
        } else {
            return this.value === token;
        }
    };
};

NFA.empty = function() {
    var result = new NFA();
    result.add(new NFA.Node(NFA.Node.INITIAL_AND_FINAL));
    return result;
};

NFA.forToken = function(token) {
    var result = new NFA();
    result.add(new NFA.Node(NFA.Node.INITIAL)).link(result.add(new NFA.Node(NFA.Node.FINAL)), token);
    return result;
};

NFA.concatenation = function() {
    if (arguments.length == 0) {
        return NFA.empty();
    } else {
        var result = arguments[0];

        var concatenate = function(other) {
            var finals = [];
            var initials = [];

            for (var i = 0; i < result.nodes.length; i++) {
                var node = result.nodes[i];
                if (node.isFinal) {
                    finals.push(node);
                    node.isFinal = false;
                }
            }

            for (var j = 0; j < other.nodes.length; j++) {
                var node = other.nodes[j];
                result.add(node);
                if (node.isInitial) {
                    initials.push(node);
                    node.isInitial = false;
                }
            }

            for (var i = 0; i < finals.length; i++) {
                var nodeFinal = finals[i];
                for (var j = 0; j < initials.length; j++) {
                    var nodeInitial = initials[j];
                    nodeFinal.link(nodeInitial, NFA.EPSILON);
                }
            }
        };

        for (var i = 1; i < arguments.length; i++) {
            concatenate(arguments[i]);
        }

        return result;
    }
};

NFA.union = function() {
    if (arguments.length == 0) {
        return NFA.empty();
    } else {
        var result = new NFA();
        var initial = result.add(new NFA.Node(NFA.Node.INITIAL));

        var unite = function(other) {
            for (var i = 0; i < other.nodes.length; i++) {
                var node = other.nodes[i];
                result.add(node);
                if (node.isInitial) {
                    initial.link(node, NFA.EPSILON);
                    node.isInitial = false;
                }
            }
        };

        for (var i = 0; i < arguments.length; i++) {
            unite(arguments[i]);
        }

        return result;
    }
};

NFA.kleene = function(nfa) {
    var initials = [];
    var finals = [];

    for (var i = 0; i < nfa.nodes.length; i++) {
        var node = nfa.nodes[i];
        if (node.isInitial) {
            initials.push(node);
            node.isInitial = false;
        }
        if (node.isFinal) {
            finals.push(node);
        }
    }

    var initial = nfa.add(new NFA.Node(NFA.Node.INITIAL_AND_FINAL));

    for (var i = 0; i < initials.length; i++) {
        initial.link(initials[i], NFA.EPSILON);
        for (var j = 0; j < finals.length; j++) {
            finals[j].link(initials[i], NFA.EPSILON);
        }
    }

    return nfa;
};

NFA.quantify = function(nfa, min, max) {
    var result = NFA.empty();

    for (var i = 0; i < min; i++) {
        result = NFA.concatenation(result, nfa.clone());
    }

    for (var j = min; j < max; j++) {
        var optional = NFA.union(NFA.empty(), nfa.clone());
        result = NFA.concatenation(result, optional);
    }

    return result;
};

var PatternParser = function(pattern) {
    var i = -1;
    var next = pattern[0];

    /*
     * union         = concatenation ("|" concatenation)*
     * concatenation = ("(" union ")" quantifier? / terminal quantifier?)*
     * quantifier    = "*" / "?" / "{" min "," max "}" / "{" exactly "}"
     * terminal      = "[:digit:]" / "[:alpha:]" / "[:alnum:]" / [^()[]{}*?|]
     */

    var isEof = function() {
        return i+1 >= pattern.length;
    };

    var peek = function(string) {
        if (string) {
            return pattern.substr(i+1, string.length) == string;
        } else {
            return pattern[i+1];
        }
    };
    var read = function(string) {
        for (var i = 0; i < string.length; i++) {
            readToken(string[i]);
        }
        return true;
    };
    var readToken = function(expect) {
        if (isEof() || expect && expect != peek()) {
            if (isEof()) {
                throw "Unexpected end of input (expected " + expect + ")";
            } else {
                throw "Unexpected symbol " + peek() + " at position " + (i+1) + " (expected " + expect + ")";
            }
        } else {
            return pattern[++i];
        }
    };
    var peekAndRead = function(string) {
        return peek(string) && read(string);
    };

    var union = function() {
        var nfa = concatenation();
        while (peekAndRead("|")) {
            nfa = NFA.union(nfa, concatenation());
        }
        return nfa;
    };

    var concatenation = function() {
        var nfa = NFA.empty();
        while (!isEof()) {
            var nfaToken;
            if (peekAndRead("(")) {
                nfaToken = union();
                read(")");
            } else if (peekAndRead("\\")) {
                if (escapes.indexOf(peek()) != -1) {
                    nfaToken = NFA.forToken(readToken());
                } else {
                    throw "Invalid escape sequence at position " + (i+1);
                }
            } else if (peekAndRead("[")) {
                nfaToken = charclass();
                read("]");
            } else if (peekAndRead(".")) {
                nfaToken = NFA.forToken(function(token) { return true; });
            } else if (peek(")") || peek("|")) {
                break;
            } else if (peekQuantifier()) {
                throw "Invalid quantifier at position " + (i+1);
            } else {
                nfaToken = NFA.forToken(readToken());
            }

            if (peekQuantifier()) {
                nfaToken = quantifier(nfaToken);
            }
            nfa = NFA.concatenation(nfa, nfaToken);
        }
        return nfa;
    };

    var peekQuantifier = function() {
        if (peek("*") || peek("+") || peek("?")) {
            return true;
        } else if (peek("{")) {
            return pattern.substr(i+1).search(/\{\d+(,\d+)?\}/) == 0;
        } else {
            return false;
        }
    };

    var quantifier = function(nfa) {
        if (peekAndRead("*")) {
            return NFA.kleene(nfa);
        } else if (peekAndRead("+")) {
            return NFA.concatenation(nfa, NFA.kleene(nfa.clone()));
        } else if (peekAndRead("?")) {
            return NFA.quantify(nfa, 0, 1);
        } else if (peekAndRead("{")) {
            var min = integer();
            var max = min;
            if (peekAndRead(",")) {
                max = integer();
            }
            read("}");
            return NFA.quantify(nfa, min, max);
        } else {
            throw "Invalid quantifier at position " + (i+1);
        }
    };

    var charclass = function() {
        if (peekAndRead(":digit:")) {
            return NFA.forToken(function(token) { return /[0-9]/.test(token); });
        } else if (peekAndRead(":alpha:")) {
            return NFA.forToken(function(token) { return /[a-zA-Z]/.test(token); });
        } else if (peekAndRead(":alnum:")) {
            return NFA.forToken(function(token) { return /[a-zA-Z0-9]/.test(token); });
        } else {
            throw "Invalid character class at position " + (i+1);
        }
    };

    var integer = function() {
        var string = "";
        while (/[0-9]/.test(peek())) {
            string = string + readToken();
        }
        return Number(string);
    };

    var escapes = ["(", ")", "[", "]", "{", "}", "*", "+", "?", "|", "\\", "."];

    this.nfa = union();
    if (!isEof()) {
        throw "Expected end of input at position " + (i+1) + " (found " + peek() + ")";
    }

    this.nfa.begin();
};

/* vim: set et ts=4 sw=4: */
<|endoftext|>


<commit_before>/**
 * yayax (c) Mikhail Bogdanov 2015
 * based on https://github.com/radiosilence/xr
 * URL: https://github.com/gm0t/yajax
 * License: MIT
 */

var Events = {
  READY_STATE_CHANGE: "readystatechange",
  LOAD_START: "loadstart",
  PROGRESS: "progress",
  ABORT: "abort",
  ERROR: "error",
  LOAD: "load",
  TIMEOUT: "timeout",
  LOAD_END: "loadend"
};

var defaults = {
  method: "GET",
  data: undefined,
  headers: {
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  dataEncoder: JSON.stringify,
  responseDecoder: JSON.parse,
  xmlHttpRequest: function () {
    return new XMLHttpRequest();
  },
  promise: function (fn) {
    return new Promise(fn);
  }
};

var interceptors = {
  send: [],
  complete: [],
  error: []
}

function onError(xhr, opts, reject) {
  // onComplete interceptors
  (interceptors.error || []).forEach(function onErrorInterceptor(fn) {
    fn(xhr);
  });

  reject({
    status: xhr.status,
    response: xhr.response,
    xhr: xhr
  });
}

function stringify(params) {
  params = params || {};

  var k, result = "", sep = "";
  for (k in params) {
    if (params.hasOwnProperty(k)) {
      result += sep + encodeURIComponent(k) + "=" + encodeURIComponent(params[k]);
      sep = "&";
    }
  }

  return result;
}

var config = {};

function configure(opts) {
  config = Object.assign({}, config, opts);
}

function createPromise(args, fn) {
  return ((args && args.promise)
      ? args.promise
      : (config.promise || defaults.promise)
  )(fn);
}

function yajax(args) {
  var xhr;
  var promise = createPromise(args, function(resolve, reject) {
    var opts = Object.assign({}, defaults, config, args);

    xhr = opts.xmlHttpRequest();
    var failed = onError.bind(null, xhr, opts, reject)

    if (opts.withCredentials) {
      xhr.withCredentials = true;
    }

    var url = opts.url;
    if (opts.params) {
      url = url.split("?")[0] + "?" + stringify(opts.params);
    }
    xhr.open(opts.method, url, true);

    xhr.addEventListener(Events.LOAD, function () {
      if (xhr.status < 200 || xhr.status >= 300) {
        return failed();
      }

      // onComplete interceptors
      (interceptors.complete || []).forEach(function onCompleteInterceptor(fn) {
        fn(xhr, opts);
      });

      var data;
      if (!xhr.responseType || xhr.responseType === "text") {
        data = xhr.responseText;
      } else {
        data = xhr.response;
      }

      if (data && !opts.raw) {
        data = opts.responseDecoder(data);
      }
      resolve(data, xhr);
    });

    xhr.addEventListener(Events.ABORT, failed);
    xhr.addEventListener(Events.ERROR, failed);
    xhr.addEventListener(Events.TIMEOUT, failed);

    var k, headers = opts.headers;
    for (k in headers) {
      if (headers.hasOwnProperty(k)) {
        xhr.setRequestHeader(k, headers[k]);
      }
    }

    var events = opts.events;
    for (k in events) {
      if (events.hasOwnProperty(k)) {
        xhr.addEventListener(k, events[k].bind(null, xhr), false);
      }
    }

    if (opts.timeout !== void 0) {
        xhr.timeout = opts.timeout;
    }

    // onSend interceptors
    (interceptors.send || []).forEach(function onSendInterceptor(fn) {
      fn(xhr, opts);
    });

    var data = opts.data;
    if (typeof opts.data === "object" && !opts.raw) {
      data = opts.dataEncoder(data, xhr);
    }

    if (xhr.readyState !== 1) {
      //one of the intercepters has called abort
      return failed();
    }

    if (opts.responseType) {
        xhr.responseType = opts.responseType;
    }

    xhr.send(data);
  });

  promise.abort = function () {
    return xhr.abort();
  }

  return promise;
}

yajax.configure = configure;
yajax.Events = Events;
yajax.defaults = defaults;

yajax.get = function (url, params, args) {
  return yajax(Object.assign({url: url, method: "GET", params: params}, args));
};
yajax.put = function (url, data, args) {
  return yajax(Object.assign({url: url, method: "PUT", data: data}, args));
};
yajax.post = function (url, data, args) {
  return yajax(Object.assign({url: url, method: "POST", data: data}, args));
};
yajax.patch = function (url, data, args) {
  return yajax(Object.assign({url: url, method: "PATCH", data: data}, args));
};
yajax.del = function (url, args) {
  return yajax(Object.assign({url: url, method: "DELETE"}, args));
};
yajax.options = function (url, args) {
  return yajax(Object.assign({url: url, method: "OPTIONS"}, args));
};

yajax.addInterceptor = function addInterceptor(type, fn, asFirst) {
  var list = interceptors[type];
  if (!list) {
    throw new Error("Unknown interceptor type: " + type + " available: send, complete");
  }

  if (asFirst) {
    list.unshift(fn);
  }  else {
    list.push(fn);
  }
};

yajax.removeInterceptor = function removeInterceptor(type, fn) {
  var list = interceptors[type];
  if (!list) {
    throw new Error("Unknown interceptor type: " + type + " available: send, complete");
  }

  var idx = list.indexOf(fn);
  if (idx !== -1) {
    list.splice(idx, 1);
    return true;
  }

  return false;
}

module.exports = yajax;
<commit_msg>Added try/catch around responseDecoder<commit_after>/**
 * yayax (c) Mikhail Bogdanov 2015
 * based on https://github.com/radiosilence/xr
 * URL: https://github.com/gm0t/yajax
 * License: MIT
 */

var Events = {
  READY_STATE_CHANGE: "readystatechange",
  LOAD_START: "loadstart",
  PROGRESS: "progress",
  ABORT: "abort",
  ERROR: "error",
  LOAD: "load",
  TIMEOUT: "timeout",
  LOAD_END: "loadend"
};

var defaults = {
  method: "GET",
  data: undefined,
  headers: {
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  dataEncoder: JSON.stringify,
  responseDecoder: JSON.parse,
  xmlHttpRequest: function () {
    return new XMLHttpRequest();
  },
  promise: function (fn) {
    return new Promise(fn);
  }
};

var interceptors = {
  send: [],
  complete: [],
  error: []
}

function onError(xhr, opts, reject) {
  // onComplete interceptors
  (interceptors.error || []).forEach(function onErrorInterceptor(fn) {
    fn(xhr);
  });

  reject({
    status: xhr.status,
    response: xhr.response,
    xhr: xhr
  });
}

function stringify(params) {
  params = params || {};

  var k, result = "", sep = "";
  for (k in params) {
    if (params.hasOwnProperty(k)) {
      result += sep + encodeURIComponent(k) + "=" + encodeURIComponent(params[k]);
      sep = "&";
    }
  }

  return result;
}

var config = {};

function configure(opts) {
  config = Object.assign({}, config, opts);
}

function createPromise(args, fn) {
  return ((args && args.promise)
      ? args.promise
      : (config.promise || defaults.promise)
  )(fn);
}

function yajax(args) {
  var xhr;
  var promise = createPromise(args, function(resolve, reject) {
    var opts = Object.assign({}, defaults, config, args);

    xhr = opts.xmlHttpRequest();
    var failed = onError.bind(null, xhr, opts, reject)

    if (opts.withCredentials) {
      xhr.withCredentials = true;
    }

    var url = opts.url;
    if (opts.params) {
      url = url.split("?")[0] + "?" + stringify(opts.params);
    }
    xhr.open(opts.method, url, true);

    xhr.addEventListener(Events.LOAD, function () {
      if (xhr.status < 200 || xhr.status >= 300) {
        return failed();
      }

      // onComplete interceptors
      (interceptors.complete || []).forEach(function onCompleteInterceptor(fn) {
        fn(xhr, opts);
      });

      var data;
      if (!xhr.responseType || xhr.responseType === "text") {
        data = xhr.responseText;
      } else {
        data = xhr.response;
      }

      if (data && !opts.raw) {
        try {
          data = opts.responseDecoder(data);
        } catch (e) {
          return failed(e);
        }
      }
      resolve(data, xhr);
    });

    xhr.addEventListener(Events.ABORT, failed);
    xhr.addEventListener(Events.ERROR, failed);
    xhr.addEventListener(Events.TIMEOUT, failed);

    var k, headers = opts.headers;
    for (k in headers) {
      if (headers.hasOwnProperty(k)) {
        xhr.setRequestHeader(k, headers[k]);
      }
    }

    var events = opts.events;
    for (k in events) {
      if (events.hasOwnProperty(k)) {
        xhr.addEventListener(k, events[k].bind(null, xhr), false);
      }
    }

    if (opts.timeout !== void 0) {
        xhr.timeout = opts.timeout;
    }

    // onSend interceptors
    (interceptors.send || []).forEach(function onSendInterceptor(fn) {
      fn(xhr, opts);
    });

    var data = opts.data;
    if (typeof opts.data === "object" && !opts.raw) {
      data = opts.dataEncoder(data, xhr);
    }

    if (xhr.readyState !== 1) {
      //one of the intercepters has called abort
      return failed();
    }

    if (opts.responseType) {
        xhr.responseType = opts.responseType;
    }

    xhr.send(data);
  });

  promise.abort = function () {
    return xhr.abort();
  }

  return promise;
}

yajax.configure = configure;
yajax.Events = Events;
yajax.defaults = defaults;

yajax.get = function (url, params, args) {
  return yajax(Object.assign({url: url, method: "GET", params: params}, args));
};
yajax.put = function (url, data, args) {
  return yajax(Object.assign({url: url, method: "PUT", data: data}, args));
};
yajax.post = function (url, data, args) {
  return yajax(Object.assign({url: url, method: "POST", data: data}, args));
};
yajax.patch = function (url, data, args) {
  return yajax(Object.assign({url: url, method: "PATCH", data: data}, args));
};
yajax.del = function (url, args) {
  return yajax(Object.assign({url: url, method: "DELETE"}, args));
};
yajax.options = function (url, args) {
  return yajax(Object.assign({url: url, method: "OPTIONS"}, args));
};

yajax.addInterceptor = function addInterceptor(type, fn, asFirst) {
  var list = interceptors[type];
  if (!list) {
    throw new Error("Unknown interceptor type: " + type + " available: send, complete");
  }

  if (asFirst) {
    list.unshift(fn);
  }  else {
    list.push(fn);
  }
};

yajax.removeInterceptor = function removeInterceptor(type, fn) {
  var list = interceptors[type];
  if (!list) {
    throw new Error("Unknown interceptor type: " + type + " available: send, complete");
  }

  var idx = list.indexOf(fn);
  if (idx !== -1) {
    list.splice(idx, 1);
    return true;
  }

  return false;
}

module.exports = yajax;
<|endoftext|>


<commit_before>'use strict';

/* Load Environment Variables */
require("dotenv-safe").config({
    path: `./config/.env.${process.env.NODE_ENV || 'development'}`,
    sample: "./config/.env.requirements",
    silent: true
});

// Load dependencies
require("./lib/root-require")();
const Logger = rootRequire('lib/logger');
const config = rootRequire('config');

let completedJobs = 0;
Logger.info('Master process loaded');

// Load job queue
const kue = require('kue');
const Queue = kue.createQueue(config.JobQueue);

// Load job queue web interface
kue.app.set('title', 'Grail Crawler Dashboard');
kue.app.listen(config.Dashboard.port);

// Thread shutdown procedure
function shutdownProcedure() {
  Logger.info('Queue shutting down');
  Queue.shutdown(5000, (error) => {
    if (error) {
      Logger.error('Queue shutdown error: ', error);
    }
    process.exit(0);
  });
}

// Process failure events
process.on('SIGTERM', shutdownProcedure);
process.on('SIGINT', shutdownProcedure);

// Log queue level events
Queue.on('job complete', (id, result) => {
  // if (result) {
  //   Logger.info(`Job ${id} Result: ${JSON.stringify(result)}`);
  // }
  Logger.info('Completed job: %d, Total completed: %d', id, ++completedJobs);
});

Queue.on('job failed', (id, result) => {
  Logger.error('Job: ', id, ' failed with result: ', result);
});

Queue.on('error', (error) => {
  Logger.error('Queue error: ', error);
});<commit_msg>Change: Style logging for Job Complete event<commit_after>'use strict';

/* Load Environment Variables */
require("dotenv-safe").config({
    path: `./config/.env.${process.env.NODE_ENV || 'development'}`,
    sample: "./config/.env.requirements",
    silent: true
});

// Load dependencies
require("./lib/root-require")();
const Logger = rootRequire('lib/logger');
const config = rootRequire('config');

let completedJobs = 0;
Logger.info('Master process loaded');

// Load job queue
const kue = require('kue');
const Queue = kue.createQueue(config.JobQueue);

// Load job queue web interface
kue.app.set('title', 'Grail Crawler Dashboard');
kue.app.listen(config.Dashboard.port);

// Thread shutdown procedure
function shutdownProcedure() {
  Logger.info('Queue shutting down');
  Queue.shutdown(5000, (error) => {
    if (error) {
      Logger.error('Queue shutdown error: ', error);
    }
    process.exit(0);
  });
}

// Process failure events
process.on('SIGTERM', shutdownProcedure);
process.on('SIGINT', shutdownProcedure);

// Log queue level events
Queue.on('job complete', (id, result) => {
  if (result) {
    return Logger.info(`Job ${id} Result: ${JSON.stringify(result)}`);
  }
  return Logger.info('Completed job: %d, Total completed: %d', id, ++completedJobs);
});

Queue.on('job failed', (id, result) => {
  Logger.error('Job: ', id, ' failed with result: ', result);
});

Queue.on('error', (error) => {
  Logger.error('Queue error: ', error);
});<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();
var revision = require('git-rev');

var sqlite3 = require('sqlite3').verbose();
var db = new sqlite3.Database('/home/pi/homeauto/backend/temp.db');

/* GET home page. */
router.get('/', function(req, res, next) {
  revision.short(function(short)) {
    res.render('index', { title: 'Temperature', page: 'home' , commit: short });
  });
});

router.get('/temperature', function(req, res, next) {
  revision.short(function(short)) {
    res.render('temperature', { title: 'Temperature', page: 'temperature', commit: short });
  });
});

router.get('/sensorstream', function(req, res, next) {
  revision.short(function(short)) {
    res.render('sensorstream', { title: 'Sensorstream', page: 'sensorstream', commit: short });
  });
});

module.exports = router;
<commit_msg>Fix typo<commit_after>var express = require('express');
var router = express.Router();
var revision = require('git-rev');

var sqlite3 = require('sqlite3').verbose();
var db = new sqlite3.Database('/home/pi/homeauto/backend/temp.db');

/* GET home page. */
router.get('/', function(req, res, next) {
  revision.short(function(short) {
    res.render('index', { title: 'Temperature', page: 'home' , commit: short });
  });
});

router.get('/temperature', function(req, res, next) {
  revision.short(function(short) {
    res.render('temperature', { title: 'Temperature', page: 'temperature', commit: short });
  });
});

router.get('/sensorstream', function(req, res, next) {
  revision.short(function(short) {
    res.render('sensorstream', { title: 'Sensorstream', page: 'sensorstream', commit: short });
  });
});

module.exports = router;
<|endoftext|>


<commit_before>import React, { Component } from 'react'

export default class Head extends Component {
  render() {
    const { head } = this.props
    return (

        <Title>{head.title}</Title>

    )
  }
}

Head.proptTypes = {
  head: React.PropTypes.shape({
    title: React.PropTypes.string.isRequired,

    logo: React.PropTypes.string,
    subtitle: React.PropTypes.string,
    description: React.PropTypes.string,
  }).isRequired
}

class Title extends Component {
  render() {
    return
{this.props.children}

  }
}
<commit_msg>Initial draft for subtitle v title<commit_after>import React, { Component } from 'react'

export default class Head extends Component {
  render() {
    const { head } = this.props
    return (

        <Title>{head.title}</Title>
        {head.subtitle?
          <SubTitle>{head.subtitle}</SubTitle>: null
        }

    )
  }
}

Head.proptTypes = {
  head: React.PropTypes.shape({
    title: React.PropTypes.string.isRequired,

    logo: React.PropTypes.string,
    subtitle: React.PropTypes.string,
    description: React.PropTypes.string,
  }).isRequired
}

class Title extends Component {
  render() {
    return (

        {this.props.children.toUpperCase()}

    )
  }
}

class SubTitle extends Component {
  render () {
    return (

          {this.props.children.toLowerCase()}

    )
  }
}
<|endoftext|>


<commit_before>import React from 'react';
import cx from 'classnames';
import { skinnable, props, t } from '../utils';
import FlexView from '../flex/FlexView';
import Icon from '../Icon/Icon';

import './accordion.scss';

@skinnable()
@props({
  children: t.ReactChildren,
  isOpen: t.Boolean,
  isSelected: t.maybe(t.Boolean),
  onChange: t.Function,
  onOpen: t.maybe(t.Function),
  onClose: t.maybe(t.Function),
  content: t.maybe(t.ReactChild),
  icons: t.maybe(t.struct({
    open: t.maybe(t.String),
    closed: t.maybe(t.String)
  })),
  className: t.maybe(t.String),
  id: t.maybe(t.String),
  style: t.maybe(t.Object)
})
export default class Accordion extends React.Component {

  static defaultProps = {
    onOpen: () => {},
    onClose: () => {}
  };

  _onChange = () => {
    const { onChange, onOpen, onClose, isOpen } = this.props;
    onChange(!isOpen);
    if (isOpen) {
      onClose();
    } else {
      onOpen();
    }
  };

  getLocals() {
    const {
      props: {
        isOpen, isSelected,
        children, content, icons,
        className, id, style
      },
      _onChange: onChange
    } = this;

    return {
      isOpen,
      children,
      content,
      icons,
      onChange,
      wrapperProps: {
        id,
        style,
        className: cx(className, 'accordion', { 'is-open': isOpen, 'is-selected': isSelected })
      }
    };
  }

  templateIcons = ({ isOpen, icons }) => (
    icons && <Icon icon={isOpen ? icons.open : icons.closed} />
  );

  template({ isOpen, children, onChange, content, icons, wrapperProps }) {
    return (
      <div {...wrapperProps}>
        <FlexView className='content' vAlignContent='center' onClick={onChange}>
          {content}
          <FlexView marginLeft='auto' shrink={false}>
            {this.templateIcons({ isOpen, icons })}
          </FlexView>
        </FlexView>
        {isOpen && children}
      </div>
    );
  }

}
<commit_msg>replace `content` with `header`<commit_after>import React from 'react';
import cx from 'classnames';
import { skinnable, props, t } from '../utils';
import FlexView from '../flex/FlexView';
import Icon from '../Icon/Icon';

import './accordion.scss';

@skinnable()
@props({
  children: t.ReactChildren,
  isOpen: t.Boolean,
  isSelected: t.maybe(t.Boolean),
  onChange: t.Function,
  onOpen: t.maybe(t.Function),
  onClose: t.maybe(t.Function),
  header: t.maybe(t.ReactChild),
  icons: t.maybe(t.struct({
    open: t.maybe(t.String),
    closed: t.maybe(t.String)
  })),
  className: t.maybe(t.String),
  id: t.maybe(t.String),
  style: t.maybe(t.Object)
})
export default class Accordion extends React.Component {

  static defaultProps = {
    onOpen: () => {},
    onClose: () => {}
  };

  _onChange = () => {
    const { onChange, onOpen, onClose, isOpen } = this.props;
    onChange(!isOpen);
    if (isOpen) {
      onClose();
    } else {
      onOpen();
    }
  };

  getLocals() {
    const {
      props: {
        isOpen, isSelected,
        children, header, icons,
        className, id, style
      },
      _onChange: onChange
    } = this;

    return {
      isOpen,
      children,
      header,
      icons,
      onChange,
      wrapperProps: {
        id,
        style,
        className: cx(className, 'accordion', { 'is-open': isOpen, 'is-selected': isSelected })
      }
    };
  }

  templateIcons = ({ isOpen, icons }) => (
    icons && <Icon icon={isOpen ? icons.open : icons.closed} />
  );

  template({ isOpen, children, onChange, header, icons, wrapperProps }) {
    return (
      <div {...wrapperProps}>
        <FlexView className='content' vAlignContent='center' onClick={onChange}>
          {header}
          <FlexView marginLeft='auto' shrink={false}>
            {this.templateIcons({ isOpen, icons })}
          </FlexView>
        </FlexView>
        {isOpen && children}
      </div>
    );
  }

}
<|endoftext|>


<commit_before>import { normalizeArray } from './-utils';

export default normalizeArray({}, (array, key) => {
  if (key === undefined) {
    return array;
  }
  return array.uniqBy(key);
});
<commit_msg>polyfill uniqBy for older ember versions<commit_after>import { normalizeArray } from './-utils';
import { A as emberA } from 'ember-array/utils';
import get from 'ember-metal/get';
import { guidFor } from 'ember-metal/utils';

export default normalizeArray({}, (array, key) => {
  if (key === undefined) {
    return array;
  }
  if (!array.uniqBy) {
    // TODO: polyfill this
    // from https://github.com/emberjs/ember.js/blob/v2.11.0/packages/ember-runtime/lib/mixins/enumerable.js#L1094-L1105
    let ret = emberA();
    let seen = Object.create(null);

    array.forEach(item => {
      let guid = guidFor(get(item, key));
      if (!(guid in seen)) {
        seen[guid] = true;
        ret.push(item);
      }
    });

    return ret;
  }
  return array.uniqBy(key);
});
<|endoftext|>