<!DOCTYPE html>
<html>

  <head>
    <script data-require="angular.js@1.4.x" src="https://code.angularjs.org/1.4.8/angular.js" data-semver="1.4.8"></script>
    <script data-require="angular-route@*" data-semver="1.4.8" src="https://code.angularjs.org/1.4.8/angular-route.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.18.1/TweenLite.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.18.1/easing/EasePack.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.18.1/plugins/ScrollToPlugin.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.5/ScrollMagic.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.5/plugins/debug.addIndicators.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.5/plugins/animation.gsap.js"></script>
    <script src="ng-magics.js"></script>
    <script src="script.js"></script>
    <link href="style.css" rel="stylesheet" />
  </head>

  <body ng-app="app" ng-controller="AppController as app">
    <nav class="navbar navbar-default navbar-fixed-top">
      <div class="container">
        <ul class="nav navbar-nav">
          <li ng-class="{ active: (route == 'a') }">
            <a href="#"><em>a</em> Scene</a>
          </li>
          <li ng-class="{ active: (route == 'b') }">
            <a href="#"><em>b</em> Scene</a>
          </li>
          <li ng-class="{ active: (route == 'c') }">
            <a href="#"><em>c</em> Scene</a>
          </li>
          <li ng-class="{ active: (route == 'd') }">
            <a href="#"><em>d</em> Scene</a>
          </li>
        </ul>
        <p class="navbar-brand pull-right">ng-magics</p>
      </div>
    </nav>

    <section magics-scene="a">
      <div class="container">
        <div class="row">
          <div class="col-md-12">
            <h1><em>a</em> Scene</h1>
            <p class="lead">Entry scene that got no special treatment.</p>
            <p>It got no parent <em>magics-stage</em> directive, so it is defined on <em>default</em> stage, which just a basic vertical stage that uses window as the container.</p>
            <p>Even though it doesn't define scene spies, top bar can reflects its activity because of <em>sceneEnter</em> event listeners on root scope.</p>
          </div>
        </div>
      </div>
    </section>
    
    <section magics-scene="b">
      <div class="container" ng-controller="GalleryController as gallery">
        <div class="row">
          <div class="col-md-12">
            <h1><em>b</em> Scene</h1>
            <p class="lead">It got a nested stage.</p>
            <p class="text-center">
              <span class="btn btn-default" ng-class="{ 'btn-success': gallery.five }">Five</span>
              <span class="btn btn-default" ng-class="{ 'btn-warning': gallery.ten }">Ten</span>
            </p>
            <div class="progress">
              <div ng-style="{ 'width': gallery.progress }" ng-class="{ 'reverse': gallery.reverse }">
                <span>{{ gallery.progress }}</span>
              </div>
            </div>
            <h2><em>gallery</em> Horizontal Stage</h2>
          </div>
        </div>
        <div class="row">
          <div class="col-md-12">
            <div>
              <div magics-stage="gallery" magics-stage-options="{ vertical: false, globalSceneOptions: { triggerHook: 0.3 } }" class="gallery">
                <ul magics-scene="gallery" magics-spy magics-spy-progress="gallery.progressHandler">
                  <li></li>
                  <li></li>
                  <li></li>
                  <li></li>
                  <li magics-scene="galleryFive" magics-spy="gallery.five" magics-spy-progress="gallery.fiveProgressHandler">
                    <div id="galleryFive">5</div>
                  </li>
                  <li></li>
                  <li></li>
                  <li></li>
                  <li></li>
                  <li magics-scene="galleryTen" magics-spy="gallery.ten">
                    <div>10</div>
                  </li>
                  <li></li>
                  <li></li>
                  <li></li>
                  <li></li>
                  <li></li>
                </ul>
              </div>
            </div>
            <p></p>
          </div>
        </div>
      </div>
    </section>
    
    <section magics-scene="c" parallax parallax-distance="0.66">
      <div class="container">
        <div class="row">
          <div class="col-md-12">
            <h1><em>c</em> Scene</h1>
            <h2><em>parallax</em> directive</h2>
            <p class="lead">An example of parallax directive that enables fancy effects with just a tiny bit of code and some scroll sorcery.</p>
          </div>
        </div>
      </div>
    </section>
    
    <section magics-scene="d">
      <h1><em>d</em> Scene</h1>
      <p class="lead">Another dull empty scene.</p>
    </section>
  </body>

