mozilla/webmaker.org

View on GitHub
public/js/angular/directives.js

Summary

Maintainability
F
6 days
Test Coverage
angular
  .module('webmakerApp')
  .directive('wmAffix', ['$window', '$timeout',
    function ($window, $timeout) {
      return {
        restrict: 'EA',
        link: function (scope, el, attrs) {
          var elHeight = attrs.offsetHeight;
          var elTop = attrs.offsetTop;

          $window.addEventListener('scroll', function () {
            var scrollTop = $(this).scrollTop();
            if (scrollTop >= elTop) {
              $('body').css('padding-top', elHeight);
              el.addClass('navbar-affix-top');
              scope.affixed = true;
              scope.$apply();
            } else {
              $('body').css('padding-top', 0);
              el.removeClass('navbar-affix-top');
              scope.affixed = false;
              scope.$apply();
            }
          }, false);
        }
      };
    }
  ])
  .directive('a', [
    '$location',
    '$anchorScroll',
    '$route',
    function ($location, $anchorScroll, $route) {
      return {
        restrict: 'E',
        link: function (scope, el, attrs) {
          // Make sure external links actually cause the browser to load a new page
          if (!attrs.pushState) {
            el.attr('target', '_self');
          }
          // Prevent default on all elements that have # as a location
          if (attrs.href === '#') {
            el.on('click', function (e) {
              e.preventDefault();
            });
          }
          // Make sure hash links send user to proper relative link
          else if (attrs.href && attrs.href.match(/(^|\s)#([^ ]*)/)) {
            el.on('click', function (e) {
              e.preventDefault();
              $location.hash(attrs.href.replace('#', ''));
              scope.$apply();
              $anchorScroll();
            });
          } else {
            window.scrollTo(0, 0);
          }
        }
      };
    }
  ])
  .directive('remixLink', ['$window', 'makeapi',
    function ($window, makeapi) {
      return {
        restrict: 'A',
        link: function (scope, el, attrs) {
          // $observe here to wait for attrs to /actually/ be ready...
          attrs.$observe('remixLink', function (val) {
            if (val && val.length > 0) {
              makeapi.makeapi
                .id(attrs.remixLink)
                .then(function (err, makes) {
                  if (err) {
                    console.error(err);
                  }
                  el.prop('href', makes[0].remixurl);
                });
            }
          });
        }
      };
    }
  ])
  .directive('masonryGallery', ['$timeout', '$rootScope',
    function ($timeout, $rootScope) {
      return {
        restrict: 'A',
        scope: {
          'masonryOptions': '='
        },
        link: function (scope, element) {
          $rootScope.$on('makeLoad', function (e) {
            scope.loaded = (scope.loaded ? scope.loaded + 1 : 1);

            var total = $(element).children('.make').length;

            if (scope.loaded === total) {
              var
              mason = new window.Masonry(element.context, scope.masonryOptions);
              mason.layout();
            }
          });
        }
      };
    }
  ])
  .directive('retinaImage', ['$window',
    function ($window) {
      'use strict';

      function renderImage($image, source) {
        $image.attr('src', source);
      }

      return {
        restrict: 'A',
        link: function (scope, el) {
          if ($window.devicePixelRatio !== undefined && $window.devicePixelRatio >= 1.5) {
            renderImage($(el), $(el).attr('data-src-2x'));
          } else {
            renderImage($(el), $(el).attr('data-src-1x'));
          }
        }
      };
    }
  ])
  .directive('marquee', ['$window',
    function ($window) {
      var $ = $window.jQuery;
      var Marquee = function (target, options) {
        var self = this,
          defaults,
          option;

        // Options ----------------------------------------------------------------

        defaults = {
          itemsPerGroup: 3,
          rotationSpeed: 5000, // Milliseconds
          fadeInSpeed: 1000
        };

        for (option in options) {
          defaults[option] = options[option] || defaults[option];
        }

        self.options = defaults;

        // Element references -----------------------------------------------------

        self.$container = $(target);
        self.$items = self.$container.children();

        // Properties -------------------------------------------------------------

        self.firstVisibleItemIndex = 0;
        self.itemCount = self.$items.length;
        self.visibleItems = [];
        self.rotationInterval = null; // Will become 'setInterval' reference
        self.isRotating = false;

        // Setup ------------------------------------------------------------------

        self.$items.fadeTo(0, 0).hide();
      };

      Marquee.prototype = {

        /**
         * Animate in a group of items
         * @param  {number} firstItemIndex
         * @return {undefined}
         */
        showGroup: function (firstItemIndex) {
          var self = this,
            group = [],
            i = 0;

          // Hide the currently visible group

          self.visibleItems.forEach(function (index) {
            self.$items.eq(index).hide();
          });

          // Build an array of indexes for next group to show

          while (i < self.options.itemsPerGroup) {
            // Loop around to start of array if necessary
            if (firstItemIndex >= self.itemCount) {
              firstItemIndex = firstItemIndex % self.itemCount;
            }

            group.push(firstItemIndex);

            firstItemIndex++;
            i++;
          }

          self.visibleItems = group;

          // Sort so that names animate in left to right

          group.sort(function (a, b) {
            return a > b;
          });

          // Fade in new group one-by-one

          group.forEach(function (itemIndex, i) {
            self.$items.eq(itemIndex).fadeTo(0, 0);

            setTimeout(function () {
              self.$items.eq(itemIndex).fadeTo(self.options.fadeInSpeed, 1);
            }, i * (self.options.fadeInSpeed / 4));
          });
        },

        /**
         * Start a periodic rotation of visible item groups
         * @return {undefined}
         */
        startRotation: function () {
          var self = this;

          self.isRotating = true;

          function showNextGroup() {
            if (self.visibleItems.length) {
              self.showGroup(self.visibleItems[self.visibleItems.length - 1] + 1);
            } else {
              self.showGroup(0);
            }
          }

          showNextGroup(); // Immediately show the first group

          self.rotationInterval = setInterval(showNextGroup, self.options.rotationSpeed);
        },

        /**
         * Stop rotating visible items
         * @return {undefined}
         */
        stopRotation: function () {
          var self = this;

          clearInterval(self.rotationInterval);
          self.isRotating = false;
        }
      };
      return {
        restrict: 'A',
        link: function (scope, el) {
          var marquee = new Marquee(el);
          marquee.startRotation();
        }
      };
    }
  ])
  .directive('wmArrowTo', function () {
    return {
      restrict: 'EA',
      template: '<img class="maker-party-arrow" src="/img/home/maker-party-arrow.svg">'
    };
  })
  .directive('languageSelect', ['CONFIG', '$timeout', '$location',
    function (config, $timeout, $location) {
      return {
        restrict: 'A',
        link: function ($scope, $element, $attrs) {
          var options = [];
          var lang;
          for (var i = 0; i < config.supported_languages.length; i++) {
            lang = config.supported_languages[i];
            options.push({
              id: lang,
              title: config.langmap[lang] ? config.langmap[lang].nativeName : lang,
              english: config.langmap[lang] && config.langmap[lang].englishName
            });
          }
          $timeout(function () {
            $element.selectize({
              options: options,
              labelField: 'title',
              valueField: 'id',
              searchField: ['title', 'english'],
              onItemAdd: function (selectedLang) {
                if ($attrs.redirect) {
                  var href = $location.path();
                  var lang = config.lang;
                  var supportedLanguages = config.supported_languages;

                  // matches any of these:
                  // `en`, `en-us`, `en-US` or `ady`
                  var matchesLang;
                  var matches = href.match(/([a-z]{2,3})([-]([a-zA-Z]{2}))?/);
                  if (matches) {
                    if (matches[1] && matches[2]) {
                      matchesLang = matches[1].toLowerCase() + matches[2].toUpperCase();
                    } else {
                      matchesLang = matches[1].toLowerCase();
                    }
                  }
                  // if the selected language is match to the language in the header
                  if (selectedLang === lang) {
                    return;
                    // check if we have any matches and they are exist in the array we have
                  } else if ((matches && matches[0]) && supportedLanguages.indexOf(matchesLang) !== -1) {
                    href = href.replace(matches[0], selectedLang);
                    window.location = href;
                  } else if (href === '/') {
                    window.location = '/' + selectedLang;
                  } else {
                    window.location = '/' + selectedLang + href;
                  }
                }
              }
            });
            var selectize = $element[0].selectize;
            selectize.setValue(config.lang);
          });
        }
      };
    }
  ])
  .directive('competencyMediaGen', ['$sce',
    function () {
      return {
        restrict: 'EA',
        templateUrl: '/views/partials/competency-media-gen.html'
      };
    }
  ]);