Implemented URL query parsing for initial token /opa/?token=abcde
[src/app-framework-demo.git] / afb-client / bower_components / foundation-apps / dist / js / foundation-apps.js
1 /*!
2  * iconic.js v0.4.0 - The Iconic JavaScript library
3  * Copyright (c) 2014 Waybury - http://useiconic.com
4  */
5
6 !function(a){"object"==typeof exports?module.exports=a():"function"==typeof define&&define.amd?define(a):"undefined"!=typeof window?window.IconicJS=a():"undefined"!=typeof global?global.IconicJS=a():"undefined"!=typeof self&&(self.IconicJS=a())}(function(){var a;return function b(a,c,d){function e(g,h){if(!c[g]){if(!a[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};a[g][0].call(j.exports,function(b){var c=a[g][1][b];return e(c?c:b)},j,j.exports,b,a,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({1:[function(a,b){var c=(a("./modules/polyfills"),a("./modules/svg-injector")),d=a("./modules/extend"),e=a("./modules/responsive"),f=a("./modules/position"),g=a("./modules/container"),h=a("./modules/log"),i={},j=window.iconicSmartIconApis={},k=("file:"===window.location.protocol,0),l=function(a,b,e){b=d({},i,b||{});var f={evalScripts:b.evalScripts,pngFallback:b.pngFallback};f.each=function(a){if(a)if("string"==typeof a)h.debug(a);else if(a instanceof SVGSVGElement){var c=a.getAttribute("data-icon");if(c&&j[c]){var d=j[c](a);for(var e in d)a[e]=d[e]}/iconic-bg-/.test(a.getAttribute("class"))&&g.addBackground(a),m(a),k++,b&&b.each&&"function"==typeof b.each&&b.each(a)}},"string"==typeof a&&(a=document.querySelectorAll(a)),c(a,f,e)},m=function(a){var b=[];a?"string"==typeof a?b=document.querySelectorAll(a):void 0!==a.length?b=a:"object"==typeof a&&b.push(a):b=document.querySelectorAll("svg.iconic"),Array.prototype.forEach.call(b,function(a){a instanceof SVGSVGElement&&(a.update&&a.update(),e.refresh(a),f.refresh(a))})},n=function(){i.debug&&console.time&&console.time("autoInjectSelector - "+i.autoInjectSelector);var a=k;l(i.autoInjectSelector,{},function(){if(i.debug&&console.timeEnd&&console.timeEnd("autoInjectSelector - "+i.autoInjectSelector),h.debug("AutoInjected: "+(k-a)),e.refreshAll(),i.autoInjectDone&&"function"==typeof i.autoInjectDone){var b=k-a;i.autoInjectDone(b)}})},o=function(a){a&&""!==a&&"complete"!==document.readyState?document.addEventListener("DOMContentLoaded",n):document.removeEventListener("DOMContentLoaded",n)},p=function(a){return a=a||{},d(i,a),o(i.autoInjectSelector),h.enableDebug(i.debug),window._Iconic?window._Iconic:{inject:l,update:m,smartIconApis:j,svgInjectedCount:k}};b.exports=p,window._Iconic=new p({autoInjectSelector:"img.iconic",evalScripts:"once",pngFallback:!1,each:null,autoInjectDone:null,debug:!1})},{"./modules/container":2,"./modules/extend":3,"./modules/log":4,"./modules/polyfills":5,"./modules/position":6,"./modules/responsive":7,"./modules/svg-injector":8}],2:[function(a,b){var c=function(a){var b=a.getAttribute("class").split(" "),c=-1!==b.indexOf("iconic-fluid"),d=[],e=["iconic-bg"];Array.prototype.forEach.call(b,function(a){switch(a){case"iconic-sm":case"iconic-md":case"iconic-lg":d.push(a),c||e.push(a.replace(/-/,"-bg-"));break;case"iconic-fluid":d.push(a),e.push(a.replace(/-/,"-bg-"));break;case"iconic-bg-circle":case"iconic-bg-rounded-rect":case"iconic-bg-badge":e.push(a);break;default:d.push(a)}}),a.setAttribute("class",d.join(" "));var f=a.parentNode,g=Array.prototype.indexOf.call(f.childNodes,a),h=document.createElement("span");h.setAttribute("class",e.join(" ")),h.appendChild(a),f.insertBefore(h,f.childNodes[g])};b.exports={addBackground:c}},{}],3:[function(a,b){b.exports=function(a){return Array.prototype.forEach.call(Array.prototype.slice.call(arguments,1),function(b){if(b)for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c])}),a}},{}],4:[function(a,b){var c=!1,d=function(a){console&&console.log&&console.log(a)},e=function(a){d("Iconic INFO: "+a)},f=function(a){d("Iconic WARNING: "+a)},g=function(a){c&&d("Iconic DEBUG: "+a)},h=function(a){c=a};b.exports={info:e,warn:f,debug:g,enableDebug:h}},{}],5:[function(){Array.prototype.forEach||(Array.prototype.forEach=function(a,b){"use strict";if(void 0===this||null===this||"function"!=typeof a)throw new TypeError;var c,d=this.length>>>0;for(c=0;d>c;++c)c in this&&a.call(b,this[c],c,this)}),function(){if(Event.prototype.preventDefault||(Event.prototype.preventDefault=function(){this.returnValue=!1}),Event.prototype.stopPropagation||(Event.prototype.stopPropagation=function(){this.cancelBubble=!0}),!Element.prototype.addEventListener){var a=[],b=function(b,c){var d=this,e=function(a){a.target=a.srcElement,a.currentTarget=d,c.handleEvent?c.handleEvent(a):c.call(d,a)};if("DOMContentLoaded"==b){var f=function(a){"complete"==document.readyState&&e(a)};if(document.attachEvent("onreadystatechange",f),a.push({object:this,type:b,listener:c,wrapper:f}),"complete"==document.readyState){var g=new Event;g.srcElement=window,f(g)}}else this.attachEvent("on"+b,e),a.push({object:this,type:b,listener:c,wrapper:e})},c=function(b,c){for(var d=0;d<a.length;){var e=a[d];if(e.object==this&&e.type==b&&e.listener==c){"DOMContentLoaded"==b?this.detachEvent("onreadystatechange",e.wrapper):this.detachEvent("on"+b,e.wrapper);break}++d}};Element.prototype.addEventListener=b,Element.prototype.removeEventListener=c,HTMLDocument&&(HTMLDocument.prototype.addEventListener=b,HTMLDocument.prototype.removeEventListener=c),Window&&(Window.prototype.addEventListener=b,Window.prototype.removeEventListener=c)}}()},{}],6:[function(a,b){var c=function(a){var b=a.getAttribute("data-position");if(b&&""!==b){var c,d,e,f,g,h,i,j=a.getAttribute("width"),k=a.getAttribute("height"),l=b.split("-"),m=a.querySelectorAll("g.iconic-container");Array.prototype.forEach.call(m,function(a){if(c=a.getAttribute("data-width"),d=a.getAttribute("data-height"),c!==j||d!==k){if(e=a.getAttribute("transform"),f=1,e){var b=e.match(/scale\((\d)/);f=b&&b[1]?b[1]:1}g=Math.floor((j/f-c)/2),h=Math.floor((k/f-d)/2),Array.prototype.forEach.call(l,function(a){switch(a){case"top":h=0;break;case"bottom":h=k/f-d;break;case"left":g=0;break;case"right":g=j/f-c;break;case"center":break;default:console&&console.log&&console.log("Unknown position: "+a)}}),i=0===h?g:g+" "+h,i="translate("+i+")",e?/translate/.test(e)?e=e.replace(/translate\(.*?\)/,i):e+=" "+i:e=i,a.setAttribute("transform",e)}})}};b.exports={refresh:c}},{}],7:[function(a,b){var c=/(iconic-sm\b|iconic-md\b|iconic-lg\b)/,d=function(a,b){var c="undefined"!=typeof window.getComputedStyle&&window.getComputedStyle(a,null).getPropertyValue(b);return!c&&a.currentStyle&&(c=a.currentStyle[b.replace(/([a-z])\-([a-z])/,function(a,b,c){return b+c.toUpperCase()})]||a.currentStyle[b]),c},e=function(a){var b=a.style.display;a.style.display="block";var c=parseFloat(d(a,"width").slice(0,-2)),e=parseFloat(d(a,"height").slice(0,-2));return a.style.display=b,{width:c,height:e}},f=function(){var a="/* Iconic Responsive Support Styles */\n.iconic-property-fill, .iconic-property-text {stroke: none !important;}\n.iconic-property-stroke {fill: none !important;}\nsvg.iconic.iconic-fluid {height:100% !important;width:100% !important;}\nsvg.iconic.iconic-sm:not(.iconic-size-md):not(.iconic-size-lg), svg.iconic.iconic-size-sm{width:16px;height:16px;}\nsvg.iconic.iconic-md:not(.iconic-size-sm):not(.iconic-size-lg), svg.iconic.iconic-size-md{width:32px;height:32px;}\nsvg.iconic.iconic-lg:not(.iconic-size-sm):not(.iconic-size-md), svg.iconic.iconic-size-lg{width:128px;height:128px;}\nsvg.iconic-sm > g.iconic-md, svg.iconic-sm > g.iconic-lg, svg.iconic-md > g.iconic-sm, svg.iconic-md > g.iconic-lg, svg.iconic-lg > g.iconic-sm, svg.iconic-lg > g.iconic-md {display: none;}\nsvg.iconic.iconic-icon-sm > g.iconic-lg, svg.iconic.iconic-icon-md > g.iconic-lg {display:none;}\nsvg.iconic-sm:not(.iconic-icon-md):not(.iconic-icon-lg) > g.iconic-sm, svg.iconic-md.iconic-icon-sm > g.iconic-sm, svg.iconic-lg.iconic-icon-sm > g.iconic-sm {display:inline;}\nsvg.iconic-md:not(.iconic-icon-sm):not(.iconic-icon-lg) > g.iconic-md, svg.iconic-sm.iconic-icon-md > g.iconic-md, svg.iconic-lg.iconic-icon-md > g.iconic-md {display:inline;}\nsvg.iconic-lg:not(.iconic-icon-sm):not(.iconic-icon-md) > g.iconic-lg, svg.iconic-sm.iconic-icon-lg > g.iconic-lg, svg.iconic-md.iconic-icon-lg > g.iconic-lg {display:inline;}";navigator&&navigator.userAgent&&/MSIE 10\.0/.test(navigator.userAgent)&&(a+="svg.iconic{zoom:1.0001;}");var b=document.createElement("style");b.id="iconic-responsive-css",b.type="text/css",b.styleSheet?b.styleSheet.cssText=a:b.appendChild(document.createTextNode(a)),(document.head||document.getElementsByTagName("head")[0]).appendChild(b)},g=function(a){if(/iconic-fluid/.test(a.getAttribute("class"))){var b,d=e(a),f=a.viewBox.baseVal.width/a.viewBox.baseVal.height;b=1===f?Math.min(d.width,d.height):1>f?d.width:d.height;var g;g=32>b?"iconic-sm":b>=32&&128>b?"iconic-md":"iconic-lg";var h=a.getAttribute("class"),i=c.test(h)?h.replace(c,g):h+" "+g;a.setAttribute("class",i)}},h=function(){var a=document.querySelectorAll(".injected-svg.iconic-fluid");Array.prototype.forEach.call(a,function(a){g(a)})};document.addEventListener("DOMContentLoaded",function(){f()}),window.addEventListener("resize",function(){h()}),b.exports={refresh:g,refreshAll:h}},{}],8:[function(b,c,d){!function(b,e){"use strict";function f(a){a=a.split(" ");for(var b={},c=a.length,d=[];c--;)b.hasOwnProperty(a[c])||(b[a[c]]=1,d.unshift(a[c]));return d.join(" ")}var g="file:"===b.location.protocol,h=e.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure","1.1"),i=Array.prototype.forEach||function(a,b){if(void 0===this||null===this||"function"!=typeof a)throw new TypeError;var c,d=this.length>>>0;for(c=0;d>c;++c)c in this&&a.call(b,this[c],c,this)},j={},k=0,l=[],m=[],n={},o=function(a){return a.cloneNode(!0)},p=function(a,b){m[a]=m[a]||[],m[a].push(b)},q=function(a){for(var b=0,c=m[a].length;c>b;b++)!function(b){setTimeout(function(){m[a][b](o(j[a]))},0)}(b)},r=function(a,c){if(void 0!==j[a])j[a]instanceof SVGSVGElement?c(o(j[a])):p(a,c);else{if(!b.XMLHttpRequest)return c("Browser does not support XMLHttpRequest"),!1;j[a]={},p(a,c);var d=new XMLHttpRequest;d.onreadystatechange=function(){if(4===d.readyState){if(404===d.status||null===d.responseXML)return c("Unable to load SVG file: "+a),g&&c("Note: SVG injection ajax calls do not work locally without adjusting security setting in your browser. Or consider using a local webserver."),c(),!1;if(!(200===d.status||g&&0===d.status))return c("There was a problem injecting the SVG: "+d.status+" "+d.statusText),!1;if(d.responseXML instanceof Document)j[a]=d.responseXML.documentElement;else if(DOMParser&&DOMParser instanceof Function){var b;try{var e=new DOMParser;b=e.parseFromString(d.responseText,"text/xml")}catch(f){b=void 0}if(!b||b.getElementsByTagName("parsererror").length)return c("Unable to parse SVG file: "+a),!1;j[a]=b.documentElement}q(a)}},d.open("GET",a),d.overrideMimeType&&d.overrideMimeType("text/xml"),d.send()}},s=function(a,c,d,e){var g=a.getAttribute("data-src")||a.getAttribute("src");if(!/svg$/i.test(g))return e("Attempted to inject a file with a non-svg extension: "+g),void 0;if(!h){var j=a.getAttribute("data-fallback")||a.getAttribute("data-png");return j?(a.setAttribute("src",j),e(null)):d?(a.setAttribute("src",d+"/"+g.split("/").pop().replace(".svg",".png")),e(null)):e("This browser does not support SVG and no PNG fallback was defined."),void 0}-1===l.indexOf(a)&&(l.push(a),a.setAttribute("src",""),r(g,function(d){if("undefined"==typeof d||"string"==typeof d)return e(d),!1;var h=a.getAttribute("id");h&&d.setAttribute("id",h);var j=a.getAttribute("title");j&&d.setAttribute("title",j);var m=[].concat(d.getAttribute("class")||[],"injected-svg",a.getAttribute("class")||[]).join(" ");d.setAttribute("class",f(m));var o=a.getAttribute("style");o&&d.setAttribute("style",o);var p=[].filter.call(a.attributes,function(a){return/^data-\w[\w\-]*$/.test(a.name)});i.call(p,function(a){a.name&&a.value&&d.setAttribute(a.name,a.value)});for(var q,r=d.querySelectorAll("defs clipPath[id]"),s=0,t=r.length;t>s;s++){q=r[s].id+"-"+k;for(var u=d.querySelectorAll('[clip-path*="'+r[s].id+'"]'),v=0,w=u.length;w>v;v++)u[v].setAttribute("clip-path","url(#"+q+")");r[s].id=q}d.removeAttribute("xmlns:a");for(var x,y,z=d.querySelectorAll("script"),A=[],B=0,C=z.length;C>B;B++)y=z[B].getAttribute("type"),y&&"application/ecmascript"!==y&&"application/javascript"!==y||(x=z[B].innerText||z[B].textContent,A.push(x),d.removeChild(z[B]));if(A.length>0&&("always"===c||"once"===c&&!n[g])){for(var D=0,E=A.length;E>D;D++)new Function(A[D])(b);n[g]=!0}a.parentNode.replaceChild(d,a),delete l[l.indexOf(a)],a=null,k++,e(d)}))},t=function(a,b,c){b=b||{};var d=b.evalScripts||"always",e=b.pngFallback||!1,f=b.each;if(void 0!==a.length){var g=0;i.call(a,function(b){s(b,d,e,function(b){f&&"function"==typeof f&&f(b),c&&a.length===++g&&c(g)})})}else a?s(a,d,e,function(b){f&&"function"==typeof f&&f(b),c&&c(1),a=null}):c&&c(0)};"object"==typeof c&&"object"==typeof c.exports?c.exports=d=t:"function"==typeof a&&a.amd?a(function(){return t}):"object"==typeof b&&(b.SVGInjector=t)}(window,document)},{}]},{},[1])(1)});
7 (function() {
8   'use strict';
9
10   angular.module('foundation.core.animation', [])
11     .service('FoundationAnimation', FoundationAnimation)
12   ;
13
14   function FoundationAnimation() {
15     var animations = [];
16     var service = {};
17
18     var initClasses        = ['ng-enter', 'ng-leave'];
19     var activeClasses      = ['ng-enter-active', 'ng-leave-active'];
20     var activeGenericClass = 'is-active';
21     var events = [
22       'webkitAnimationEnd', 'mozAnimationEnd',
23       'MSAnimationEnd', 'oanimationend',
24       'animationend', 'webkitTransitionEnd',
25       'otransitionend', 'transitionend'
26     ];
27
28     service.animate = animate;
29     service.toggleAnimation = toggleAnimation;
30
31     return service;
32
33     function toggleAnimation(element, futureState) {
34       if(futureState) {
35         element.addClass(activeGenericClass);
36       } else {
37         element.removeClass(activeGenericClass);
38       }
39     }
40
41     function animate(element, futureState, animationIn, animationOut) {
42       var timedOut = true;
43       var self = this;
44       self.cancelAnimation = cancelAnimation;
45
46       var animationClass = futureState ? animationIn: animationOut;
47       var activation = futureState;
48       var initClass = activation ? initClasses[0] : initClasses[1];
49       var activeClass = activation ? activeClasses[0] : activeClasses[1];
50       //stop animation
51       registerElement(element);
52       reset();
53       element.addClass(animationClass);
54       element.addClass(initClass);
55
56       element.addClass(activeGenericClass);
57
58       //force a "tick"
59       reflow();
60
61       //activate
62       element[0].style.transitionDuration = '';
63       element.addClass(activeClass);
64
65       element.one(events.join(' '), function() {
66         finishAnimation();
67       });
68
69       setTimeout(function() {
70         if(timedOut) {
71           finishAnimation();
72         }
73       }, 3000);
74
75       function finishAnimation() {
76         deregisterElement(element);
77         reset(); //reset all classes
78         element[0].style.transitionDuration = '';
79         element.removeClass(!activation ? activeGenericClass : ''); //if not active, remove active class
80         reflow();
81         timedOut = false;
82       }
83
84
85       function cancelAnimation(element) {
86         deregisterElement(element);
87         angular.element(element).off(events.join(' ')); //kill all animation event handlers
88         timedOut = false;
89       }
90
91       function registerElement(el) {
92         var elObj = {
93           el: el,
94           animation: self
95         };
96
97         //kill in progress animations
98         var inProgress = animations.filter(function(obj) {
99           return obj.el === el;
100         });
101         if(inProgress.length > 0) {
102           var target = inProgress[0].el[0];
103
104           inProgress[0].animation.cancelAnimation(target);
105         }
106
107         animations.push(elObj);
108       }
109
110       function deregisterElement(el) {
111         var index;
112         var currentAnimation = animations.filter(function(obj, ind) {
113           if(obj.el === el) {
114             index = ind;
115           }
116         });
117
118         if(index >= 0) {
119           animations.splice(index, 1);
120         }
121
122       }
123
124       function reflow() {
125         return element[0].offsetWidth;
126       }
127
128       function reset() {
129         element[0].style.transitionDuration = 0;
130         element.removeClass(initClasses.join(' ') + ' ' + activeClasses.join(' ') + ' ' + animationIn + ' ' + animationOut);
131       }
132     }
133   }
134
135 })();
136
137 (function() {
138   'use strict';
139
140   angular.module('foundation.core', [
141       'foundation.core.animation'
142     ])
143     .service('FoundationApi', FoundationApi)
144     .service('FoundationAdapter', FoundationAdapter)
145     .factory('Utils', Utils)
146   ;
147
148   FoundationApi.$inject = ['FoundationAnimation'];
149
150   function FoundationApi(FoundationAnimation) {
151     var listeners  = {};
152     var settings   = {};
153     var uniqueIds  = [];
154     var service    = {};
155
156     service.subscribe           = subscribe;
157     service.unsubscribe         = unsubscribe;
158     service.publish             = publish;
159     service.getSettings         = getSettings;
160     service.modifySettings      = modifySettings;
161     service.generateUuid        = generateUuid;
162     service.toggleAnimate       = toggleAnimate;
163     service.closeActiveElements = closeActiveElements;
164     service.animate             = animate;
165
166     return service;
167
168     function subscribe(name, callback) {
169       if (!listeners[name]) {
170         listeners[name] = [];
171       }
172
173       listeners[name].push(callback);
174       return true;
175     }
176
177     function unsubscribe(name, callback) {
178       if (listeners[name] !== undefined) {
179         delete listeners[name];
180       }
181       if (typeof callback == 'function') {
182           callback.call(this);
183       }
184     }
185
186     function publish(name, msg) {
187       if (!listeners[name]) {
188         listeners[name] = [];
189       }
190
191       listeners[name].forEach(function(cb) {
192         cb(msg);
193       });
194
195       return;
196     }
197
198     function getSettings() {
199       return settings;
200     }
201
202     function modifySettings(tree) {
203       settings = angular.extend(settings, tree);
204       return settings;
205     }
206
207     function generateUuid() {
208       var uuid = '';
209
210       //little trick to produce semi-random IDs
211       do {
212         uuid += 'zf-uuid-';
213         for (var i=0; i<15; i++) {
214           uuid += Math.floor(Math.random()*16).toString(16);
215         }
216       } while(!uniqueIds.indexOf(uuid));
217
218       uniqueIds.push(uuid);
219       return uuid;
220     }
221
222     function toggleAnimate(element, futureState) {
223       FoundationAnimation.toggleAnimate(element, futureState);
224     }
225
226     function closeActiveElements(options) {
227       var self = this;
228       options = options || {};
229       var activeElements = document.querySelectorAll('.is-active[zf-closable]');
230       // action sheets are nested zf-closable elements, so we have to target the parent
231       var nestedActiveElements = document.querySelectorAll('[zf-closable] > .is-active')
232       
233       if (activeElements.length) {
234         angular.forEach(activeElements, function(el) {
235           if (options.exclude !== el.id) {
236             self.publish(el.id, 'close');
237           }
238         });
239       }
240       if (nestedActiveElements.length) {
241         angular.forEach(nestedActiveElements, function(el) {
242           var parentId = el.parentNode.id;
243           if (options.exclude !== parentId) {
244             self.publish(parentId, 'close');
245           }
246         })
247       }
248     }
249
250     function animate(element, futureState, animationIn, animationOut) {
251       FoundationAnimation.animate(element, futureState, animationIn, animationOut);
252     }
253   }
254
255   FoundationAdapter.$inject = ['FoundationApi'];
256
257   function FoundationAdapter(foundationApi) {
258
259     var service    = {};
260
261     service.activate = activate;
262     service.deactivate = deactivate;
263
264     return service;
265
266     function activate(target) {
267       foundationApi.publish(target, 'show');
268     }
269
270     function deactivate(target) {
271       foundationApi.publish(target, 'hide');
272     }
273   }
274
275
276   function Utils() {
277     var utils = {};
278
279     utils.throttle = throttleUtil;
280
281     return utils;
282
283     function throttleUtil(func, delay) {
284       var timer = null;
285
286       return function () {
287         var context = this, args = arguments;
288
289         if (timer === null) {
290           timer = setTimeout(function () {
291             func.apply(context, args);
292             timer = null;
293           }, delay);
294         }
295       };
296     }
297   }
298
299 })();
300
301 (function() {
302   'use strict';
303
304   angular.module('foundation.dynamicRouting.animations', ['foundation.dynamicRouting'])
305     .directive('uiView', uiView)
306   ;
307
308   uiView.$inject = ['$rootScope', '$state'];
309
310   function uiView($rootScope, $state) {
311     var directive = {
312       restrict : 'ECA',
313       priority : -400,
314       link     : link
315     };
316
317     return directive;
318
319     function link(scope, element) {
320       var animation = {};
321       var animationEnded = false;
322       var presetHeight;
323
324       var cleanup = [
325         $rootScope.$on('$stateChangeStart', onStateChangeStart),
326         $rootScope.$on('$stateChangeError', onStateChangeError),
327         scope.$on('$stateChangeSuccess', onStateChangeSuccess),
328         scope.$on('$viewContentAnimationEnded', onViewContentAnimationEnded)
329       ];
330
331       var destroyed = scope.$on('$destroy', function onDestroy() {
332         angular.forEach(cleanup, function (cb) {
333           if (angular.isFunction(cb)) {
334             cb();
335           }
336         });
337
338         destroyed();
339       });
340
341       function onStateChangeStart(event, toState, toParams, fromState, fromParams) {
342
343         if (fromState.animation) {
344           if (!fromState.animation.leave && !toState.animation.leave) {
345             return;
346           }
347           else {
348              animationRouter(event, toState, fromState);
349           }
350         }
351       }
352
353       function animationRouter(event, toState, fromState) {
354         if (!animationEnded) {
355           resetParent();
356           prepareParent();
357
358           element.removeClass(fromState.animation.leave);
359         }
360         else {
361           prepareParent();
362
363           element.addClass(fromState.animation.leave);
364         }
365
366       }
367
368       function onStateChangeError() {
369         if(animation.leave) {
370           element.removeClass(animation.leave);
371         }
372
373         resetParent(); //reset parent if state change fails
374       }
375
376       function onStateChangeSuccess() {
377         resetParent();
378         if ($state.includes(getState()) && animation.enter) {
379           element.addClass(animation.enter);
380         }
381       }
382
383       function onViewContentAnimationEnded(event) {
384         if (event.targetScope === scope && animation.enter) {
385           element.removeClass(animation.enter);
386         }
387         
388         animationEnded = true;
389
390       }
391
392       function getState() {
393         var view  = element.data('$uiView');
394         var state = view && view.state && view.state.self;
395
396         if (state) {
397           angular.extend(animation, state.animation);
398         }
399
400         return state;
401       }
402
403       function resetParent() {
404         element.parent().removeClass('position-absolute');
405         if(presetHeight !== true) {
406           element.parent()[0].style.height = null;
407         }
408       }
409
410       function prepareParent() {
411         var parentHeight = parseInt(element.parent()[0].style.height);
412         var elHeight = parseInt(window.getComputedStyle(element[0], null).getPropertyValue('height'));
413         var tempHeight = parentHeight > 0 ? parentHeight : elHeight > 0 ? elHeight : '';
414
415         if(parentHeight > 0) {
416           presetHeight = true;
417         }
418
419         element.parent()[0].style.height = tempHeight + 'px';
420         element.parent().addClass('position-absolute');
421       }
422     }
423   }
424
425 })();
426 (function() {
427   'use strict';
428
429   angular.module('foundation.dynamicRouting', ['ui.router'])
430     .provider('$FoundationState', FoundationState)
431     .controller('DefaultController', DefaultController)
432     .config(DynamicRoutingConfig)
433     .run(DynamicRoutingRun)
434   ;
435
436   FoundationState.$inject = ['$stateProvider'];
437
438   function FoundationState($stateProvider) {
439     var complexViews = {};
440
441     this.registerDynamicRoutes = function(routes) {
442       var dynamicRoutes = routes || foundationRoutes;
443
444       angular.forEach(dynamicRoutes, function(page) {
445         if (page.hasComposed) {
446           if (!angular.isDefined(complexViews[page.parent])) {
447             complexViews[page.parent] = { children: {} };
448           }
449
450           if (page.controller) {
451             page.controller = getController(page);
452           }
453
454           complexViews[page.parent].children[page.name] = page;
455
456         } else if (page.composed) {
457           if(!angular.isDefined(complexViews[page.name])) {
458             complexViews[page.name] = { children: {} };
459           }
460
461           if (page.controller) {
462             page.controller = getController(page);
463           }
464
465           angular.extend(complexViews[page.name], page);
466         } else {
467           var state = {
468             url: page.url,
469             templateUrl: page.path,
470             abstract: page.abstract || false,
471             parent: page.parent || '',
472             controller: getController(page),
473             data: getData(page),
474             animation: buildAnimations(page),
475           };
476           
477           $stateProvider.state(page.name, state);
478         }
479       });
480
481       angular.forEach(complexViews, function(page) {
482           var state = {
483             url: page.url,
484             parent: page.parent || '',
485             abstract: page.abstract || false,
486             data: getData(page),
487             animation: buildAnimations(page),
488             views: {
489               '': buildState(page.path, page)
490             }
491           };
492           
493           angular.forEach(page.children, function(sub) {
494             state.views[sub.name + '@' + page.name] = buildState(sub.path, page);
495           });
496
497           $stateProvider.state(page.name, state);
498       });
499     };
500
501     this.$get = angular.noop;
502     
503     function getData(page) {
504       var data = { vars: {} };
505       if (page.data) {
506         if (typeof page.data.vars === "object") {
507           data.vars = page.data.vars;
508         }
509         delete page.data.vars;
510         angular.extend(data, page.data);
511       }
512       delete page.data;
513       angular.extend(data.vars, page);
514       return data;
515     }
516     
517     function buildState(path, state) {
518       return {
519         templateUrl: path,
520         controller: getController(state),
521       };
522     }
523
524     function getController(state) {
525       var ctrl = state.controller || 'DefaultController';
526
527       if (!/\w\s+as\s+\w/.test(ctrl)) {
528         ctrl += ' as PageCtrl';
529       }
530
531       return ctrl;
532     }
533
534     function buildAnimations(state) {
535       var animations = {};
536
537       if (state.animationIn) {
538         animations.enter = state.animationIn;
539       }
540
541       if (state.animationOut) {
542         animations.leave = state.animationOut;
543       }
544
545       return animations;
546     }
547   }
548
549   DefaultController.$inject = ['$scope', '$stateParams', '$state'];
550
551   function DefaultController($scope, $stateParams, $state) {
552     var params = {};
553     angular.forEach($stateParams, function(value, key) {
554       params[key] = value;
555     });
556
557     $scope.params = params;
558     $scope.current = $state.current.name;
559
560     if($state.current.views) {
561       $scope.vars = $state.current.data.vars;
562       $scope.composed = $state.current.data.vars.children;
563     } else {
564       $scope.vars = $state.current.data.vars;
565     }
566   }
567
568   DynamicRoutingConfig.$inject = ['$FoundationStateProvider'];
569
570   function DynamicRoutingConfig(FoundationStateProvider) {
571     FoundationStateProvider.registerDynamicRoutes(foundationRoutes);
572   }
573
574   DynamicRoutingRun.$inject = ['$rootScope', '$state', '$stateParams'];
575
576   function DynamicRoutingRun($rootScope, $state, $stateParams) {
577     $rootScope.$state = $state;
578     $rootScope.$stateParams = $stateParams;
579   }
580
581 })();
582
583 (function() {
584   'use strict';
585
586   angular.module('foundation.mediaquery', ['foundation.core'])
587     .run(mqInitRun)
588     .factory('FoundationMQInit', FoundationMQInit)
589     .factory('mqHelpers', mqHelpers)
590     .service('FoundationMQ', FoundationMQ)
591   ;
592
593   mqInitRun.$inject = ['FoundationMQInit'];
594
595   function mqInitRun(mqInit) {
596     mqInit.init();
597   }
598
599   FoundationMQInit.$inject = ['mqHelpers', 'FoundationApi', 'Utils'];
600
601   function FoundationMQInit(helpers, foundationApi, u){
602     var factory = {};
603     var namedQueries = {
604       'default' : 'only screen',
605       landscape : 'only screen and (orientation: landscape)',
606       portrait : 'only screen and (orientation: portrait)',
607       retina : 'only screen and (-webkit-min-device-pixel-ratio: 2),' +
608         'only screen and (min--moz-device-pixel-ratio: 2),' +
609         'only screen and (-o-min-device-pixel-ratio: 2/1),' +
610         'only screen and (min-device-pixel-ratio: 2),' +
611         'only screen and (min-resolution: 192dpi),' +
612         'only screen and (min-resolution: 2dppx)'
613     };
614
615     factory.init = init;
616
617     return factory;
618
619     function init() {
620       var mediaQueries;
621       var extractedMedia;
622       var mediaObject;
623
624       helpers.headerHelper(['foundation-mq']);
625       extractedMedia = helpers.getStyle('.foundation-mq', 'font-family');
626
627       mediaQueries = helpers.parseStyleToObject((extractedMedia));
628
629       for(var key in mediaQueries) {
630         mediaQueries[key] = 'only screen and (min-width: ' + mediaQueries[key].replace('rem', 'em') + ')';
631       }
632
633
634       foundationApi.modifySettings({
635         mediaQueries: angular.extend(mediaQueries, namedQueries)
636       });
637
638       window.addEventListener('resize', u.throttle(function() {
639         foundationApi.publish('resize', 'window resized');
640       }, 50));
641
642     }
643   }
644
645
646   function mqHelpers() {
647     var factory = {};
648
649     factory.headerHelper = headerHelper;
650     factory.getStyle = getStyle;
651     factory.parseStyleToObject = parseStyleToObject;
652
653     return factory;
654
655     function headerHelper(classArray) {
656       var i = classArray.length;
657       var head = angular.element(document.querySelectorAll('head'));
658
659       while(i--) {
660         head.append('<meta class="' + classArray[i] + '" />');
661       }
662
663       return;
664     }
665
666     function getStyle(selector, styleName) {
667       var elem  = document.querySelectorAll(selector)[0];
668       var style = window.getComputedStyle(elem, null);
669
670       return style.getPropertyValue('font-family');
671     }
672
673       // https://github.com/sindresorhus/query-string
674     function parseStyleToObject(str) {
675       var styleObject = {};
676
677       if (typeof str !== 'string') {
678         return styleObject;
679       }
680
681       str = str.trim().slice(1, -1); // browsers re-quote string style values
682
683       if (!str) {
684         return styleObject;
685       }
686
687       styleObject = str.split('&').reduce(function(ret, param) {
688         var parts = param.replace(/\+/g, ' ').split('=');
689         var key = parts[0];
690         var val = parts[1];
691         key = decodeURIComponent(key);
692
693         // missing `=` should be `null`:
694         // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters
695         val = val === undefined ? null : decodeURIComponent(val);
696
697         if (!ret.hasOwnProperty(key)) {
698           ret[key] = val;
699         } else if (Array.isArray(ret[key])) {
700           ret[key].push(val);
701         } else {
702           ret[key] = [ret[key], val];
703         }
704         return ret;
705       }, {});
706
707       return styleObject;
708     }
709   }
710
711   FoundationMQ.$inject = ['FoundationApi'];
712
713   function FoundationMQ(foundationApi) {
714     var service = [];
715
716     service.getMediaQueries = getMediaQueries;
717     service.match = match;
718     service.collectScenariosFromElement = collectScenariosFromElement;
719
720     return service;
721
722     function getMediaQueries() {
723       return foundationApi.getSettings().mediaQueries;
724     }
725
726     function match(scenarios) {
727       var count   = scenarios.length;
728       var queries = service.getMediaQueries();
729       var matches = [];
730
731       if (count > 0) {
732         while (count--) {
733           var mq;
734           var rule = scenarios[count].media;
735
736           if (queries[rule]) {
737             mq = matchMedia(queries[rule]);
738           } else {
739             mq = matchMedia(rule);
740           }
741
742           if (mq.matches) {
743             matches.push({ ind: count});
744           }
745         }
746       }
747
748       return matches;
749     }
750
751     // Collects a scenario object and templates from element
752     function collectScenariosFromElement(parentElement) {
753       var scenarios = [];
754       var templates = [];
755
756       var elements = parentElement.children();
757       var i        = 0;
758
759       angular.forEach(elements, function(el) {
760         var elem = angular.element(el);
761
762
763         //if no source or no html, capture element itself
764         if (!elem.attr('src') || !elem.attr('src').match(/.html$/)) {
765           templates[i] = elem;
766           scenarios[i] = { media: elem.attr('media'), templ: i };
767         } else {
768           scenarios[i] = { media: elem.attr('media'), src: elem.attr('src') };
769         }
770
771         i++;
772       });
773
774       return {
775         scenarios: scenarios,
776         templates: templates
777       };
778     }
779   }
780 })();
781
782 angular.module('markdown', [])
783   .directive('markdown', function() {
784     return {
785       restrict: 'A',
786       link: function(scope, element, attrs, controller) {
787         element.html(marked(element.html()));
788       }
789     };
790
791 });
792
793 'use strict';
794
795 (function(){
796   var svgDirectives = {};
797
798   angular.forEach([
799       'clipPath',
800       'colorProfile',
801       'src',
802       'cursor',
803       'fill',
804       'filter',
805       'marker',
806       'markerStart',
807       'markerMid',
808       'markerEnd',
809       'mask',
810       'stroke'
811     ],
812     function(attr) {
813       svgDirectives[attr] = [
814           '$rootScope',
815           '$location',
816           '$interpolate',
817           '$sniffer',
818           'urlResolve',
819           'computeSVGAttrValue',
820           'svgAttrExpressions',
821           function(
822               $rootScope,
823               $location,
824               $interpolate,
825               $sniffer,
826               urlResolve,
827               computeSVGAttrValue,
828               svgAttrExpressions) {
829             return {
830               restrict: 'A',
831               link: function(scope, element, attrs) {
832                 var initialUrl;
833
834                 //Only apply to svg elements to avoid unnecessary observing
835                 //Check that is in html5Mode and that history is supported
836                 if ((!svgAttrExpressions.SVG_ELEMENT.test(element[0] &&
837                     element[0].toString())) ||
838                   !$location.$$html5 ||
839                   !$sniffer.history) return;
840
841                 //Assumes no expressions, since svg is unforgiving of xml violations
842                 initialUrl = attrs[attr];
843                 attrs.$observe(attr, updateValue);
844                 $rootScope.$on('$locationChangeSuccess', updateValue);
845
846                 function updateValue () {
847                   var newVal = computeSVGAttrValue(initialUrl);
848                   //Prevent recursive updating
849                   if (newVal && attrs[attr] !== newVal) attrs.$set(attr, newVal);
850                 }
851               }
852             };
853           }];
854   });
855
856   angular.module('ngSVGAttributes', []).
857     factory('urlResolve', [function() {
858       //Duplicate of urlResolve & urlParsingNode in angular core
859       var urlParsingNode = document.createElement('a');
860       return function urlResolve(url) {
861         urlParsingNode.setAttribute('href', url);
862         return urlParsingNode;
863       };
864     }]).
865     value('svgAttrExpressions', {
866       FUNC_URI: /^url\((.*)\)$/,
867       SVG_ELEMENT: /SVG[a-zA-Z]*Element/,
868       HASH_PART: /#.*/
869     }).
870     factory('computeSVGAttrValue', [
871                 '$location', '$sniffer', 'svgAttrExpressions', 'urlResolve',
872         function($location,   $sniffer,   svgAttrExpressions,   urlResolve) {
873           return function computeSVGAttrValue(url) {
874             var match, fullUrl;
875             if (match = svgAttrExpressions.FUNC_URI.exec(url)) {
876               //hash in html5Mode, forces to be relative to current url instead of base
877               if (match[1].indexOf('#') === 0) {
878                 fullUrl = $location.absUrl().
879                   replace(svgAttrExpressions.HASH_PART, '') +
880                   match[1];
881               }
882               //Presumably links to external SVG document
883               else {
884                 fullUrl = urlResolve(match[1]);
885               }
886             }
887             return fullUrl ? 'url(' + fullUrl + ')' : null;
888           };
889         }
890       ]
891     ).
892     directive(svgDirectives);
893 }());
894
895 (function() {
896   'use strict';
897
898   angular.module('foundation.accordion', [])
899     .controller('ZfAccordionController', zfAccordionController)
900     .directive('zfAccordion', zfAccordion)
901     .directive('zfAccordionItem', zfAccordionItem)
902   ;
903
904   zfAccordionController.$inject = ['$scope'];
905
906   function zfAccordionController($scope) {
907     var controller = this;
908     var sections = controller.sections = $scope.sections = [];
909     var multiOpen = controller.multiOpen = $scope.multiOpen = $scope.multiOpen || false;
910     var collapsible = controller.collapsible = $scope.collapsible = $scope.multiOpen || $scope.collapsible || true; //multi open infers a collapsible true
911     var autoOpen = controller.autoOpen = $scope.autoOpen = $scope.autoOpen || true; //auto open opens first tab on render
912
913     controller.select = function(selectSection) {
914       sections.forEach(function(section) {
915         //if multi open is allowed, toggle a tab
916         if(controller.multiOpen) {
917           if(section.scope === selectSection) {
918             section.scope.active = !section.scope.active;
919           }
920         } else {
921           //non  multi open will close all tabs and open one
922           if(section.scope === selectSection) {
923             //if collapsible is allowed, a tab will toggle
924             section.scope.active = collapsible ? !section.scope.active : true;
925           } else {
926             section.scope.active = false;
927           }
928         }
929
930       });
931     };
932
933     controller.addSection = function addsection(sectionScope) {
934       sections.push({ scope: sectionScope });
935
936       if(sections.length === 1 && autoOpen === true) {
937         sections[0].active = true;
938         sections[0].scope.active = true;
939       }
940     };
941
942     controller.closeAll = function() {
943       sections.forEach(function(section) {
944         section.scope.active = false;
945       });
946     };
947   }
948
949   function zfAccordion() {
950     var directive = {
951       restrict: 'EA',
952       transclude: 'true',
953       replace: true,
954       templateUrl: 'components/accordion/accordion.html',
955       controller: 'ZfAccordionController',
956       scope: {
957         multiOpen: '@?',
958         collapsible: '@?',
959         autoOpen: '@?'
960       },
961       link: link
962     };
963
964     return directive;
965
966     function link(scope, element, attrs, controller) {
967       scope.multiOpen = controller.multiOpen = scope.multiOpen === "true" ? true : false;
968       scope.collapsible = controller.collapsible = scope.collapsible === "true" ? true : false;
969       scope.autoOpen = controller.autoOpen = scope.autoOpen === "true" ? true : false;
970     }
971   }
972
973   //accordion item
974   function zfAccordionItem() {
975     var directive = {
976         restrict: 'EA',
977         templateUrl: 'components/accordion/accordion-item.html',
978         transclude: true,
979         scope: {
980           title: '@'
981         },
982         require: '^zfAccordion',
983         replace: true,
984         controller: function() {},
985         link: link
986     };
987
988     return directive;
989
990     function link(scope, element, attrs, controller, transclude) {
991       scope.active = false;
992       controller.addSection(scope);
993
994       scope.activate = function() {
995         controller.select(scope);
996       };
997
998     }
999   }
1000
1001 })();
1002
1003 (function() {
1004   'use strict';
1005
1006   angular.module('foundation.actionsheet', ['foundation.core'])
1007     .controller('ZfActionSheetController', zfActionSheetController)
1008     .directive('zfActionSheet', zfActionSheet)
1009     .directive('zfAsContent', zfAsContent)
1010     .directive('zfAsButton', zfAsButton)
1011     .service('FoundationActionSheet', FoundationActionSheet)
1012   ;
1013
1014   FoundationActionSheet.$inject = ['FoundationApi'];
1015
1016   function FoundationActionSheet(foundationApi) {
1017     var service    = {};
1018
1019     service.activate = activate;
1020     service.deactivate = deactivate;
1021
1022     return service;
1023
1024     //target should be element ID
1025     function activate(target) {
1026       foundationApi.publish(target, 'show');
1027     }
1028
1029     //target should be element ID
1030     function deactivate(target) {
1031       foundationApi.publish(target, 'hide');
1032     }
1033   }
1034
1035   zfActionSheetController.$inject = ['$scope', 'FoundationApi'];
1036
1037   function zfActionSheetController($scope, foundationApi) {
1038     var controller = this;
1039     var content = controller.content = $scope.content;
1040     var container = controller.container = $scope.container;
1041     var body = angular.element(document.body);
1042
1043     controller.registerContent = function(scope) {
1044       content = scope;
1045       content.active = false;
1046     };
1047
1048     controller.registerContainer = function(scope) {
1049       container = scope;
1050       container.active = false;
1051     };
1052
1053     controller.toggle = toggle;
1054     controller.hide = hide;
1055
1056     controller.registerListener = function() {
1057       document.body.addEventListener('click', listenerLogic);
1058     };
1059
1060     controller.deregisterListener = function() {
1061       document.body.removeEventListener('click', listenerLogic);
1062     }
1063
1064     function listenerLogic(e) {
1065       var el = e.target;
1066       var insideActionSheet = false;
1067
1068       do {
1069         if(el.classList && el.classList.contains('action-sheet-container')) {
1070           insideActionSheet = true;
1071           break;
1072         }
1073
1074       } while ((el = el.parentNode));
1075
1076       if(!insideActionSheet) {
1077         // if the element has a toggle attribute, do nothing
1078         if (e.target.attributes['zf-toggle'] || e.target.attributes['zf-hard-toggle']) {
1079           return;
1080         };
1081         // if the element is outside the action sheet and is NOT a toggle element, hide
1082         hide();
1083       }
1084     }
1085
1086     function hide() {
1087       content.hide();
1088       container.hide();
1089
1090       content.$apply();
1091       container.$apply();
1092     }
1093
1094     function toggle() {
1095       content.toggle();
1096       container.toggle();
1097
1098       content.$apply();
1099       container.$apply();
1100     }
1101   }
1102
1103   zfActionSheet.$inject = ['FoundationApi'];
1104
1105   function zfActionSheet(foundationApi) {
1106     var directive = {
1107       restrict: 'EA',
1108       transclude: true,
1109       replace: true,
1110       templateUrl: 'components/actionsheet/actionsheet.html',
1111       controller: 'ZfActionSheetController',
1112       compile: compile
1113     };
1114
1115     return directive;
1116
1117     function compile() {
1118
1119       return {
1120         pre: preLink,
1121         post: postLink
1122       };
1123
1124       function preLink(scope, iElement, iAttrs) {
1125         iAttrs.$set('zf-closable', 'actionsheet');
1126       }
1127
1128       function postLink(scope, element, attrs, controller) {
1129         var id = attrs.id || foundationApi.generateUuid();
1130         attrs.$set('id', id);
1131
1132         scope.active = false;
1133
1134         foundationApi.subscribe(id, function(msg) {
1135           if (msg === 'toggle') {
1136             controller.toggle();
1137           }
1138
1139           if (msg === 'hide' || msg === 'close') {
1140             controller.hide();
1141           }
1142
1143         });
1144
1145         controller.registerContainer(scope);
1146
1147         scope.toggle = function() {
1148           scope.active = !scope.active;
1149           return;
1150         };
1151
1152         scope.hide = function() {
1153           scope.active = false;
1154           return;
1155         };
1156       }
1157     }
1158   }
1159
1160   zfAsContent.$inject = ['FoundationApi'];
1161
1162   function zfAsContent(foundationApi) {
1163     var directive = {
1164       restrict: 'EA',
1165       transclude: true,
1166       replace: true,
1167       templateUrl: 'components/actionsheet/actionsheet-content.html',
1168       require: '^zfActionSheet',
1169       scope: {
1170         position: '@?'
1171       },
1172       link: link
1173     };
1174
1175     return directive;
1176
1177     function link(scope, element, attrs, controller) {
1178       scope.active = false;
1179       scope.position = scope.position || 'bottom';
1180       controller.registerContent(scope);
1181
1182       scope.toggle = function() {
1183         scope.active = !scope.active;
1184         if(scope.active) {
1185           controller.registerListener();
1186         } else {
1187           controller.deregisterListener();
1188         }
1189
1190         return;
1191       };
1192
1193       scope.hide = function() {
1194         scope.active = false;
1195         controller.deregisterListener();
1196         return;
1197       };
1198     }
1199   }
1200
1201   zfAsButton.$inject = ['FoundationApi'];
1202
1203   function zfAsButton(foundationApi) {
1204     var directive = {
1205       restrict: 'EA',
1206       transclude: true,
1207       replace: true,
1208       templateUrl: 'components/actionsheet/actionsheet-button.html',
1209       require: '^zfActionSheet',
1210       scope: {
1211         title: '@?'
1212       },
1213       link: link
1214     }
1215
1216     return directive;
1217
1218     function link(scope, element, attrs, controller) {
1219
1220       element.on('click', function(e) {
1221         controller.toggle();
1222         e.preventDefault();
1223       });
1224
1225     }
1226   }
1227
1228 })();
1229
1230 (function() {
1231   'use strict';
1232
1233   angular.module('foundation.common', ['foundation.core'])
1234     .directive('zfClose', zfClose)
1235     .directive('zfOpen', zfOpen)
1236     .directive('zfToggle', zfToggle)
1237     .directive('zfEscClose', zfEscClose)
1238     .directive('zfSwipeClose', zfSwipeClose)
1239     .directive('zfHardToggle', zfHardToggle)
1240   ;
1241
1242   zfClose.$inject = ['FoundationApi'];
1243
1244   function zfClose(foundationApi) {
1245     var directive = {
1246       restrict: 'A',
1247       link: link
1248     };
1249
1250     return directive;
1251
1252     function link(scope, element, attrs) {
1253       var targetId = '';
1254       if (attrs.zfClose) {
1255         targetId = attrs.zfClose;
1256       } else {
1257         var parentElement= false;
1258         var tempElement = element.parent();
1259         //find parent modal
1260         while(parentElement === false) {
1261           if(tempElement[0].nodeName == 'BODY') {
1262             parentElement = '';
1263           }
1264
1265           if(typeof tempElement.attr('zf-closable') !== 'undefined' && tempElement.attr('zf-closable') !== false) {
1266             parentElement = tempElement;
1267           }
1268
1269           tempElement = tempElement.parent();
1270         }
1271         targetId = parentElement.attr('id');
1272       }
1273
1274       element.on('click', function(e) {
1275         foundationApi.publish(targetId, 'close');
1276         e.preventDefault();
1277       });
1278     }
1279   }
1280
1281   zfOpen.$inject = ['FoundationApi'];
1282
1283   function zfOpen(foundationApi) {
1284     var directive = {
1285       restrict: 'A',
1286       link: link
1287     };
1288
1289     return directive;
1290
1291     function link(scope, element, attrs) {
1292       element.on('click', function(e) {
1293         foundationApi.publish(attrs.zfOpen, 'open');
1294         e.preventDefault();
1295       });
1296     }
1297   }
1298
1299   zfToggle.$inject = ['FoundationApi'];
1300
1301   function zfToggle(foundationApi) {
1302     var directive = {
1303       restrict: 'A',
1304       link: link
1305     }
1306
1307     return directive;
1308
1309     function link(scope, element, attrs) {
1310       element.on('click', function(e) {
1311         foundationApi.publish(attrs.zfToggle, 'toggle');
1312         e.preventDefault();
1313       });
1314     }
1315   }
1316
1317   zfEscClose.$inject = ['FoundationApi'];
1318
1319   function zfEscClose(foundationApi) {
1320     var directive = {
1321       restrict: 'A',
1322       link: link
1323     };
1324
1325     return directive;
1326
1327     function link(scope, element, attrs) {
1328       element.on('keyup', function(e) {
1329         if (e.keyCode === 27) {
1330           foundationApi.closeActiveElements();
1331         }
1332         e.preventDefault();
1333       });
1334     }
1335   }
1336
1337   zfSwipeClose.$inject = ['FoundationApi'];
1338
1339   function zfSwipeClose(foundationApi) {
1340     var directive = {
1341       restrict: 'A',
1342       link: link
1343     };
1344     return directive;
1345
1346     function link($scope, element, attrs) {
1347       var swipeDirection;
1348       var hammerElem;
1349       if (Hammer) {
1350         hammerElem = new Hammer(element[0]);
1351         // set the options for swipe (to make them a bit more forgiving in detection)
1352         hammerElem.get('swipe').set({
1353           direction: Hammer.DIRECTION_ALL,
1354           threshold: 5, // this is how far the swipe has to travel
1355           velocity: 0.5 // and this is how fast the swipe must travel
1356         });
1357       }
1358       // detect what direction the directive is pointing
1359       switch (attrs.zfSwipeClose) {
1360         case 'right':
1361           swipeDirection = 'swiperight';
1362           break;
1363         case 'left':
1364           swipeDirection = 'swipeleft';
1365           break;
1366         case 'up':
1367           swipeDirection = 'swipeup';
1368           break;
1369         case 'down':
1370           swipeDirection = 'swipedown';
1371           break;
1372         default:
1373           swipeDirection = 'swipe';
1374       }
1375       hammerElem.on(swipeDirection, function() {
1376         foundationApi.publish(attrs.id, 'close');
1377       });
1378     }
1379   }
1380
1381   zfHardToggle.$inject = ['FoundationApi'];
1382
1383   function zfHardToggle(foundationApi) {
1384     var directive = {
1385       restrict: 'A',
1386       link: link
1387     };
1388
1389     return directive;
1390
1391     function link(scope, element, attrs) {
1392       element.on('click', function(e) {
1393         foundationApi.closeActiveElements({exclude: attrs.zfHardToggle});
1394         foundationApi.publish(attrs.zfHardToggle, 'toggle');
1395         e.preventDefault();
1396       });
1397     }
1398   }
1399
1400 })();
1401
1402 (function () {
1403   'use strict';
1404
1405   angular.module('foundation.iconic', [])
1406     .provider('Iconic', Iconic)
1407     .directive('zfIconic', zfIconic)
1408   ;
1409
1410   // iconic wrapper
1411   function Iconic() {
1412     // default path
1413     var assetPath = 'assets/img/iconic/';
1414
1415     /**
1416      * Sets the path used to locate the iconic SVG files
1417      * @param {string} path - the base path used to locate the iconic SVG files
1418      */
1419     this.setAssetPath = function (path) {
1420       assetPath = angular.isString(path) ? path : assetPath;
1421     };
1422
1423     /**
1424      * Service implementation
1425      * @returns {{}}
1426      */
1427     this.$get = function () {
1428       var iconicObject = new IconicJS();
1429
1430       var service = {
1431         getAccess: getAccess,
1432         getAssetPath: getAssetPath
1433       };
1434
1435       return service;
1436
1437       /**
1438        *
1439        * @returns {Window.IconicJS}
1440        */
1441       function getAccess() {
1442         return iconicObject;
1443       }
1444
1445       /**
1446        *
1447        * @returns {string}
1448        */
1449       function getAssetPath() {
1450         return assetPath;
1451       }
1452     };
1453   }
1454
1455   zfIconic.$inject = ['Iconic', 'FoundationApi', '$compile'];
1456
1457   function zfIconic(iconic, foundationApi, $compile) {
1458     var directive = {
1459       restrict: 'A',
1460       template: '<img ng-transclude>',
1461       transclude: true,
1462       replace: true,
1463       scope: {
1464         dynSrc: '=?',
1465         dynIcon: '=?',
1466         size: '@?',
1467         icon: '@',
1468         iconDir: '@?'
1469       },
1470       compile: compile
1471     };
1472
1473     return directive;
1474
1475     function compile() {
1476       var contents, assetPath;
1477
1478       return {
1479         pre: preLink,
1480         post: postLink
1481       };
1482
1483       function preLink(scope, element, attrs) {
1484
1485         if (scope.iconDir) {
1486           // path set via attribute
1487           assetPath = scope.iconDir;
1488         } else {
1489           // default path
1490           assetPath = iconic.getAssetPath();
1491         }
1492         // make sure ends with /
1493         if (assetPath.charAt(assetPath.length - 1) !== '/') {
1494           assetPath += '/';
1495         }
1496
1497         if (scope.dynSrc) {
1498           attrs.$set('data-src', scope.dynSrc);
1499         } else if (scope.dynIcon) {
1500           attrs.$set('data-src', assetPath + scope.dynIcon + '.svg');
1501         } else {
1502           if (scope.icon) {
1503             attrs.$set('data-src', assetPath + scope.icon + '.svg');
1504           } else {
1505             // To support expressions on data-src
1506             attrs.$set('data-src', attrs.src);
1507           }
1508         }
1509
1510         // check if size already added as class
1511         if (!element.hasClass('iconic-sm') && !element.hasClass('iconic-md') && !element.hasClass('iconic-lg')) {
1512           var iconicClass;
1513           switch (scope.size) {
1514             case 'small':
1515               iconicClass = 'iconic-sm';
1516               break;
1517             case 'medium':
1518               iconicClass = 'iconic-md';
1519               break;
1520             case 'large':
1521               iconicClass = 'iconic-lg';
1522               break;
1523             default:
1524               iconicClass = 'iconic-fluid';
1525           }
1526           element.addClass(iconicClass);
1527         }
1528
1529         // save contents of un-inject html, to use for dynamic re-injection
1530         contents = element[0].outerHTML;
1531       }
1532
1533       function postLink(scope, element, attrs) {
1534         var svgElement, ico = iconic.getAccess();
1535
1536         injectSvg(element[0]);
1537
1538         foundationApi.subscribe('resize', function () {
1539           // only run update on current element
1540           ico.update(element[0]);
1541         });
1542
1543         // handle dynamic updating of src
1544         if (scope.dynSrc) {
1545           scope.$watch('dynSrc', function (newVal, oldVal) {
1546             if (newVal && newVal !== oldVal) {
1547               reinjectSvg(scope.dynSrc);
1548             }
1549           });
1550         }
1551         // handle dynamic updating of icon
1552         if (scope.dynIcon) {
1553           scope.$watch('dynIcon', function (newVal, oldVal) {
1554             if (newVal && newVal !== oldVal) {
1555               reinjectSvg(assetPath + scope.dynIcon + '.svg');
1556             }
1557           });
1558         }
1559
1560         function reinjectSvg(newSrc) {
1561           if (svgElement) {
1562             // set html
1563             svgElement.empty();
1564             svgElement.append(angular.element(contents));
1565
1566             // set new source
1567             svgElement.attr('data-src', newSrc);
1568
1569             // reinject
1570             injectSvg(svgElement[0]);
1571           }
1572         }
1573
1574         function injectSvg(element) {
1575           ico.inject(element, {
1576             each: function (injectedElem) {
1577               // compile injected svg
1578               var angElem = angular.element(injectedElem);
1579               svgElement = $compile(angElem)(angElem.scope());
1580             }
1581           });
1582         }
1583       }
1584     }
1585   }
1586
1587 })();
1588
1589 (function() {
1590   'use strict';
1591
1592   angular.module('foundation.interchange', ['foundation.core', 'foundation.mediaquery'])
1593     .directive('zfInterchange', zfInterchange)
1594   ;
1595
1596   zfInterchange.$inject = [ '$compile', '$http', '$templateCache', 'FoundationApi', 'FoundationMQ'];
1597
1598   function zfInterchange($compile, $http, $templateCache, foundationApi, foundationMQ) {
1599
1600     var directive = {
1601       restrict: 'EA',
1602       transclude: 'element',
1603       scope: {
1604         position: '@'
1605       },
1606       replace: true,
1607       template: '<div></div>',
1608       link: link
1609     };
1610
1611     return directive;
1612
1613     function link(scope, element, attrs, ctrl, transclude) {
1614       var childScope, current, scenarios, innerTemplates;
1615
1616       var globalQueries = foundationMQ.getMediaQueries();
1617
1618       //setup
1619       foundationApi.subscribe('resize', function(msg) {
1620         transclude(function(clone, newScope) {
1621           if(!scenarios || !innerTemplates) {
1622             collectInformation(clone);
1623           }
1624
1625           var ruleMatches = foundationMQ.match(scenarios);
1626           var scenario = ruleMatches.length === 0 ? null : scenarios[ruleMatches[0].ind];
1627
1628           //this could use some love
1629           if(scenario && checkScenario(scenario)) {
1630             var compiled;
1631
1632             if(childScope) {
1633               childScope.$destroy();
1634               childScope = null;
1635             }
1636
1637             if(typeof scenario.templ !== 'undefined') {
1638               childScope = newScope;
1639
1640               //temp container
1641               var tmp = document.createElement('div');
1642               tmp.appendChild(innerTemplates[scenario.templ][0]);
1643
1644               element.html(tmp.innerHTML);
1645               $compile(element.contents())(childScope);
1646               current = scenario;
1647             } else {
1648               var loader = templateLoader(scenario.src);
1649               loader.success(function(html) {
1650                 childScope = newScope;
1651                 element.html(html);
1652               }).then(function(){
1653                 $compile(element.contents())(childScope);
1654                 current = scenario;
1655               });
1656             }
1657           }
1658         });
1659
1660       });
1661
1662       //init
1663       foundationApi.publish('resize', 'initial resize');
1664
1665       function templateLoader(templateUrl) {
1666         return $http.get(templateUrl, {cache: $templateCache});
1667       }
1668
1669       function collectInformation(el) {
1670         var data = foundationMQ.collectScenariosFromElement(el);
1671
1672         scenarios = data.scenarios;
1673         innerTemplates = data.templates;
1674       }
1675
1676       function checkScenario(scenario) {
1677         return !current || current !== scenario;
1678       }
1679     }
1680   }
1681
1682 })();
1683
1684 (function() {
1685   'use strict';
1686
1687   angular.module('foundation.modal', ['foundation.core'])
1688     .directive('zfModal', modalDirective)
1689     .factory('ModalFactory', ModalFactory)
1690   ;
1691
1692   FoundationModal.$inject = ['FoundationApi', 'ModalFactory'];
1693
1694   function FoundationModal(foundationApi, ModalFactory) {
1695     var service    = {};
1696
1697     service.activate = activate;
1698     service.deactivate = deactivate;
1699     service.newModal = newModal;
1700
1701     return service;
1702
1703     //target should be element ID
1704     function activate(target) {
1705       foundationApi.publish(target, 'show');
1706     }
1707
1708     //target should be element ID
1709     function deactivate(target) {
1710       foundationApi.publish(target, 'hide');
1711     }
1712
1713     //new modal has to be controlled via the new instance
1714     function newModal(config) {
1715       return new ModalFactory(config);
1716     }
1717   }
1718
1719   modalDirective.$inject = ['FoundationApi'];
1720
1721   function modalDirective(foundationApi) {
1722
1723     var directive = {
1724       restrict: 'EA',
1725       templateUrl: 'components/modal/modal.html',
1726       transclude: true,
1727       scope: true,
1728       replace: true,
1729       compile: compile
1730     };
1731
1732     return directive;
1733
1734     function compile(tElement, tAttrs, transclude) {
1735       var type = 'modal';
1736
1737       return {
1738         pre: preLink,
1739         post: postLink
1740       };
1741
1742       function preLink(scope, iElement, iAttrs, controller) {
1743           iAttrs.$set('zf-closable', type);
1744       }
1745
1746       function postLink(scope, element, attrs) {
1747         var dialog = angular.element(element.children()[0]);
1748
1749         scope.active = scope.active || false;
1750         scope.overlay = attrs.overlay === 'false' ? false : true;
1751         scope.overlayClose = attrs.overlayClose === 'false' ? false : true;
1752
1753         var animationIn = attrs.animationIn || 'fadeIn';
1754         var animationOut = attrs.animationOut || 'fadeOut';
1755
1756         var overlayIn = 'fadeIn';
1757         var overlayOut = 'fadeOut';
1758
1759         scope.hideOverlay = function() {
1760           if(scope.overlayClose) {
1761             scope.hide();
1762           }
1763         };
1764
1765         scope.hide = function() {
1766           scope.active = false;
1767           animate();
1768           return;
1769         };
1770
1771         scope.show = function() {
1772           scope.active = true;
1773           animate();
1774           dialog.tabIndex = -1;
1775           dialog[0].focus();
1776           return;
1777         };
1778
1779         scope.toggle = function() {
1780           scope.active = !scope.active;
1781           animate();
1782           return;
1783         };
1784
1785         init();
1786
1787         //setup
1788         foundationApi.subscribe(attrs.id, function(msg) {
1789           if(msg === 'show' || msg === 'open') {
1790             scope.show();
1791           } else if (msg === 'close' || msg === 'hide') {
1792             scope.hide();
1793           } else if (msg === 'toggle') {
1794             scope.toggle();
1795           }
1796
1797           if (scope.$root && !scope.$root.$$phase) {
1798             scope.$apply();
1799           }
1800
1801           return;
1802         });
1803
1804         function animate() {
1805           //animate both overlay and dialog
1806           if(!scope.overlay) {
1807             element.css('background', 'transparent');
1808           }
1809
1810           foundationApi.animate(element, scope.active, overlayIn, overlayOut);
1811           foundationApi.animate(dialog, scope.active, animationIn, animationOut);
1812         }
1813
1814         function init() {
1815           if(scope.active) {
1816             scope.show();
1817           }
1818         }
1819       }
1820     }
1821   }
1822
1823   ModalFactory.$inject = ['$http', '$templateCache', '$rootScope', '$compile', '$timeout', '$q', 'FoundationApi'];
1824
1825   function ModalFactory($http, $templateCache, $rootScope, $compile, $timeout, $q, foundationApi) {
1826     return modalFactory;
1827
1828     function modalFactory(config) {
1829       var self = this, //for prototype functions
1830           container = angular.element(config.container || document.body),
1831           id = config.id || foundationApi.generateUuid(),
1832           attached = false,
1833           destroyed = false,
1834           html,
1835           element,
1836           fetched,
1837           scope,
1838           contentScope
1839       ;
1840
1841       var props = [
1842         'animationIn',
1843         'animationOut',
1844         'overlay',
1845         'overlayClose'
1846       ];
1847
1848       if(config.templateUrl) {
1849         //get template
1850         fetched = $http.get(config.templateUrl, {
1851           cache: $templateCache
1852         }).then(function (response) {
1853           html = response.data;
1854           assembleDirective();
1855         });
1856
1857       } else if(config.template) {
1858         //use provided template
1859         fetched = true;
1860         html = config.template;
1861         assembleDirective();
1862       }
1863
1864       self.activate = activate;
1865       self.deactivate = deactivate;
1866       self.toggle = toggle;
1867       self.destroy = destroy;
1868
1869
1870       return {
1871         activate: activate,
1872         deactivate: deactivate,
1873         toggle: toggle,
1874         destroy: destroy
1875       };
1876
1877       function checkStatus() {
1878         if(destroyed) {
1879           throw "Error: Modal was destroyed. Delete the object and create a new ModalFactory instance."
1880         }
1881       }
1882
1883       function activate() {
1884         checkStatus();
1885         $timeout(function() {
1886           init(true);
1887           foundationApi.publish(id, 'show');
1888         }, 0, false);
1889       }
1890
1891       function deactivate() {
1892         checkStatus();
1893         $timeout(function() {
1894           init(false);
1895           foundationApi.publish(id, 'hide');
1896         }, 0, false);
1897       }
1898
1899       function toggle() {
1900         checkStatus();
1901         $timeout(function() {
1902           init(true);
1903           foundationApi.publish(id, 'toggle');
1904         }, 0, false);
1905       }
1906
1907       function init(state) {
1908         $q.when(fetched).then(function() {
1909           if(!attached && html.length > 0) {
1910             var modalEl = container.append(element);
1911
1912             scope.active = state;
1913             $compile(element)(scope);
1914
1915             attached = true;
1916           }
1917         });
1918       }
1919
1920       function assembleDirective() {
1921         // check for duplicate elements to prevent factory from cloning modals
1922         if (document.getElementById(id)) {
1923           return;
1924         }
1925
1926         html = '<zf-modal id="' + id + '">' + html + '</zf-modal>';
1927
1928         element = angular.element(html);
1929
1930         scope = $rootScope.$new();
1931
1932         // account for directive attributes
1933         for(var i = 0; i < props.length; i++) {
1934           var prop = props[i];
1935
1936           if(config[prop]) {
1937             switch (prop) {
1938               case 'animationIn':
1939                 element.attr('animation-in', config[prop]);
1940                 break;
1941               case 'animationOut':
1942                 element.attr('animation-out', config[prop]);
1943                 break;
1944               default:
1945                 element.attr(prop, config[prop]);
1946             }
1947           }
1948         }
1949         // access view scope variables
1950         if (config.contentScope) {
1951           contentScope = config.contentScope;
1952           for (var prop in config.contentScope) {
1953             if (config.contentScope.hasOwnProperty(prop)) {
1954               scope[prop] = config.contentScope[prop];
1955             }
1956           }
1957         }
1958       }
1959
1960       function destroy() {
1961         self.deactivate();
1962         setTimeout(function() {
1963           scope.$destroy();
1964           element.remove();
1965           destroyed = true;
1966         }, 3000);
1967         foundationApi.unsubscribe(id);
1968       }
1969
1970     }
1971
1972   }
1973
1974 })();
1975
1976 (function() {
1977   'use strict';
1978
1979   angular.module('foundation.notification', ['foundation.core'])
1980     .controller('ZfNotificationController', ZfNotificationController)
1981     .directive('zfNotificationSet', zfNotificationSet)
1982     .directive('zfNotification', zfNotification)
1983     .directive('zfNotificationStatic', zfNotificationStatic)
1984     .directive('zfNotify', zfNotify)
1985     .factory('NotificationFactory', NotificationFactory)
1986     .service('FoundationNotification', FoundationNotification)
1987   ;
1988
1989   FoundationNotification.$inject = ['FoundationApi', 'NotificationFactory'];
1990
1991   function FoundationNotification(foundationApi, NotificationFactory) {
1992     var service    = {};
1993
1994     service.activate = activate;
1995     service.deactivate = deactivate;
1996
1997     return service;
1998
1999     //target should be element ID
2000     function activate(target) {
2001       foundationApi.publish(target, 'show');
2002     }
2003
2004     //target should be element ID
2005     function deactivate(target) {
2006       foundationApi.publish(target, 'hide');
2007     }
2008
2009     function toggle(target) {
2010       foundationApi.publish(target, 'toggle');
2011     }
2012
2013     function createNotificationSet(config) {
2014       return new NotificationFactory(config);
2015     }
2016   }
2017
2018
2019   ZfNotificationController.$inject = ['$scope', 'FoundationApi'];
2020
2021   function ZfNotificationController($scope, foundationApi) {
2022     var controller    = this;
2023     controller.notifications = $scope.notifications = $scope.notifications || [];
2024
2025     controller.addNotification = function(info) {
2026       var id  = foundationApi.generateUuid();
2027       info.id = id;
2028       $scope.notifications.push(info);
2029     };
2030
2031     controller.removeNotification = function(id) {
2032       $scope.notifications.forEach(function(notification) {
2033         if(notification.id === id) {
2034           var ind = $scope.notifications.indexOf(notification);
2035           $scope.notifications.splice(ind, 1);
2036         }
2037       });
2038     };
2039
2040     controller.clearAll = function() {
2041       while($scope.notifications.length > 0) {
2042         $scope.notifications.pop();
2043       }
2044     };
2045   }
2046
2047   zfNotificationSet.$inject = ['FoundationApi'];
2048
2049   function zfNotificationSet(foundationApi) {
2050     var directive = {
2051       restrict: 'EA',
2052       templateUrl: 'components/notification/notification-set.html',
2053       controller: 'ZfNotificationController',
2054       replace: true,
2055       scope: {
2056         position: '@'
2057       },
2058       link: link
2059     };
2060
2061     return directive;
2062
2063     function link(scope, element, attrs, controller) {
2064       scope.position = scope.position ? scope.position.split(' ').join('-') : 'top-right';
2065
2066       foundationApi.subscribe(attrs.id, function(msg) {
2067         if(msg === 'clearall') {
2068           controller.clearAll();
2069         }
2070         else {
2071           controller.addNotification(msg);
2072           if (!scope.$root.$$phase) {
2073             scope.$apply();
2074           }
2075         }
2076       });
2077     }
2078   }
2079
2080   zfNotification.$inject = ['FoundationApi'];
2081
2082   function zfNotification(foundationApi) {
2083     var directive = {
2084       restrict: 'EA',
2085       templateUrl: 'components/notification/notification.html',
2086       replace: true,
2087       transclude: true,
2088       require: '^zfNotificationSet',
2089       controller: function() { },
2090       scope: {
2091         title: '=?',
2092         content: '=?',
2093         image: '=?',
2094         notifId: '=',
2095         color: '=?',
2096         autoclose: '=?'
2097       },
2098       compile: compile
2099     };
2100
2101     return directive;
2102
2103     function compile() {
2104
2105       return {
2106         pre: preLink,
2107         post: postLink
2108       };
2109
2110       function preLink(scope, iElement, iAttrs) {
2111         iAttrs.$set('zf-closable', 'notification');
2112       }
2113
2114       function postLink(scope, element, attrs, controller) {
2115         scope.active = false;
2116         var animationIn  = attrs.animationIn || 'fadeIn';
2117         var animationOut = attrs.animationOut || 'fadeOut';
2118         var hammerElem;
2119
2120         //due to dynamic insertion of DOM, we need to wait for it to show up and get working!
2121         setTimeout(function() {
2122           scope.active = true;
2123           foundationApi.animate(element, scope.active, animationIn, animationOut);
2124         }, 50);
2125
2126         scope.hide = function() {
2127           scope.active = false;
2128           foundationApi.animate(element, scope.active, animationIn, animationOut);
2129           setTimeout(function() {
2130             controller.removeNotification(scope.notifId);
2131           }, 50);
2132         };
2133
2134         // close if autoclose
2135         if (scope.autoclose) {
2136           setTimeout(function() {
2137             if (scope.active) {
2138               scope.hide();
2139             }
2140           }, parseInt(scope.autoclose));
2141         };
2142
2143         // close on swipe
2144         if (Hammer) {
2145           hammerElem = new Hammer(element[0]);
2146           // set the options for swipe (to make them a bit more forgiving in detection)
2147           hammerElem.get('swipe').set({
2148             direction: Hammer.DIRECTION_ALL,
2149             threshold: 5, // this is how far the swipe has to travel
2150             velocity: 0.5 // and this is how fast the swipe must travel
2151           });
2152         }
2153
2154         hammerElem.on('swipe', function() {
2155           if (scope.active) {
2156             scope.hide();
2157           }
2158         });
2159       }
2160     }
2161   }
2162
2163   zfNotificationStatic.$inject = ['FoundationApi'];
2164
2165   function zfNotificationStatic(foundationApi) {
2166     var directive = {
2167       restrict: 'EA',
2168       templateUrl: 'components/notification/notification-static.html',
2169       replace: true,
2170       transclude: true,
2171       scope: {
2172         title: '@?',
2173         content: '@?',
2174         image: '@?',
2175         color: '@?',
2176         autoclose: '@?'
2177       },
2178       compile: compile
2179     };
2180
2181     return directive;
2182
2183     function compile() {
2184       var type = 'notification';
2185
2186       return {
2187         pre: preLink,
2188         post: postLink
2189       };
2190
2191       function preLink(scope, iElement, iAttrs, controller) {
2192         iAttrs.$set('zf-closable', type);
2193       }
2194
2195       function postLink(scope, element, attrs, controller) {
2196         scope.position = attrs.position ? attrs.position.split(' ').join('-') : 'top-right';
2197
2198         var animationIn = attrs.animationIn || 'fadeIn';
2199         var animationOut = attrs.animationOut || 'fadeOut';
2200
2201         //setup
2202         foundationApi.subscribe(attrs.id, function(msg) {
2203           if(msg == 'show' || msg == 'open') {
2204             scope.show();
2205             // close if autoclose
2206             if (scope.autoclose) {
2207               setTimeout(function() {
2208                 if (scope.active) {
2209                   scope.hide();
2210                 }
2211               }, parseInt(scope.autoclose));
2212             };
2213           } else if (msg == 'close' || msg == 'hide') {
2214             scope.hide();
2215           } else if (msg == 'toggle') {
2216             scope.toggle();
2217             // close if autoclose
2218             if (scope.autoclose) {
2219               setTimeout(function() {
2220                 if (scope.active) {
2221                   scope.toggle();
2222                 }
2223               }, parseInt(scope.autoclose));
2224             };
2225           }
2226
2227           foundationApi.animate(element, scope.active, animationIn, animationOut);
2228           scope.$apply();
2229
2230           return;
2231         });
2232
2233         scope.hide = function() {
2234           scope.active = false;
2235           foundationApi.animate(element, scope.active, animationIn, animationOut);
2236           return;
2237         };
2238
2239         scope.show = function() {
2240           scope.active = true;
2241           foundationApi.animate(element, scope.active, animationIn, animationOut);
2242           return;
2243         };
2244
2245         scope.toggle = function() {
2246           scope.active = !scope.active;
2247           foundationApi.animate(element, scope.active, animationIn, animationOut);
2248           return;
2249         };
2250
2251       }
2252     }
2253   }
2254
2255   zfNotify.$inject = ['FoundationApi'];
2256
2257   function zfNotify(foundationApi) {
2258     var directive = {
2259       restrict: 'A',
2260       scope: {
2261         title: '@?',
2262         content: '@?',
2263         color: '@?',
2264         image: '@?',
2265         autoclose: '@?'
2266       },
2267       link: link
2268     };
2269
2270     return directive;
2271
2272     function link(scope, element, attrs, controller) {
2273       element.on('click', function(e) {
2274         foundationApi.publish(attrs.zfNotify, {
2275           title: scope.title,
2276           content: scope.content,
2277           color: scope.color,
2278           image: scope.image,
2279           autoclose: scope.autoclose
2280         });
2281         e.preventDefault();
2282       });
2283     }
2284   }
2285
2286   NotificationFactory.$inject = ['$http', '$templateCache', '$rootScope', '$compile', '$timeout', 'FoundationApi'];
2287
2288   function NotificationFactory($http, $templateCache, $rootScope, $compile, $timeout, foundationApi) {
2289     return notificationFactory;
2290
2291     function notificationFactory(config) {
2292       var self = this, //for prototype functions
2293           container = angular.element(config.container || document.body),
2294           id = config.id || foundationApi.generateUuid(),
2295           attached = false,
2296           destroyed = false,
2297           html,
2298           element,
2299           scope,
2300           contentScope
2301       ;
2302
2303       var props = [
2304         'position'
2305       ];
2306
2307       assembleDirective();
2308
2309       self.addNotification = addNotification;
2310       self.clearAll = clearAll;
2311       self.destroy = destroy;
2312
2313       return {
2314         addNotification: addNotification,
2315         clearAll: clearAll,
2316         destroy: destroy
2317       };
2318
2319       function checkStatus() {
2320         if(destroyed) {
2321           throw "Error: Notification Set was destroyed. Delete the object and create a new NotificationFactory instance."
2322         }
2323       }
2324
2325       function addNotification(notification) {
2326         checkStatus();
2327         $timeout(function() {
2328           foundationApi.publish(id, notification);
2329         }, 0, false);
2330       }
2331
2332       function clearAll() {
2333         checkStatus();
2334         $timeout(function() {
2335           foundationApi.publish(id, 'clearall');
2336         }, 0, false);
2337       }
2338
2339       function init(state) {
2340         if(!attached && html.length > 0) {
2341           var modalEl = container.append(element);
2342
2343           scope.active = state;
2344           $compile(element)(scope);
2345
2346           attached = true;
2347         }
2348       }
2349
2350       function assembleDirective() {
2351         // check for duplicate element to prevent factory from cloning notification sets
2352         if (document.getElementById(id)) {
2353           return;
2354         }
2355         html = '<zf-notification-set id="' + id + '"></zf-notification-set>';
2356
2357         element = angular.element(html);
2358
2359         scope = $rootScope.$new();
2360         
2361         for(var i = 0; i < props.length; i++) {
2362           if(config[props[i]]) {
2363             element.attr(props[i], config[props[i]]);
2364           }
2365         }
2366
2367         // access view scope variables
2368         if (config.contentScope) {
2369           contentScope = config.contentScope;
2370           for (var prop in contentScope) {
2371             if (contentScope.hasOwnProperty(prop)) {
2372               scope[prop] = contentScope[prop];
2373             }
2374           }
2375         }
2376         init(true);
2377       }
2378
2379       function destroy() {
2380         self.clearAll();
2381         setTimeout(function() {
2382           scope.$destroy();
2383           element.remove();
2384           destroyed = true;
2385         }, 3000);
2386         foundationApi.unsubscribe(id);
2387       }
2388
2389     }
2390
2391   }
2392 })();
2393
2394 (function() {
2395   'use strict';
2396
2397   angular.module('foundation.offcanvas', ['foundation.core'])
2398     .directive('zfOffcanvas', zfOffcanvas)
2399     .service('FoundationOffcanvas', FoundationOffcanvas)
2400   ;
2401
2402   FoundationOffcanvas.$inject = ['FoundationApi'];
2403
2404   function FoundationOffcanvas(foundationApi) {
2405     var service    = {};
2406
2407     service.activate = activate;
2408     service.deactivate = deactivate;
2409
2410     return service;
2411
2412     //target should be element ID
2413     function activate(target) {
2414       foundationApi.publish(target, 'show');
2415     }
2416
2417     //target should be element ID
2418     function deactivate(target) {
2419       foundationApi.publish(target, 'hide');
2420     }
2421
2422     function toggle(target) {
2423       foundationApi.publish(target, 'toggle');
2424     }
2425   }
2426
2427   zfOffcanvas.$inject = ['FoundationApi'];
2428
2429   function zfOffcanvas(foundationApi) {
2430     var directive = {
2431       restrict: 'EA',
2432       templateUrl: 'components/offcanvas/offcanvas.html',
2433       transclude: true,
2434       scope: {
2435         position: '@'
2436       },
2437       replace: true,
2438       compile: compile
2439     };
2440
2441     return directive;
2442
2443     function compile(tElement, tAttrs, transclude) {
2444       var type = 'offcanvas';
2445
2446       return {
2447         pre: preLink,
2448         post: postLink
2449       }
2450
2451       function preLink(scope, iElement, iAttrs, controller) {
2452         iAttrs.$set('zf-closable', type);
2453         document.body.classList.add('has-off-canvas');
2454       }
2455
2456       function postLink(scope, element, attrs) {
2457         scope.position = scope.position || 'left';
2458
2459         scope.active = false;
2460         //setup
2461         foundationApi.subscribe(attrs.id, function(msg) {
2462           if(msg === 'show' || msg === 'open') {
2463             scope.show();
2464           } else if (msg === 'close' || msg === 'hide') {
2465             scope.hide();
2466           } else if (msg === 'toggle') {
2467             scope.toggle();
2468           }
2469
2470           if (!scope.$root.$$phase) {
2471             scope.$apply();
2472           }
2473           
2474           return;
2475         });
2476
2477         scope.hide = function() {
2478           scope.active = false;
2479           return;
2480         };
2481
2482         scope.show = function() {
2483           scope.active = true;
2484           return;
2485         };
2486
2487         scope.toggle = function() {
2488           scope.active = !scope.active;
2489           return;
2490         };
2491       }
2492     }
2493   }
2494
2495 })();
2496
2497 (function() {
2498   'use strict';
2499
2500   angular.module('foundation.panel', ['foundation.core'])
2501     .directive('zfPanel', zfPanel)
2502     .service('FoundationPanel', FoundationPanel)
2503   ;
2504
2505   FoundationPanel.$inject = ['FoundationApi'];
2506
2507   function FoundationPanel(foundationApi) {
2508     var service    = {};
2509
2510     service.activate = activate;
2511     service.deactivate = deactivate;
2512
2513     return service;
2514
2515     //target should be element ID
2516     function activate(target) {
2517       foundationApi.publish(target, 'show');
2518     }
2519
2520     //target should be element ID
2521     function deactivate(target) {
2522       foundationApi.publish(target, 'hide');
2523     }
2524   }
2525
2526   zfPanel.$inject = ['FoundationApi', '$window'];
2527
2528   function zfPanel(foundationApi, $window) {
2529     var directive = {
2530       restrict: 'EA',
2531       templateUrl: 'components/panel/panel.html',
2532       transclude: true,
2533       scope: {
2534         position: '@?'
2535       },
2536       replace: true,
2537       compile: compile
2538     };
2539
2540     return directive;
2541
2542     function compile(tElement, tAttrs, transclude) {
2543       var type = 'panel';
2544
2545       return {
2546         pre: preLink,
2547         post: postLink
2548       };
2549
2550       function preLink(scope, iElement, iAttrs, controller) {
2551         iAttrs.$set('zf-closable', type);
2552         scope.position = scope.position || 'left';
2553         scope.positionClass = 'panel-' + scope.position;
2554       }
2555
2556       function postLink(scope, element, attrs) {
2557         scope.active = false;
2558         var animationIn, animationOut;
2559         var globalQueries = foundationApi.getSettings().mediaQueries;
2560
2561         //urgh, there must be a better way
2562         if(scope.position === 'left') {
2563           animationIn  = attrs.animationIn || 'slideInRight';
2564           animationOut = attrs.animationOut || 'slideOutLeft';
2565         } else if (scope.position === 'right') {
2566           animationIn  = attrs.animationIn || 'slideInLeft';
2567           animationOut = attrs.animationOut || 'slideOutRight';
2568         } else if (scope.position === 'top') {
2569           animationIn  = attrs.animationIn || 'slideInDown';
2570           animationOut = attrs.animationOut || 'slideOutUp';
2571         } else if (scope.position === 'bottom') {
2572           animationIn  = attrs.animationIn || 'slideInUp';
2573           animationOut = attrs.animationOut || 'slideOutBottom';
2574         }
2575
2576
2577         //setup
2578         foundationApi.subscribe(attrs.id, function(msg) {
2579           var panelPosition = $window.getComputedStyle(element[0]).getPropertyValue("position");
2580
2581           // patch to prevent panel animation on larger screen devices
2582           if (panelPosition !== 'absolute') {
2583             return;
2584           }
2585
2586           if(msg == 'show' || msg == 'open') {
2587             scope.show();
2588           } else if (msg == 'close' || msg == 'hide') {
2589             scope.hide();
2590           } else if (msg == 'toggle') {
2591             scope.toggle();
2592           }
2593           
2594           if (!scope.$root.$$phase) {
2595             scope.$apply();
2596           }
2597
2598           return;
2599         });
2600
2601         scope.hide = function() {
2602           if(scope.active){
2603             scope.active = false;
2604             foundationApi.animate(element, scope.active, animationIn, animationOut);
2605           }
2606
2607           return;
2608         };
2609
2610         scope.show = function() {
2611           if(!scope.active){
2612             scope.active = true;
2613             foundationApi.animate(element, scope.active, animationIn, animationOut);
2614           }
2615
2616           return;
2617         };
2618
2619         scope.toggle = function() {
2620           scope.active = !scope.active;
2621           foundationApi.animate(element, scope.active, animationIn, animationOut);
2622           
2623           return;
2624         };
2625
2626         element.on('click', function(e) {
2627           //check sizing
2628           var srcEl = e.srcElement;
2629
2630           if(!matchMedia(globalQueries.medium).matches && srcEl.href && srcEl.href.length > 0) {
2631             //hide element if it can't match at least medium
2632             scope.hide();
2633             foundationApi.animate(element, scope.active, animationIn, animationOut);
2634           }
2635         });
2636       }
2637     }
2638   }
2639
2640 })();
2641
2642 (function() {
2643   'use strict';
2644
2645   angular.module('foundation.popup', ['foundation.core'])
2646     .directive('zfPopup', zfPopup)
2647     .directive('zfPopupToggle', zfPopupToggle)
2648     .service('FoundationPopup', FoundationPopup)
2649   ;
2650
2651   FoundationPopup.$inject = ['FoundationApi'];
2652
2653   function FoundationPopup(foundationApi) {
2654     var service    = {};
2655
2656     service.activate = activate;
2657     service.deactivate = deactivate;
2658
2659     return service;
2660
2661     //target should be element ID
2662     function activate(target) {
2663       foundationApi.publish(target, 'show');
2664     }
2665
2666     //target should be element ID
2667     function deactivate(target) {
2668       foundationApi.publish(target, 'hide');
2669     }
2670
2671     function toggle(target, popupTarget) {
2672       foundationApi.publish(target, ['toggle', popupTarget]);
2673     }
2674   }
2675
2676   zfPopup.$inject = ['FoundationApi'];
2677
2678   function zfPopup(foundationApi) {
2679     var directive = {
2680       restrict: 'EA',
2681       transclude: true,
2682       replace: true,
2683       templateUrl: 'components/popup/popup.html',
2684       scope: {
2685         pinTo: '@?',
2686         pinAt: '@?',
2687         target: '@?'
2688       },
2689       compile: compile
2690     };
2691
2692     return directive;
2693
2694     function compile() {
2695       return {
2696         pre: preLink,
2697         post: postLink
2698       };
2699
2700       function preLink(scope, iElement, iAttrs) {
2701         iAttrs.$set('zf-closable', 'popup');
2702       }
2703
2704       function postLink(scope, element, attrs) {
2705         scope.active = false;
2706         scope.target = scope.target || false;
2707
2708         var attachment = scope.pinTo || 'top center';
2709         var targetAttachment = scope.pinAt || 'bottom center';
2710         var tetherInit = false;
2711         var tether     = {};
2712
2713         //setup
2714         foundationApi.subscribe(attrs.id, function(msg) {
2715           if(msg[0] === 'show' || msg[0] === 'open') {
2716             scope.show(msg[1]);
2717           } else if (msg[0] === 'close' || msg[0] === 'hide') {
2718             scope.hide();
2719           } else if (msg[0] === 'toggle') {
2720             scope.toggle(msg[1]);
2721           }
2722
2723           scope.$apply();
2724
2725           return;
2726         });
2727
2728
2729         scope.hide = function() {
2730           scope.active = false;
2731           tetherElement();
2732           tether.disable();
2733           return;
2734         };
2735
2736         scope.show = function(newTarget) {
2737           scope.active = true;
2738           tetherElement(newTarget);
2739           tether.enable();
2740
2741           return;
2742         };
2743
2744         scope.toggle = function(newTarget) {
2745           scope.active = !scope.active;
2746           tetherElement(newTarget);
2747
2748           if(scope.active) {
2749             tether.enable();
2750           } else  {
2751             tether.disable();
2752           }
2753
2754           return;
2755         };
2756
2757         function tetherElement(target) {
2758           if(tetherInit) {
2759             return;
2760           }
2761
2762           scope.target = scope.target ? document.getElementById(scope.target) : document.getElementById(target);
2763
2764           tether = new Tether({
2765             element: element[0],
2766             target: scope.target,
2767             attachment: attachment,
2768             targetAttachment: targetAttachment,
2769             enable: false
2770           });
2771
2772           tetherInit = true;
2773         }
2774
2775       }
2776     }
2777   }
2778
2779   zfPopupToggle.$inject = ['FoundationApi'];
2780
2781   function zfPopupToggle(foundationApi) {
2782     var directive = {
2783       restrict: 'A',
2784       link: link
2785     };
2786
2787     return directive;
2788
2789     function link(scope, element, attrs) {
2790       var target = attrs.zfPopupToggle;
2791       var id = attrs.id || foundationApi.generateUuid();
2792       attrs.$set('id', id);
2793
2794       element.on('click', function(e) {
2795         foundationApi.publish(target, ['toggle', id]);
2796         e.preventDefault();
2797       });
2798     }
2799   }
2800
2801 })();
2802
2803 (function() {
2804   'use strict';
2805
2806   angular.module('foundation.tabs', ['foundation.core'])
2807     .controller('ZfTabsController', ZfTabsController)
2808     .directive('zfTabs', zfTabs)
2809     .directive('zfTabContent', zfTabContent)
2810     .directive('zfTab', zfTab)
2811     .directive('zfTabIndividual', zfTabIndividual)
2812     .directive('zfTabHref', zfTabHref)
2813     .directive('zfTabCustom', zfTabCustom)
2814     .directive('zfTabContentCustom', zfTabContentCustom)
2815     .service('FoundationTabs', FoundationTabs)
2816   ;
2817
2818   FoundationTabs.$inject = ['FoundationApi'];
2819
2820   function FoundationTabs(foundationApi) {
2821     var service    = {};
2822
2823     service.activate = activate;
2824
2825     return service;
2826
2827     //target should be element ID
2828     function activate(target) {
2829       foundationApi.publish(target, 'show');
2830     }
2831
2832   }
2833
2834   ZfTabsController.$inject = ['$scope', 'FoundationApi'];
2835
2836   function ZfTabsController($scope, foundationApi) {
2837     var controller = this;
2838     var tabs       = controller.tabs = $scope.tabs = [];
2839     var id         = '';
2840
2841     controller.select = function(selectTab) {
2842       tabs.forEach(function(tab) {
2843         tab.active = false;
2844         tab.scope.active = false;
2845
2846         if(tab.scope === selectTab) {
2847           foundationApi.publish(id, ['activate', tab]);
2848
2849           tab.active = true;
2850           tab.scope.active = true;
2851         }
2852       });
2853
2854     };
2855
2856     controller.addTab = function addTab(tabScope) {
2857       tabs.push({ scope: tabScope, active: false, parentContent: controller.id });
2858
2859       if(tabs.length === 1) {
2860         tabs[0].active = true;
2861         tabScope.active = true;
2862       }
2863     };
2864
2865     controller.getId = function() {
2866       return id;
2867     };
2868
2869     controller.setId = function(newId) {
2870       id = newId;
2871     };
2872   }
2873
2874   zfTabs.$inject = ['FoundationApi'];
2875
2876   function zfTabs(foundationApi) {
2877     var directive = {
2878       restrict: 'EA',
2879       transclude: 'true',
2880       replace: true,
2881       templateUrl: 'components/tabs/tabs.html',
2882       controller: 'ZfTabsController',
2883       scope: {
2884         displaced: '@?'
2885       },
2886       link: link
2887     };
2888
2889     return directive;
2890
2891     function link(scope, element, attrs, controller) {
2892       scope.id = attrs.id || foundationApi.generateUuid();
2893       scope.showTabContent = scope.displaced !== 'true';
2894       attrs.$set('id', scope.id);
2895       controller.setId(scope.id);
2896
2897       //update tabs in case tab-content doesn't have them
2898       var updateTabs = function() {
2899         foundationApi.publish(scope.id + '-tabs', scope.tabs);
2900       };
2901
2902       foundationApi.subscribe(scope.id + '-get-tabs', function() {
2903         updateTabs();
2904       });
2905     }
2906   }
2907
2908   zfTabContent.$inject = ['FoundationApi'];
2909
2910   function zfTabContent(foundationApi) {
2911     var directive = {
2912       restrict: 'A',
2913       transclude: 'true',
2914       replace: true,
2915       scope: {
2916         tabs: '=?',
2917         target: '@'
2918       },
2919       templateUrl: 'components/tabs/tab-content.html',
2920       link: link
2921     };
2922
2923     return directive;
2924
2925     function link(scope, element, attrs, ctrl) {
2926       scope.tabs = scope.tabs || [];
2927       var id = scope.target;
2928
2929       foundationApi.subscribe(id, function(msg) {
2930         if(msg[0] === 'activate') {
2931           var tabId = msg[1];
2932           scope.tabs.forEach(function (tab) {
2933             tab.scope.active = false;
2934             tab.active = false;
2935
2936             if(tab.scope.id === id) {
2937               tab.scope.active = true;
2938               tab.active = true;
2939             }
2940           });
2941         }
2942       });
2943
2944       //if tabs empty, request tabs
2945       if(scope.tabs.length === 0) {
2946         foundationApi.subscribe(id + '-tabs', function(tabs) {
2947           scope.tabs = tabs;
2948         });
2949
2950         foundationApi.publish(id + '-get-tabs', '');
2951       }
2952     }
2953   }
2954
2955   zfTab.$inject = ['FoundationApi'];
2956
2957   function zfTab(foundationApi) {
2958     var directive = {
2959       restrict: 'EA',
2960       templateUrl: 'components/tabs/tab.html',
2961       transclude: true,
2962       scope: {
2963         title: '@'
2964       },
2965       require: '^zfTabs',
2966       replace: true,
2967       link: link
2968     };
2969
2970     return directive;
2971
2972     function link(scope, element, attrs, controller, transclude) {
2973       scope.id = attrs.id || foundationApi.generateUuid();
2974       scope.active = false;
2975       scope.transcludeFn = transclude;
2976       controller.addTab(scope);
2977
2978       foundationApi.subscribe(scope.id, function(msg) {
2979         if(msg === 'show' || msg === 'open' || msg === 'activate') {
2980           scope.makeActive();
2981         }
2982       });
2983
2984       scope.makeActive = function() {
2985         controller.select(scope);
2986       };
2987     }
2988   }
2989
2990   zfTabIndividual.$inject = ['FoundationApi'];
2991
2992   function zfTabIndividual(foundationApi) {
2993     var directive = {
2994       restrict: 'EA',
2995       transclude: 'true',
2996       link: link
2997     };
2998
2999     return directive;
3000
3001     function link(scope, element, attrs, ctrl, transclude) {
3002       var tab = scope.$eval(attrs.tab);
3003       var id = tab.scope.id;
3004
3005       tab.scope.transcludeFn(tab.scope, function(tabContent) {
3006         element.append(tabContent);
3007       });
3008
3009       foundationApi.subscribe(tab.scope.id, function(msg) {
3010         foundationApi.publish(tab.parentContent, ['activate', tab.scope.id]);
3011         scope.$apply();
3012       });
3013
3014     }
3015   }
3016
3017   //custom tabs
3018
3019   zfTabHref.$inject = ['FoundationApi'];
3020
3021   function zfTabHref(foundationApi) {
3022     var directive = {
3023       restrict: 'A',
3024       replace: false,
3025       link: link
3026     }
3027
3028     return directive;
3029
3030     function link(scope, element, attrs, ctrl) {
3031       var target = attrs.zfTabHref;
3032
3033       foundationApi.subscribe(target, function(msg) {
3034         if(msg === 'activate' || msg === 'show' || msg === 'open') {
3035           makeActive();
3036         }
3037       });
3038
3039
3040       element.on('click', function(e) {
3041         foundationApi.publish(target, 'activate');
3042         makeActive();
3043         e.preventDefault();
3044       });
3045
3046       function makeActive() {
3047         element.parent().children().removeClass('is-active');
3048         element.addClass('is-active');
3049       }
3050     }
3051   }
3052
3053   zfTabCustom.$inject = ['FoundationApi'];
3054
3055   function zfTabCustom(foundationApi) {
3056     var directive = {
3057       restrict: 'A',
3058       replace: false,
3059       link: link
3060     };
3061
3062     return directive;
3063
3064     function link(scope, element, attrs, ctrl, transclude) {
3065       var children = element.children();
3066       angular.element(children[0]).addClass('is-active');
3067     }
3068   }
3069
3070   zfTabContentCustom.$inject = ['FoundationApi'];
3071
3072   function zfTabContentCustom(foundationApi) {
3073     return {
3074       restrict: 'A',
3075       link: link
3076     };
3077
3078     function link(scope, element, attrs) {
3079       var tabs = [];
3080       var children = element.children();
3081
3082       angular.forEach(children, function(node) {
3083         if(node.id) {
3084           var tabId = node.id;
3085           tabs.push(tabId);
3086           foundationApi.subscribe(tabId, function(msg) {
3087             if(msg === 'activate' || msg === 'show' || msg === 'open') {
3088               activateTabs(tabId);
3089             }
3090           });
3091
3092           if(tabs.length === 1) {
3093             var el = angular.element(node);
3094             el.addClass('is-active');
3095           }
3096         }
3097       });
3098
3099       function activateTabs(tabId) {
3100         var tabNodes = element.children();
3101         angular.forEach(tabNodes, function(node) {
3102           var el = angular.element(node);
3103           el.removeClass('is-active');
3104           if(el.attr('id') === tabId) {
3105             el.addClass('is-active');
3106           }
3107
3108         });
3109       }
3110     }
3111   }
3112
3113 })();
3114
3115 (function() {
3116   'use strict';
3117
3118   // imports all components and dependencies under a single namespace
3119
3120   angular.module('foundation', [
3121     'foundation.core',
3122     'foundation.mediaquery',
3123     'foundation.accordion',
3124     'foundation.actionsheet',
3125     'foundation.common',
3126     'foundation.iconic',
3127     'foundation.interchange',
3128     'foundation.modal',
3129     'foundation.notification',
3130     'foundation.offcanvas',
3131     'foundation.panel',
3132     'foundation.popup',
3133     'foundation.tabs'
3134   ]);
3135
3136 })();