</html>
var app = angular.module('app', ['ngRoute', 'ngMagics']);

app.config(function (magicsProvider) {
  // magicsProvider.stageOptions = { vertical: false };

  magicsProvider.debug = true;
  magicsProvider.debugOptions = {
    colorStart: '#00bc8c',
    colorEnd: '#e74c3c',
    colorTrigger: '#3498db'
  };
});

app.constant('routes', ['a', 'b', 'c', 'd']);

app.config(function ($locationProvider, $routeProvider, routes) {
  angular.forEach(routes, function (route) {
    $routeProvider.when('/' + route, {});
  });
  
  $routeProvider.otherwise({
    redirectTo: '/a'
  });
});

app.run(function ($compile, $rootScope, $location, magics, routes) {
  // dummy ng-view
	$compile('<ng-view>')($rootScope);

  $rootScope.$on('sceneEnter', function (e, sceneName) {
    console.log('sceneEnter', sceneName, e);
    
    if (routes.indexOf(sceneName) >= 0) {
      // 'sceneEnter' can be triggered inside or outside the digest this way
      $rootScope.$evalAsync(function () {
        $location.path('/' + sceneName);
      });
    }
  });
  
// current route as single source of truth for active scene state
  $rootScope.$on('$routeChangeSuccess', function (e, current) {
    try {
      var route = current.$$route.originalPath.replace(/(^\/)/, '');
    } catch (e) {}

    // should be a service, but global scope variable
    // can do the job for 1-way bindings on children scopes,
    // as long as its value is changed only on root scope
    $rootScope.route = route;
    
    console.debug('route', route);
  });
});

app.controller('AppController', function ($scope, throttle, magics) {
  this.progressHandler = throttle(function (e) {
    $scope.$apply(function () {
      self.progress = Math.round(e.progress * 100) + '%';
    });

    console.debug('app.progress', e);
  }, 50);
});

app.controller('GalleryController', function ($scope, throttle) {
  var self = this;

  // generic throttle/debounce functions are bundled with ngMagics,
  // scope manipulations have to be reasonably braked
  this.progressHandler = throttle(function (e) {
    $scope.$apply(function () {
      self.reverse = (e.scrollDirection === 'REVERSE');
      self.progress = Math.round(e.progress * 100) + '%';
    });

    console.debug('gallery.progress', e);
  }, 50);

  this.fiveProgressHandler = function (e) {
    var scale = 1 + 0.5 * e.progress;

    // directive is the proper place for that
    if (!this.fiveElement)
      this.fiveElement = angular.element(e.target.triggerElement()).find('div');

    // unthrottled progress handler is a good place for realtime animations
    this.fiveElement.css('transform', 'scale(' + scale + ',' + scale + ')');
  }
});

app.controller('ParallaxController', function ($element, $scope) {
  // extra pixels that the background height has off-canvas
  this.extra = 42;

  this.distance = 1;
  this.progress = 0;

  this.scale = function () {
    return 1 / this.distance();
  }

  this.distance = function () {
    return $scope.distance || 1;
  }
  
  this.height = function () {
    return $element[0].offsetHeight;
  }
  
  this.offset = function () {
    var offsetMax = Math.max(this.height() * (this.scale() - 1) - this.extra, 0);
    return (this.progress - 0.5) * offsetMax * -1;
  }
  
  this.progressHandler = function () {}

});

app.directive('parallax', function (magics, debounce) {
  return {
    scope: {
      distance: '=parallaxDistance'
    },
    transclude: true,
    template:
      '<div magics-scene="parallax" class="parallax-container">' +
      ' <div parallax-background></div>' +
      ' <div ng-transclude></div>' +
      '</div>',
    controller: 'ParallaxController',
    controllerAs: 'parallax',
    link: function (scope, element, attrs, ctrl) {
      var self = ctrl;
      
      var scene = magics.scene('parallax');
      
      scene.on('shift', debounce(function () {
        scope.$broadcast('parallax:refresh');
      }, 50));

      scene.on('progress', function (e) {
        self.progress = e.progress;
        scope.$broadcast('parallax:progress');
      });      
    }
  };
});

app.directive('parallaxBackground', function (tweenEasing) {
  return {
    require: ['parallaxBackground', '^parallax'],
    controller: function () {
    },
    link: function (scope, element, attrs, ctrls) {
      var parallaxCtrl = ctrls[1];
      var blurRadius = 20;
      
      scope.$on('parallax:refresh', refresh);
      scope.$on('parallax:progress', progress);

      function refresh() {
        console.log('parallax refresh', parallaxCtrl.scale())
        var scale = parallaxCtrl.scale();
        element.css('transform', 'scale(' + scale + ',' + scale + ')');
        progress();
      }
  
      function progress() {
        console.log('parallax progress', parallaxCtrl.offset())
        element.css('margin-top', parallaxCtrl.offset() + 'px');
        
        var amount = ease(Math.abs((parallaxCtrl.progress * 2 - 1)));
        var blur = Math.round(amount * blurRadius + 1) + 'px';
        element.css('filter', 'blur(' + blur + ')');        
      }

      function ease(val) {
        return tweenEasing.Power2.easeIn.getRatio(val);
      }
    }
  }
});
@import (less) "http://cdn.rawgit.com/twbs/bootstrap/v3.3.6/less/bootstrap.less";
@import (less) "http://cdn.rawgit.com/thomaspark/bootswatch/gh-pages/darkly/bootswatch.less";
@import (less) "http://cdn.rawgit.com/thomaspark/bootswatch/gh-pages/darkly/variables.less";

.for(@i, @n) {
  .-each(@i);
}
.for(@n) when (isnumber(@n)) {
  .for(1, @n);
}
.for(@i, @n) when not (@i = @n)  {
    .for((@i + (@n - @i) / abs(@n - @i)), @n);
}

@color_a: #264653;
@color_b: #2a9d8f;
@color_c: #e9c46a;
@color_d: #f4a261;
@color_e: #e76f51;

@grid-float-breakpoint: 0;

section {
  &:first-of-type {
    padding-top: @navbar-height;
  }

  &:nth-of-type(2) {
    background: @color_a;
  }
  
  &:nth-of-type(3) {
    background: @color_b;
  }
  
  &:nth-of-type(3) {
    background: @color_b;
  }

  min-height: 100vh;
}

h1, h2 {
  text-align: center;
}

nav {
    & ul a {
    cursor: default;
  }

  .navbar-brand {
    height: auto;
    padding-bottom: 0;
  }
}

.lead {
  text-align: center;
}

.gallery {
  & ul  {
    .list-inline();

    float: left;
    display: inline-block;
    font-size: 0rem;    
    white-space: nowrap;
  }

  & ul > li {
    .for(15); .-each(@i) {
      &:nth-of-type(@{i}) {
        background: url('http://placeimg.com/300/300/nature/@{i}') no-repeat center;
      }
    }

    vertical-align: middle;
    padding: 0;
    font-size: 1rem;
    width: 300px;
    height: 300px;
  }
  
  & li div {
    .text-center();
    
    line-height: 300px;
    font-size: 15em;
  }

  overflow-x: scroll;
  overflow-y: hidden;  
}

.progress {
  & div {
    .progress-bar();
    .progress-bar-success();
    
    min-width: 2em;
    line-height: @line-height-computed * 1.25;
    font-size:@font-size-base;
  }

  & div.reverse {
    .progress-bar-danger();
  }
  
  height: @line-height-computed * 1.25
}

[parallax] {
  overflow: hidden;
    height: 100vh;
    
  & .parallax-container {
    height: 100vh;
    position: relative;
  }
}

[parallax-background] {
  background: url('http://placeimg.com/2000/2000/nature/1') no-repeat center;
  background-size: cover;
  width: 100%;
  height: 100vh;
  position: absolute;
}
(function(__root, __factory) { if (typeof define === "function" && define.amd) { define("ng-magics", ["angular"], __factory);} else if (typeof exports === "object") {module.exports = __factory(require("angular"));} else {__root["ng-magics"] = __factory(angular);}})(this, (function(__small$_mod_0) {
var exports = {};
var __small$_4 = (function() {
var exports = {};
/// <reference path="../typings/globals.d.ts"/>
/// <reference path="../typings/lib.d.ts"/>
var angular = __small$_mod_0;
var debottle_1 = ((function() {
var exports = {};
var apply = ((function() {
var exports = {};
function apply(self, fn, args) {
	var selfless = (self === undefined) || (self === null);
	var length = args ? args.length : 0;
	
	switch (length) {
		case 0:
			return selfless ? fn() : fn.call(self);
		case 1:
			return selfless ? fn(args[0]) : fn.call(self, args[0]);
		case 2:
			return selfless ? fn(args[0], args[1]) : fn.call(self, args[0], args[1]);
		case 3:
			return selfless ? fn(args[0], args[1], args[2]) : fn.call(self, args[0], args[1], args[2]);
		case 4:
			return selfless ? fn(args[0], args[1], args[2], args[3]) : fn.call(self, args[0], args[1], args[2], args[3]);
		case 5:
			return selfless ? fn(args[0], args[1], args[2], args[3], args[4]) : fn.call(self, args[0], args[1], args[2], args[3], args[4]);
		case 6:
			return selfless ? fn(args[0], args[1], args[2], args[3], args[4], args[5]) : fn.call(self, args[0], args[1], args[2], args[3], args[4], args[5]);
		// the crucial point
		case 7:
			return selfless ? fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6]) : fn.call(self, args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
		default:
			return selfless ? fn.apply(null, args) : fn.apply(self, args);
	}
}

exports = apply;
return exports;
})());

function Debottle(delay) {
	this.$delay = delay;

	this.$cancel = function () {
		return clearTimeout(this.$timeout);
	}
}

function debounce(fn, delay, cb) {
	if (arguments.length === 2 && typeof delay === 'function') {
		cb = delay;
		delay = undefined;
	}

	var self = debouncedFn;
	Debottle.call(self, delay);

	function debouncedFn() {
		var args = arguments;

		if (self.$timeout !== undefined)
			self.$cancel();

		self.$timeout = setTimeout(function () {
			if (typeof cb !== 'function')
				return apply(null, fn, args);

			try {
				cb(null, apply(null, fn, args));
			} catch (e)	{
				cb(e);
			}
		}, self.$delay);
	};

	return self;
}

function throttle(fn, delay, cb) {
	if (arguments.length === 2 && typeof delay === 'function') {
		cb = delay;
		delay = undefined;
	}

	var self = throttledFn;
	Debottle.call(self, delay);

	function throttledFn() {
		var args = arguments;

		if (self.$timeout !== undefined)
			return;

		self.$timeout = setTimeout(function () {
			self.$timeout = undefined;
		}, self.$delay);

		if (typeof cb !== 'function')
			return apply(null, fn, args);

		try {
			cb(null, apply(null, fn, args));
		} catch (e)	{
			cb(e);
		}
	};

	return self;
}

exports.debounce = debounce;
exports.throttle = throttle;

return exports;
})());
exports.__esModule = true;
exports["default"] = angular.module('ngMagics.constants', [])
    .constant('debounce', debottle_1.debounce)
    .constant('throttle', debottle_1.throttle)
    .constant('scrollMagic', (function () {
    if (typeof ScrollMagic !== 'undefined') {
        return ScrollMagic;
    }
})())
    .constant('Tween', (function () {
    var Tween;
    // module bundler global substitution
    if (typeof TweenMax !== 'undefined') {
        Tween = TweenMax;
    }
    else if (typeof TweenLite !== 'undefined') {
        Tween = TweenLite;
    }
    else if (typeof GreenSockGlobals !== 'undefined') {
        Tween = GreenSockGlobals.TweenMax || GreenSockGlobals.TweenLite;
    }
    return Tween;
})())
    .constant('tweenEasing', (function () {
    if (typeof GreenSockGlobals !== 'undefined') {
        return GreenSockGlobals.com.greensock.easing;
    }
})())
    .name;

return exports;
})();
var __small$_5 = (function() {
var exports = {};
/// <reference path="../typings/globals.d.ts"/>
/// <reference path="../typings/lib.d.ts"/>
var angular = __small$_mod_0;
var constants_1 = __small$_4;
var MagicsProvider = (function () {
    function MagicsProvider() {
        var _this = this;
        var deps = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            deps[_i - 0] = arguments[_i];
        }
        this.$ = {};
        this.debug = false;
        this.debugOptions = {};
        this.sceneOptions = {};
        this.stageOptions = {};
        this.pinOptions = {};
        this.performanceOptions = {
            brake: 'debounce',
            delay: 50
        };
        var self = this.constructor;
        angular.forEach(self.$inject, function (depName, i) {
            _this.$[depName] = deps[i];
        });
        // TODO: proper typing
        this.$get.$inject = MagicsInstance.$inject;
        this.scrollOptions = {
            y: true,
            duration: 0.75,
            ease: this.$.tweenEasing.Power2.easeInOut
        };
    }
    MagicsProvider.prototype.$get = function () {
        var deps = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            deps[_i - 0] = arguments[_i];
        }
        var instance = Object.create(MagicsInstance.prototype);
        MagicsInstance.apply(instance, [this].concat(deps));
        return instance;
    };
    MagicsProvider.$inject = ['tweenEasing'];
    return MagicsProvider;
})();
var MagicsInstance = (function () {
    function MagicsInstance(provider) {
        var _this = this;
        var deps = [];
        for (var _i = 1; _i < arguments.length; _i++) {
            deps[_i - 1] = arguments[_i];
        }
        this.$ = {};
        // stage.scrollTo accepts 1 optional argument
        // TODO: swappable method
        this._scrollHandler = function (target, _a) {
            var container = _a[0], deferred = _a[1];
            var _b = _this.$, $rootScope = _b.$rootScope, Tween = _b.Tween;
            var scrollOptions = _this._provider.scrollOptions;
            function completeHandler() {
                deferred.resolve();
                $rootScope.$apply();
            }
            function autoKillHandler() {
                deferred.reject();
                $rootScope.$apply();
            }
            var scrollToOptions = {
                autoKill: true,
                onAutoKill: autoKillHandler
            };
            if (scrollOptions.x) {
                scrollToOptions.x = target;
            }
            if (scrollOptions.y) {
                scrollToOptions.y = target;
            }
            Tween.to(container, scrollOptions.duration, {
                scrollTo: scrollToOptions,
                ease: scrollOptions.ease,
                onComplete: completeHandler
            });
        };
        var self = this.constructor;
        angular.forEach(self.$inject, function (depName, i) {
            _this.$[depName] = deps[i];
        });
        this._provider = provider;
        //
        var _a = this.$, $cacheFactory = _a.$cacheFactory, debounce = _a.debounce, throttle = _a.throttle;
        this._brake = (this._provider.performanceOptions.brake === 'throttle')
            ? throttle
            : debounce;
        this._delay = this._provider.performanceOptions.delay;
        var cache = $cacheFactory('magics');
        this._scenes = cache.put('scenes', {});
        this._stages = cache.put('stages', {});
        this.stage('default', {});
    }
    MagicsInstance.prototype._isEmpty = function (val) {
        return ([null, undefined, ''].indexOf(val) >= 0);
    };
    MagicsInstance.prototype._patch = function (name, storageName, instance) {
        var _this = this;
        if ('$$patched' in instance) {
            return instance;
        }
        var destroy_ = instance.destroy;
        instance.destroy = function () {
            var args = [];
            for (var _i = 0; _i < arguments.length; _i++) {
                args[_i - 0] = arguments[_i];
            }
            if (name === 'default') {
                return instance;
            }
            if (name in _this[storageName]) {
                delete _this[storageName][name];
            }
            return destroy_.apply(instance, args);
        };
        instance.$$patched = true;
        return instance;
    };
    MagicsInstance.prototype.stage = function (name, options) {
        var _a = this.$, $window = _a.$window, scrollMagic = _a.scrollMagic;
        if (this._isEmpty(name)) {
            name = 'default';
        }
        // TODO: duplicate warning for 'options'
        if (name in this._stages) {
            return this._stages[name];
        }
        var stageOptions = angular.extend({}, this._provider.stageOptions, {
            globalSceneOptions: this._provider.sceneOptions,
            // TODO: custom container	
            container: this._provider.container || $window
        }, options);
        var stage = this._stages[name] = new scrollMagic.Controller(stageOptions);
        stage.scrollTo(this._scrollHandler);
        return this._patch(name, '_stages', stage);
    };
    MagicsInstance.prototype.scene = function (name, options, stageName) {
        var _a = this.$, $rootScope = _a.$rootScope, scrollMagic = _a.scrollMagic;
        // TODO: duplicate warning for 'options'
        if (name in this._scenes) {
            return this._scenes[name];
        }
        if (this._isEmpty(stageName)) {
            stageName = 'default';
        }
        var stage = this._stages[stageName];
        var scene = this._scenes[name] = (new scrollMagic.Scene(options)).addTo(stage);
        if (this._provider.debug) {
            scene.addIndicators(angular.extend({}, this._provider.debugOptions, {
                name: name
            }));
        }
        this.onSceneEnter(name, function (e) {
            $rootScope.$broadcast('sceneEnter:' + name, e);
            $rootScope.$broadcast('sceneEnter', name, e);
        });
        this.onSceneLeave(name, function (e) {
            $rootScope.$broadcast('sceneLeave:' + name, e);
            $rootScope.$broadcast('sceneLeave', name, e);
        });
        return this._patch(name, '_scenes', scene);
    };
    MagicsInstance.prototype.scrollToScene = function (scene, offset) {
        var _a = this.$, $q = _a.$q, scrollMagic = _a.scrollMagic;
        offset = offset || 0;
        if (angular.isString(scene)) {
            scene = this._scenes[scene];
        }
        if (!(scene instanceof scrollMagic.Scene)) {
            return false;
        }
        var target = scene.scrollOffset() + offset;
        var stage = scene.controller();
        var container = stage.info('container');
        var deferred = $q.defer();
        // the proper use case for Deferred, one and only
        stage.scrollTo(target, [container, deferred]);
        return deferred.promise;
    };
    // TODO: ? stage namespacing, brake per stage
    // TODO: additional params
    MagicsInstance.prototype.onSceneEnter = function (name, handler) {
        var _this = this;
        var brakedHandler = this._brake(handler, this._delay);
        this._scenes[name].on('enter', brakedHandler);
        return function () {
            var scene = _this._scenes[name];
            if (scene) {
                scene.off('enter', brakedHandler);
            }
        };
    };
    MagicsInstance.prototype.onSceneLeave = function (name, handler) {
        var _this = this;
        var brakedHandler = this._brake(handler, this._delay);
        var scene = this._scenes[name];
        scene.on('leave', brakedHandler);
        return function () {
            var scene = _this._scenes[name];
            if (scene) {
                scene.off('leave', brakedHandler);
            }
        };
    };
    // TODO: onScenePoint
    MagicsInstance.prototype.onSceneProgress = function (name, handler) {
        var _this = this;
        var scene = this._scenes[name];
        scene.on('progress', handler);
        return function () {
            var scene = _this._scenes[name];
            if (scene) {
                scene.off('progress', handler);
            }
        };
    };
    MagicsInstance.prototype.onSceneDestroy = function (name, handler) {
        var _this = this;
        var scene = this._scenes[name];
        scene.on('destroy', handler);
        return function () {
            var scene = _this._scenes[name];
            if (scene) {
                scene.off('destroy', handler);
            }
        };
    };
    MagicsInstance.$inject = ['$cacheFactory', '$q', '$rootScope', '$window', 'debounce', 'throttle', 'scrollMagic', 'Tween'];
    return MagicsInstance;
})();
exports.__esModule = true;
exports["default"] = angular.module('ngMagics.magics', [constants_1["default"]])
    .provider('magics', MagicsProvider)
    .name;

return exports;
})();
/// <reference path="./typings/globals.d.ts"/>
/// <reference path="./typings/lib.d.ts"/>
var angular = __small$_mod_0;
var magics_scene_1 = ((function() {
var exports = {};
/// <reference path="../typings/globals.d.ts"/>
/// <reference path="../typings/lib.d.ts"/>
var angular = __small$_mod_0;
var magics_1 = __small$_5;
var MagicsSceneDirectiveController = (function () {
    function MagicsSceneDirectiveController() {
        var _this = this;
        var deps = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            deps[_i - 0] = arguments[_i];
        }
        this.$ = {};
        var self = this.constructor;
        angular.forEach(self.$inject, function (depName, i) {
            _this.$[depName] = deps[i];
        });
        //
        var _a = this.$, $scope = _a.$scope, $element = _a.$element, $attrs = _a.$attrs;
        var expando = angular.element.expando;
        // TODO: global option to get scene name from 'id' attr
        // uninterpolated attribute
        this.sceneName = $attrs.magicsScene || (expando + '.' + $element[0][expando]);
        // TODO: filter duration, offset, triggerHook, reverse
        this.sceneOptions = $scope.$eval($attrs.magicsSceneOptions) || {};
    }
    MagicsSceneDirectiveController.prototype.init = function (stageName) {
        var _this = this;
        var _a = this.$, $element = _a.$element, magics = _a.magics;
        // TODO: avoid _scenes check
        // TODO: duplicate warning
        if (this.sceneName in magics._scenes) {
            this.scene = magics.scene(this.sceneName);
            return;
        }
        this.scene = magics.scene(this.sceneName, this.sceneOptions, stageName);
        if (!('duration' in this.sceneOptions)) {
            // TODO: ? stage property
            var stage = magics.stage(stageName);
            var element = $element[0];
            var isVertical = stage.info('vertical');
            // TODO: ? separate method
            this.scene.duration(function () {
                // TODO: caching
                return isVertical ? element.offsetHeight : element.offsetWidth;
            });
            // TODO: spec
            this.scene.triggerElement(element);
        }
        $element.on('$destroy', function () {
            magics.scene(_this.sceneName).destroy();
        });
    };
    MagicsSceneDirectiveController.$inject = ['$scope', '$element', '$attrs', '$rootScope', 'magics'];
    return MagicsSceneDirectiveController;
})();
exports.__esModule = true;
exports["default"] = angular.module('ngMagics.magicsScene', [magics_1["default"]])
    .directive('magicsScene', function () { return ({
    /**
     * @ngdoc directive
     * @name magicsScene
     *
     * @element ANY
     * @restrict A
     *
     * @param {string=} magicsScene Scene name.
     * @param {Object=} magicsSceneOptions Options that are used on scene creation.
     *
     * @description Creates a new scene or reuses the existing one on either the stage specified by `magicsStage` directive or 'default' stage.
     */
    restrict: 'A',
    require: ['magicsScene', '?^magicsStage'],
    controller: MagicsSceneDirectiveController,
    link: {
        pre: function (scope, element, attrs, ctrls) {
            var sceneCtrl = ctrls[0], stageCtrl = ctrls[1];
            var stageName = (stageCtrl || {}).stageName;
            sceneCtrl.init(stageName);
        }
    }
}); })
    .name;

return exports;
})());
var magics_spy_1 = ((function() {
var exports = {};
/// <reference path="../typings/globals.d.ts"/>
/// <reference path="../typings/lib.d.ts"/>
var angular = __small$_mod_0;
var magics_1 = __small$_5;
var MagicsSpyDirectiveController = (function () {
    function MagicsSpyDirectiveController() {
        var _this = this;
        var deps = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            deps[_i - 0] = arguments[_i];
        }
        this.$ = {};
        var self = this.constructor;
        angular.forEach(self.$inject, function (depName, i) {
            _this.$[depName] = deps[i];
        });
        //
        var _a = this.$, $scope = _a.$scope, $attrs = _a.$attrs, $parse = _a.$parse;
        // uninterpolated attribute
        if ($attrs.magicsSpy) {
            var setter = $parse($attrs.magicsSpy).assign;
            if (setter) {
                this._flagSetter = function (val) { return setter($scope, val); };
            }
        }
    }
    // TODO: get/set
    MagicsSpyDirectiveController.prototype.flag = function (val) {
        if (arguments.length) {
            this._flag = val;
            if (this._flagSetter) {
                this._flagSetter(val);
            }
        }
        return this._flag;
    };
    MagicsSpyDirectiveController.prototype.init = function (sceneName) {
        var _this = this;
        var _a = this.$, $scope = _a.$scope, $element = _a.$element, $attrs = _a.$attrs, magics = _a.magics;
        var progressHandler = $scope.$eval($attrs.magicsSpyProgress);
        if (progressHandler) {
            var offSceneProgress = magics.onSceneProgress(sceneName, progressHandler);
            $element.on('$destroy', function () { return offSceneProgress(); });
        }
        // TODO: ? $rootScope
        $scope.$on('sceneEnter:' + sceneName, function (e) {
            $scope.$apply(function () { return _this.flag(true); });
        });
        $scope.$on('sceneLeave:' + sceneName, function (e) {
            $scope.$apply(function () { return _this.flag(false); });
        });
    };
    MagicsSpyDirectiveController.$inject = ['$scope', '$element', '$attrs', '$parse', 'magics'];
    return MagicsSpyDirectiveController;
})();
exports.__esModule = true;
exports["default"] = angular.module('ngMagics.magicsSpy', [magics_1["default"]])
    .directive('magicsSpy', ['magics', function (magics) { return ({
        /**
         * @ngdoc directive
         * @name magicsSpy
         *
         * @element ANY
         * @restrict A
         *
         * @param {expression=} magicsSpy Scope variable flag *(read-only)*.
         * @param {string=} magicsSpyScene Scene name.
         * @param {expression=} magicsSpyProgress Scene progress callback function.
         *
         * @description Sets up a spy for the scene specified by either parent `magicsScene` directive or `magicsSpyScene` attribute.
         */
        restrict: 'A',
        require: ['magicsSpy', '?^magicsScene'],
        controller: MagicsSpyDirectiveController,
        link: {
            pre: function (scope, element, attrs, ctrls) {
                var spyCtrl = ctrls[0], sceneCtrl = ctrls[1];
                var parentSceneName = (sceneCtrl || {}).sceneName;
                var sceneName = attrs.magicsSpyScene || parentSceneName;
                if (magics._isEmpty(sceneName)) {
                    return;
                }
                spyCtrl.init(sceneName);
            }
        }
    }); }])
    .name;

return exports;
})());
var magics_stage_1 = ((function() {
var exports = {};
/// <reference path="../typings/globals.d.ts"/>
/// <reference path="../typings/lib.d.ts"/>
var angular = __small$_mod_0;
var magics_1 = __small$_5;
var MagicsStageDirectiveController = (function () {
    function MagicsStageDirectiveController() {
        var _this = this;
        var deps = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            deps[_i - 0] = arguments[_i];
        }
        this.$ = {};
        var self = this.constructor;
        angular.forEach(self.$inject, function (depName, i) {
            _this.$[depName] = deps[i];
        });
        //
        var _a = this.$, $scope = _a.$scope, $attrs = _a.$attrs;
        // uninterpolated attribute
        this.stageName = $attrs.magicsStage || 'default';
        // TODO: filter vertical, refreshInterval
        this.stageOptions = $scope.$eval($attrs.magicsStageOptions) || {};
        // TODO: custom scrollTo function
    }
    MagicsStageDirectiveController.prototype.init = function () {
        var _this = this;
        var _a = this.$, $element = _a.$element, magics = _a.magics;
        // TODO: avoid _stages check
        // TODO: duplicate warning
        if (this.stageName in magics._stages) {
            this.stage = magics.stage(this.stageName);
            return;
        }
        var stageOptions = angular.extend({}, this.stageOptions, { container: $element[0] });
        this.stage = magics.stage(this.stageName, stageOptions);
        // TODO: ? scope on destroy
        $element.on('$destroy', function () {
            magics.stage(_this.stageName).destroy();
        });
    };
    MagicsStageDirectiveController.$inject = ['$scope', '$element', '$attrs', 'magics'];
    return MagicsStageDirectiveController;
})();
exports.__esModule = true;
exports["default"] = angular.module('ngMagics.magicsStage', [magics_1["default"]])
    .directive('magicsStage', function () { return ({
    /**
     * @ngdoc directive
     * @name magicsStage
     *
     * @element ANY
     * @restrict A
     *
     * @param {string=} magicsStage Stage name.
     * @param {Object=} magicsStageOptions Options that have to be passed  to `magics.stage`.
     *
     * @description Creates a new stage or reuses the existing one.
     */
    restrict: 'A',
    controller: MagicsStageDirectiveController,
    link: {
        pre: function (scope, element, attrs, ctrl) {
            ctrl.init();
        }
    }
}); })
    .name;

return exports;
})());
var constants_1 = __small$_4;
var magics_1 = __small$_5;
exports.__esModule = true;
exports["default"] = angular.module('ngMagics', [
    magics_scene_1["default"],
    magics_spy_1["default"],
    magics_stage_1["default"],
    constants_1["default"],
    magics_1["default"]
])
    .name;

return exports;
}))