2 * iconic.js v0.4.0 - The Iconic JavaScript library
3 * Copyright (c) 2014 Waybury - http://useiconic.com
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)});
10 angular.module('foundation.core.animation', [])
11 .service('FoundationAnimation', FoundationAnimation)
14 function FoundationAnimation() {
18 var initClasses = ['ng-enter', 'ng-leave'];
19 var activeClasses = ['ng-enter-active', 'ng-leave-active'];
20 var activeGenericClass = 'is-active';
22 'webkitAnimationEnd', 'mozAnimationEnd',
23 'MSAnimationEnd', 'oanimationend',
24 'animationend', 'webkitTransitionEnd',
25 'otransitionend', 'transitionend'
28 service.animate = animate;
29 service.toggleAnimation = toggleAnimation;
33 function toggleAnimation(element, futureState) {
35 element.addClass(activeGenericClass);
37 element.removeClass(activeGenericClass);
41 function animate(element, futureState, animationIn, animationOut) {
44 self.cancelAnimation = cancelAnimation;
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];
51 registerElement(element);
53 element.addClass(animationClass);
54 element.addClass(initClass);
56 element.addClass(activeGenericClass);
62 element[0].style.transitionDuration = '';
63 element.addClass(activeClass);
65 element.one(events.join(' '), function() {
69 setTimeout(function() {
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
85 function cancelAnimation(element) {
86 deregisterElement(element);
87 angular.element(element).off(events.join(' ')); //kill all animation event handlers
91 function registerElement(el) {
97 //kill in progress animations
98 var inProgress = animations.filter(function(obj) {
101 if(inProgress.length > 0) {
102 var target = inProgress[0].el[0];
104 inProgress[0].animation.cancelAnimation(target);
107 animations.push(elObj);
110 function deregisterElement(el) {
112 var currentAnimation = animations.filter(function(obj, ind) {
119 animations.splice(index, 1);
125 return element[0].offsetWidth;
129 element[0].style.transitionDuration = 0;
130 element.removeClass(initClasses.join(' ') + ' ' + activeClasses.join(' ') + ' ' + animationIn + ' ' + animationOut);
140 angular.module('foundation.core', [
141 'foundation.core.animation'
143 .service('FoundationApi', FoundationApi)
144 .service('FoundationAdapter', FoundationAdapter)
145 .factory('Utils', Utils)
148 FoundationApi.$inject = ['FoundationAnimation'];
150 function FoundationApi(FoundationAnimation) {
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;
168 function subscribe(name, callback) {
169 if (!listeners[name]) {
170 listeners[name] = [];
173 listeners[name].push(callback);
177 function unsubscribe(name, callback) {
178 if (listeners[name] !== undefined) {
179 delete listeners[name];
181 if (typeof callback == 'function') {
186 function publish(name, msg) {
187 if (!listeners[name]) {
188 listeners[name] = [];
191 listeners[name].forEach(function(cb) {
198 function getSettings() {
202 function modifySettings(tree) {
203 settings = angular.extend(settings, tree);
207 function generateUuid() {
210 //little trick to produce semi-random IDs
213 for (var i=0; i<15; i++) {
214 uuid += Math.floor(Math.random()*16).toString(16);
216 } while(!uniqueIds.indexOf(uuid));
218 uniqueIds.push(uuid);
222 function toggleAnimate(element, futureState) {
223 FoundationAnimation.toggleAnimate(element, futureState);
226 function closeActiveElements(options) {
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')
233 if (activeElements.length) {
234 angular.forEach(activeElements, function(el) {
235 if (options.exclude !== el.id) {
236 self.publish(el.id, 'close');
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');
250 function animate(element, futureState, animationIn, animationOut) {
251 FoundationAnimation.animate(element, futureState, animationIn, animationOut);
255 FoundationAdapter.$inject = ['FoundationApi'];
257 function FoundationAdapter(foundationApi) {
261 service.activate = activate;
262 service.deactivate = deactivate;
266 function activate(target) {
267 foundationApi.publish(target, 'show');
270 function deactivate(target) {
271 foundationApi.publish(target, 'hide');
279 utils.throttle = throttleUtil;
283 function throttleUtil(func, delay) {
287 var context = this, args = arguments;
289 if (timer === null) {
290 timer = setTimeout(function () {
291 func.apply(context, args);
304 angular.module('foundation.dynamicRouting.animations', ['foundation.dynamicRouting'])
305 .directive('uiView', uiView)
308 uiView.$inject = ['$rootScope', '$state'];
310 function uiView($rootScope, $state) {
319 function link(scope, element) {
321 var animationEnded = false;
325 $rootScope.$on('$stateChangeStart', onStateChangeStart),
326 $rootScope.$on('$stateChangeError', onStateChangeError),
327 scope.$on('$stateChangeSuccess', onStateChangeSuccess),
328 scope.$on('$viewContentAnimationEnded', onViewContentAnimationEnded)
331 var destroyed = scope.$on('$destroy', function onDestroy() {
332 angular.forEach(cleanup, function (cb) {
333 if (angular.isFunction(cb)) {
341 function onStateChangeStart(event, toState, toParams, fromState, fromParams) {
343 if (fromState.animation) {
344 if (!fromState.animation.leave && !toState.animation.leave) {
348 animationRouter(event, toState, fromState);
353 function animationRouter(event, toState, fromState) {
354 if (!animationEnded) {
358 element.removeClass(fromState.animation.leave);
363 element.addClass(fromState.animation.leave);
368 function onStateChangeError() {
369 if(animation.leave) {
370 element.removeClass(animation.leave);
373 resetParent(); //reset parent if state change fails
376 function onStateChangeSuccess() {
378 if ($state.includes(getState()) && animation.enter) {
379 element.addClass(animation.enter);
383 function onViewContentAnimationEnded(event) {
384 if (event.targetScope === scope && animation.enter) {
385 element.removeClass(animation.enter);
388 animationEnded = true;
392 function getState() {
393 var view = element.data('$uiView');
394 var state = view && view.state && view.state.self;
397 angular.extend(animation, state.animation);
403 function resetParent() {
404 element.parent().removeClass('position-absolute');
405 if(presetHeight !== true) {
406 element.parent()[0].style.height = null;
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 : '';
415 if(parentHeight > 0) {
419 element.parent()[0].style.height = tempHeight + 'px';
420 element.parent().addClass('position-absolute');
429 angular.module('foundation.dynamicRouting', ['ui.router'])
430 .provider('$FoundationState', FoundationState)
431 .controller('DefaultController', DefaultController)
432 .config(DynamicRoutingConfig)
433 .run(DynamicRoutingRun)
436 FoundationState.$inject = ['$stateProvider'];
438 function FoundationState($stateProvider) {
439 var complexViews = {};
441 this.registerDynamicRoutes = function(routes) {
442 var dynamicRoutes = routes || foundationRoutes;
444 angular.forEach(dynamicRoutes, function(page) {
445 if (page.hasComposed) {
446 if (!angular.isDefined(complexViews[page.parent])) {
447 complexViews[page.parent] = { children: {} };
450 if (page.controller) {
451 page.controller = getController(page);
454 complexViews[page.parent].children[page.name] = page;
456 } else if (page.composed) {
457 if(!angular.isDefined(complexViews[page.name])) {
458 complexViews[page.name] = { children: {} };
461 if (page.controller) {
462 page.controller = getController(page);
465 angular.extend(complexViews[page.name], page);
469 templateUrl: page.path,
470 abstract: page.abstract || false,
471 parent: page.parent || '',
472 controller: getController(page),
474 animation: buildAnimations(page),
477 $stateProvider.state(page.name, state);
481 angular.forEach(complexViews, function(page) {
484 parent: page.parent || '',
485 abstract: page.abstract || false,
487 animation: buildAnimations(page),
489 '': buildState(page.path, page)
493 angular.forEach(page.children, function(sub) {
494 state.views[sub.name + '@' + page.name] = buildState(sub.path, page);
497 $stateProvider.state(page.name, state);
501 this.$get = angular.noop;
503 function getData(page) {
504 var data = { vars: {} };
506 if (typeof page.data.vars === "object") {
507 data.vars = page.data.vars;
509 delete page.data.vars;
510 angular.extend(data, page.data);
513 angular.extend(data.vars, page);
517 function buildState(path, state) {
520 controller: getController(state),
524 function getController(state) {
525 var ctrl = state.controller || 'DefaultController';
527 if (!/\w\s+as\s+\w/.test(ctrl)) {
528 ctrl += ' as PageCtrl';
534 function buildAnimations(state) {
537 if (state.animationIn) {
538 animations.enter = state.animationIn;
541 if (state.animationOut) {
542 animations.leave = state.animationOut;
549 DefaultController.$inject = ['$scope', '$stateParams', '$state'];
551 function DefaultController($scope, $stateParams, $state) {
553 angular.forEach($stateParams, function(value, key) {
557 $scope.params = params;
558 $scope.current = $state.current.name;
560 if($state.current.views) {
561 $scope.vars = $state.current.data.vars;
562 $scope.composed = $state.current.data.vars.children;
564 $scope.vars = $state.current.data.vars;
568 DynamicRoutingConfig.$inject = ['$FoundationStateProvider'];
570 function DynamicRoutingConfig(FoundationStateProvider) {
571 FoundationStateProvider.registerDynamicRoutes(foundationRoutes);
574 DynamicRoutingRun.$inject = ['$rootScope', '$state', '$stateParams'];
576 function DynamicRoutingRun($rootScope, $state, $stateParams) {
577 $rootScope.$state = $state;
578 $rootScope.$stateParams = $stateParams;
586 angular.module('foundation.mediaquery', ['foundation.core'])
588 .factory('FoundationMQInit', FoundationMQInit)
589 .factory('mqHelpers', mqHelpers)
590 .service('FoundationMQ', FoundationMQ)
593 mqInitRun.$inject = ['FoundationMQInit'];
595 function mqInitRun(mqInit) {
599 FoundationMQInit.$inject = ['mqHelpers', 'FoundationApi', 'Utils'];
601 function FoundationMQInit(helpers, foundationApi, u){
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)'
624 helpers.headerHelper(['foundation-mq']);
625 extractedMedia = helpers.getStyle('.foundation-mq', 'font-family');
627 mediaQueries = helpers.parseStyleToObject((extractedMedia));
629 for(var key in mediaQueries) {
630 mediaQueries[key] = 'only screen and (min-width: ' + mediaQueries[key].replace('rem', 'em') + ')';
634 foundationApi.modifySettings({
635 mediaQueries: angular.extend(mediaQueries, namedQueries)
638 window.addEventListener('resize', u.throttle(function() {
639 foundationApi.publish('resize', 'window resized');
646 function mqHelpers() {
649 factory.headerHelper = headerHelper;
650 factory.getStyle = getStyle;
651 factory.parseStyleToObject = parseStyleToObject;
655 function headerHelper(classArray) {
656 var i = classArray.length;
657 var head = angular.element(document.querySelectorAll('head'));
660 head.append('<meta class="' + classArray[i] + '" />');
666 function getStyle(selector, styleName) {
667 var elem = document.querySelectorAll(selector)[0];
668 var style = window.getComputedStyle(elem, null);
670 return style.getPropertyValue('font-family');
673 // https://github.com/sindresorhus/query-string
674 function parseStyleToObject(str) {
675 var styleObject = {};
677 if (typeof str !== 'string') {
681 str = str.trim().slice(1, -1); // browsers re-quote string style values
687 styleObject = str.split('&').reduce(function(ret, param) {
688 var parts = param.replace(/\+/g, ' ').split('=');
691 key = decodeURIComponent(key);
693 // missing `=` should be `null`:
694 // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters
695 val = val === undefined ? null : decodeURIComponent(val);
697 if (!ret.hasOwnProperty(key)) {
699 } else if (Array.isArray(ret[key])) {
702 ret[key] = [ret[key], val];
711 FoundationMQ.$inject = ['FoundationApi'];
713 function FoundationMQ(foundationApi) {
716 service.getMediaQueries = getMediaQueries;
717 service.match = match;
718 service.collectScenariosFromElement = collectScenariosFromElement;
722 function getMediaQueries() {
723 return foundationApi.getSettings().mediaQueries;
726 function match(scenarios) {
727 var count = scenarios.length;
728 var queries = service.getMediaQueries();
734 var rule = scenarios[count].media;
737 mq = matchMedia(queries[rule]);
739 mq = matchMedia(rule);
743 matches.push({ ind: count});
751 // Collects a scenario object and templates from element
752 function collectScenariosFromElement(parentElement) {
756 var elements = parentElement.children();
759 angular.forEach(elements, function(el) {
760 var elem = angular.element(el);
763 //if no source or no html, capture element itself
764 if (!elem.attr('src') || !elem.attr('src').match(/.html$/)) {
766 scenarios[i] = { media: elem.attr('media'), templ: i };
768 scenarios[i] = { media: elem.attr('media'), src: elem.attr('src') };
775 scenarios: scenarios,
782 angular.module('markdown', [])
783 .directive('markdown', function() {
786 link: function(scope, element, attrs, controller) {
787 element.html(marked(element.html()));
796 var svgDirectives = {};
813 svgDirectives[attr] = [
819 'computeSVGAttrValue',
820 'svgAttrExpressions',
828 svgAttrExpressions) {
831 link: function(scope, element, attrs) {
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;
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);
846 function updateValue () {
847 var newVal = computeSVGAttrValue(initialUrl);
848 //Prevent recursive updating
849 if (newVal && attrs[attr] !== newVal) attrs.$set(attr, newVal);
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;
865 value('svgAttrExpressions', {
866 FUNC_URI: /^url\((.*)\)$/,
867 SVG_ELEMENT: /SVG[a-zA-Z]*Element/,
870 factory('computeSVGAttrValue', [
871 '$location', '$sniffer', 'svgAttrExpressions', 'urlResolve',
872 function($location, $sniffer, svgAttrExpressions, urlResolve) {
873 return function computeSVGAttrValue(url) {
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, '') +
882 //Presumably links to external SVG document
884 fullUrl = urlResolve(match[1]);
887 return fullUrl ? 'url(' + fullUrl + ')' : null;
892 directive(svgDirectives);
898 angular.module('foundation.accordion', [])
899 .controller('ZfAccordionController', zfAccordionController)
900 .directive('zfAccordion', zfAccordion)
901 .directive('zfAccordionItem', zfAccordionItem)
904 zfAccordionController.$inject = ['$scope'];
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
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;
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;
926 section.scope.active = false;
933 controller.addSection = function addsection(sectionScope) {
934 sections.push({ scope: sectionScope });
936 if(sections.length === 1 && autoOpen === true) {
937 sections[0].active = true;
938 sections[0].scope.active = true;
942 controller.closeAll = function() {
943 sections.forEach(function(section) {
944 section.scope.active = false;
949 function zfAccordion() {
954 templateUrl: 'components/accordion/accordion.html',
955 controller: 'ZfAccordionController',
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;
974 function zfAccordionItem() {
977 templateUrl: 'components/accordion/accordion-item.html',
982 require: '^zfAccordion',
984 controller: function() {},
990 function link(scope, element, attrs, controller, transclude) {
991 scope.active = false;
992 controller.addSection(scope);
994 scope.activate = function() {
995 controller.select(scope);
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)
1014 FoundationActionSheet.$inject = ['FoundationApi'];
1016 function FoundationActionSheet(foundationApi) {
1019 service.activate = activate;
1020 service.deactivate = deactivate;
1024 //target should be element ID
1025 function activate(target) {
1026 foundationApi.publish(target, 'show');
1029 //target should be element ID
1030 function deactivate(target) {
1031 foundationApi.publish(target, 'hide');
1035 zfActionSheetController.$inject = ['$scope', 'FoundationApi'];
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);
1043 controller.registerContent = function(scope) {
1045 content.active = false;
1048 controller.registerContainer = function(scope) {
1050 container.active = false;
1053 controller.toggle = toggle;
1054 controller.hide = hide;
1056 controller.registerListener = function() {
1057 document.body.addEventListener('click', listenerLogic);
1060 controller.deregisterListener = function() {
1061 document.body.removeEventListener('click', listenerLogic);
1064 function listenerLogic(e) {
1066 var insideActionSheet = false;
1069 if(el.classList && el.classList.contains('action-sheet-container')) {
1070 insideActionSheet = true;
1074 } while ((el = el.parentNode));
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']) {
1081 // if the element is outside the action sheet and is NOT a toggle element, hide
1103 zfActionSheet.$inject = ['FoundationApi'];
1105 function zfActionSheet(foundationApi) {
1110 templateUrl: 'components/actionsheet/actionsheet.html',
1111 controller: 'ZfActionSheetController',
1117 function compile() {
1124 function preLink(scope, iElement, iAttrs) {
1125 iAttrs.$set('zf-closable', 'actionsheet');
1128 function postLink(scope, element, attrs, controller) {
1129 var id = attrs.id || foundationApi.generateUuid();
1130 attrs.$set('id', id);
1132 scope.active = false;
1134 foundationApi.subscribe(id, function(msg) {
1135 if (msg === 'toggle') {
1136 controller.toggle();
1139 if (msg === 'hide' || msg === 'close') {
1145 controller.registerContainer(scope);
1147 scope.toggle = function() {
1148 scope.active = !scope.active;
1152 scope.hide = function() {
1153 scope.active = false;
1160 zfAsContent.$inject = ['FoundationApi'];
1162 function zfAsContent(foundationApi) {
1167 templateUrl: 'components/actionsheet/actionsheet-content.html',
1168 require: '^zfActionSheet',
1177 function link(scope, element, attrs, controller) {
1178 scope.active = false;
1179 scope.position = scope.position || 'bottom';
1180 controller.registerContent(scope);
1182 scope.toggle = function() {
1183 scope.active = !scope.active;
1185 controller.registerListener();
1187 controller.deregisterListener();
1193 scope.hide = function() {
1194 scope.active = false;
1195 controller.deregisterListener();
1201 zfAsButton.$inject = ['FoundationApi'];
1203 function zfAsButton(foundationApi) {
1208 templateUrl: 'components/actionsheet/actionsheet-button.html',
1209 require: '^zfActionSheet',
1218 function link(scope, element, attrs, controller) {
1220 element.on('click', function(e) {
1221 controller.toggle();
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)
1242 zfClose.$inject = ['FoundationApi'];
1244 function zfClose(foundationApi) {
1252 function link(scope, element, attrs) {
1254 if (attrs.zfClose) {
1255 targetId = attrs.zfClose;
1257 var parentElement= false;
1258 var tempElement = element.parent();
1260 while(parentElement === false) {
1261 if(tempElement[0].nodeName == 'BODY') {
1265 if(typeof tempElement.attr('zf-closable') !== 'undefined' && tempElement.attr('zf-closable') !== false) {
1266 parentElement = tempElement;
1269 tempElement = tempElement.parent();
1271 targetId = parentElement.attr('id');
1274 element.on('click', function(e) {
1275 foundationApi.publish(targetId, 'close');
1281 zfOpen.$inject = ['FoundationApi'];
1283 function zfOpen(foundationApi) {
1291 function link(scope, element, attrs) {
1292 element.on('click', function(e) {
1293 foundationApi.publish(attrs.zfOpen, 'open');
1299 zfToggle.$inject = ['FoundationApi'];
1301 function zfToggle(foundationApi) {
1309 function link(scope, element, attrs) {
1310 element.on('click', function(e) {
1311 foundationApi.publish(attrs.zfToggle, 'toggle');
1317 zfEscClose.$inject = ['FoundationApi'];
1319 function zfEscClose(foundationApi) {
1327 function link(scope, element, attrs) {
1328 element.on('keyup', function(e) {
1329 if (e.keyCode === 27) {
1330 foundationApi.closeActiveElements();
1337 zfSwipeClose.$inject = ['FoundationApi'];
1339 function zfSwipeClose(foundationApi) {
1346 function link($scope, element, attrs) {
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
1358 // detect what direction the directive is pointing
1359 switch (attrs.zfSwipeClose) {
1361 swipeDirection = 'swiperight';
1364 swipeDirection = 'swipeleft';
1367 swipeDirection = 'swipeup';
1370 swipeDirection = 'swipedown';
1373 swipeDirection = 'swipe';
1375 hammerElem.on(swipeDirection, function() {
1376 foundationApi.publish(attrs.id, 'close');
1381 zfHardToggle.$inject = ['FoundationApi'];
1383 function zfHardToggle(foundationApi) {
1391 function link(scope, element, attrs) {
1392 element.on('click', function(e) {
1393 foundationApi.closeActiveElements({exclude: attrs.zfHardToggle});
1394 foundationApi.publish(attrs.zfHardToggle, 'toggle');
1405 angular.module('foundation.iconic', [])
1406 .provider('Iconic', Iconic)
1407 .directive('zfIconic', zfIconic)
1413 var assetPath = 'assets/img/iconic/';
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
1419 this.setAssetPath = function (path) {
1420 assetPath = angular.isString(path) ? path : assetPath;
1424 * Service implementation
1427 this.$get = function () {
1428 var iconicObject = new IconicJS();
1431 getAccess: getAccess,
1432 getAssetPath: getAssetPath
1439 * @returns {Window.IconicJS}
1441 function getAccess() {
1442 return iconicObject;
1449 function getAssetPath() {
1455 zfIconic.$inject = ['Iconic', 'FoundationApi', '$compile'];
1457 function zfIconic(iconic, foundationApi, $compile) {
1460 template: '<img ng-transclude>',
1475 function compile() {
1476 var contents, assetPath;
1483 function preLink(scope, element, attrs) {
1485 if (scope.iconDir) {
1486 // path set via attribute
1487 assetPath = scope.iconDir;
1490 assetPath = iconic.getAssetPath();
1492 // make sure ends with /
1493 if (assetPath.charAt(assetPath.length - 1) !== '/') {
1498 attrs.$set('data-src', scope.dynSrc);
1499 } else if (scope.dynIcon) {
1500 attrs.$set('data-src', assetPath + scope.dynIcon + '.svg');
1503 attrs.$set('data-src', assetPath + scope.icon + '.svg');
1505 // To support expressions on data-src
1506 attrs.$set('data-src', attrs.src);
1510 // check if size already added as class
1511 if (!element.hasClass('iconic-sm') && !element.hasClass('iconic-md') && !element.hasClass('iconic-lg')) {
1513 switch (scope.size) {
1515 iconicClass = 'iconic-sm';
1518 iconicClass = 'iconic-md';
1521 iconicClass = 'iconic-lg';
1524 iconicClass = 'iconic-fluid';
1526 element.addClass(iconicClass);
1529 // save contents of un-inject html, to use for dynamic re-injection
1530 contents = element[0].outerHTML;
1533 function postLink(scope, element, attrs) {
1534 var svgElement, ico = iconic.getAccess();
1536 injectSvg(element[0]);
1538 foundationApi.subscribe('resize', function () {
1539 // only run update on current element
1540 ico.update(element[0]);
1543 // handle dynamic updating of src
1545 scope.$watch('dynSrc', function (newVal, oldVal) {
1546 if (newVal && newVal !== oldVal) {
1547 reinjectSvg(scope.dynSrc);
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');
1560 function reinjectSvg(newSrc) {
1564 svgElement.append(angular.element(contents));
1567 svgElement.attr('data-src', newSrc);
1570 injectSvg(svgElement[0]);
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());
1592 angular.module('foundation.interchange', ['foundation.core', 'foundation.mediaquery'])
1593 .directive('zfInterchange', zfInterchange)
1596 zfInterchange.$inject = [ '$compile', '$http', '$templateCache', 'FoundationApi', 'FoundationMQ'];
1598 function zfInterchange($compile, $http, $templateCache, foundationApi, foundationMQ) {
1602 transclude: 'element',
1607 template: '<div></div>',
1613 function link(scope, element, attrs, ctrl, transclude) {
1614 var childScope, current, scenarios, innerTemplates;
1616 var globalQueries = foundationMQ.getMediaQueries();
1619 foundationApi.subscribe('resize', function(msg) {
1620 transclude(function(clone, newScope) {
1621 if(!scenarios || !innerTemplates) {
1622 collectInformation(clone);
1625 var ruleMatches = foundationMQ.match(scenarios);
1626 var scenario = ruleMatches.length === 0 ? null : scenarios[ruleMatches[0].ind];
1628 //this could use some love
1629 if(scenario && checkScenario(scenario)) {
1633 childScope.$destroy();
1637 if(typeof scenario.templ !== 'undefined') {
1638 childScope = newScope;
1641 var tmp = document.createElement('div');
1642 tmp.appendChild(innerTemplates[scenario.templ][0]);
1644 element.html(tmp.innerHTML);
1645 $compile(element.contents())(childScope);
1648 var loader = templateLoader(scenario.src);
1649 loader.success(function(html) {
1650 childScope = newScope;
1653 $compile(element.contents())(childScope);
1663 foundationApi.publish('resize', 'initial resize');
1665 function templateLoader(templateUrl) {
1666 return $http.get(templateUrl, {cache: $templateCache});
1669 function collectInformation(el) {
1670 var data = foundationMQ.collectScenariosFromElement(el);
1672 scenarios = data.scenarios;
1673 innerTemplates = data.templates;
1676 function checkScenario(scenario) {
1677 return !current || current !== scenario;
1687 angular.module('foundation.modal', ['foundation.core'])
1688 .directive('zfModal', modalDirective)
1689 .factory('ModalFactory', ModalFactory)
1692 FoundationModal.$inject = ['FoundationApi', 'ModalFactory'];
1694 function FoundationModal(foundationApi, ModalFactory) {
1697 service.activate = activate;
1698 service.deactivate = deactivate;
1699 service.newModal = newModal;
1703 //target should be element ID
1704 function activate(target) {
1705 foundationApi.publish(target, 'show');
1708 //target should be element ID
1709 function deactivate(target) {
1710 foundationApi.publish(target, 'hide');
1713 //new modal has to be controlled via the new instance
1714 function newModal(config) {
1715 return new ModalFactory(config);
1719 modalDirective.$inject = ['FoundationApi'];
1721 function modalDirective(foundationApi) {
1725 templateUrl: 'components/modal/modal.html',
1734 function compile(tElement, tAttrs, transclude) {
1742 function preLink(scope, iElement, iAttrs, controller) {
1743 iAttrs.$set('zf-closable', type);
1746 function postLink(scope, element, attrs) {
1747 var dialog = angular.element(element.children()[0]);
1749 scope.active = scope.active || false;
1750 scope.overlay = attrs.overlay === 'false' ? false : true;
1751 scope.overlayClose = attrs.overlayClose === 'false' ? false : true;
1753 var animationIn = attrs.animationIn || 'fadeIn';
1754 var animationOut = attrs.animationOut || 'fadeOut';
1756 var overlayIn = 'fadeIn';
1757 var overlayOut = 'fadeOut';
1759 scope.hideOverlay = function() {
1760 if(scope.overlayClose) {
1765 scope.hide = function() {
1766 scope.active = false;
1771 scope.show = function() {
1772 scope.active = true;
1774 dialog.tabIndex = -1;
1779 scope.toggle = function() {
1780 scope.active = !scope.active;
1788 foundationApi.subscribe(attrs.id, function(msg) {
1789 if(msg === 'show' || msg === 'open') {
1791 } else if (msg === 'close' || msg === 'hide') {
1793 } else if (msg === 'toggle') {
1797 if (scope.$root && !scope.$root.$$phase) {
1804 function animate() {
1805 //animate both overlay and dialog
1806 if(!scope.overlay) {
1807 element.css('background', 'transparent');
1810 foundationApi.animate(element, scope.active, overlayIn, overlayOut);
1811 foundationApi.animate(dialog, scope.active, animationIn, animationOut);
1823 ModalFactory.$inject = ['$http', '$templateCache', '$rootScope', '$compile', '$timeout', '$q', 'FoundationApi'];
1825 function ModalFactory($http, $templateCache, $rootScope, $compile, $timeout, $q, foundationApi) {
1826 return modalFactory;
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(),
1848 if(config.templateUrl) {
1850 fetched = $http.get(config.templateUrl, {
1851 cache: $templateCache
1852 }).then(function (response) {
1853 html = response.data;
1854 assembleDirective();
1857 } else if(config.template) {
1858 //use provided template
1860 html = config.template;
1861 assembleDirective();
1864 self.activate = activate;
1865 self.deactivate = deactivate;
1866 self.toggle = toggle;
1867 self.destroy = destroy;
1872 deactivate: deactivate,
1877 function checkStatus() {
1879 throw "Error: Modal was destroyed. Delete the object and create a new ModalFactory instance."
1883 function activate() {
1885 $timeout(function() {
1887 foundationApi.publish(id, 'show');
1891 function deactivate() {
1893 $timeout(function() {
1895 foundationApi.publish(id, 'hide');
1901 $timeout(function() {
1903 foundationApi.publish(id, 'toggle');
1907 function init(state) {
1908 $q.when(fetched).then(function() {
1909 if(!attached && html.length > 0) {
1910 var modalEl = container.append(element);
1912 scope.active = state;
1913 $compile(element)(scope);
1920 function assembleDirective() {
1921 // check for duplicate elements to prevent factory from cloning modals
1922 if (document.getElementById(id)) {
1926 html = '<zf-modal id="' + id + '">' + html + '</zf-modal>';
1928 element = angular.element(html);
1930 scope = $rootScope.$new();
1932 // account for directive attributes
1933 for(var i = 0; i < props.length; i++) {
1934 var prop = props[i];
1939 element.attr('animation-in', config[prop]);
1941 case 'animationOut':
1942 element.attr('animation-out', config[prop]);
1945 element.attr(prop, config[prop]);
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];
1960 function destroy() {
1962 setTimeout(function() {
1967 foundationApi.unsubscribe(id);
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)
1989 FoundationNotification.$inject = ['FoundationApi', 'NotificationFactory'];
1991 function FoundationNotification(foundationApi, NotificationFactory) {
1994 service.activate = activate;
1995 service.deactivate = deactivate;
1999 //target should be element ID
2000 function activate(target) {
2001 foundationApi.publish(target, 'show');
2004 //target should be element ID
2005 function deactivate(target) {
2006 foundationApi.publish(target, 'hide');
2009 function toggle(target) {
2010 foundationApi.publish(target, 'toggle');
2013 function createNotificationSet(config) {
2014 return new NotificationFactory(config);
2019 ZfNotificationController.$inject = ['$scope', 'FoundationApi'];
2021 function ZfNotificationController($scope, foundationApi) {
2022 var controller = this;
2023 controller.notifications = $scope.notifications = $scope.notifications || [];
2025 controller.addNotification = function(info) {
2026 var id = foundationApi.generateUuid();
2028 $scope.notifications.push(info);
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);
2040 controller.clearAll = function() {
2041 while($scope.notifications.length > 0) {
2042 $scope.notifications.pop();
2047 zfNotificationSet.$inject = ['FoundationApi'];
2049 function zfNotificationSet(foundationApi) {
2052 templateUrl: 'components/notification/notification-set.html',
2053 controller: 'ZfNotificationController',
2063 function link(scope, element, attrs, controller) {
2064 scope.position = scope.position ? scope.position.split(' ').join('-') : 'top-right';
2066 foundationApi.subscribe(attrs.id, function(msg) {
2067 if(msg === 'clearall') {
2068 controller.clearAll();
2071 controller.addNotification(msg);
2072 if (!scope.$root.$$phase) {
2080 zfNotification.$inject = ['FoundationApi'];
2082 function zfNotification(foundationApi) {
2085 templateUrl: 'components/notification/notification.html',
2088 require: '^zfNotificationSet',
2089 controller: function() { },
2103 function compile() {
2110 function preLink(scope, iElement, iAttrs) {
2111 iAttrs.$set('zf-closable', 'notification');
2114 function postLink(scope, element, attrs, controller) {
2115 scope.active = false;
2116 var animationIn = attrs.animationIn || 'fadeIn';
2117 var animationOut = attrs.animationOut || 'fadeOut';
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);
2126 scope.hide = function() {
2127 scope.active = false;
2128 foundationApi.animate(element, scope.active, animationIn, animationOut);
2129 setTimeout(function() {
2130 controller.removeNotification(scope.notifId);
2134 // close if autoclose
2135 if (scope.autoclose) {
2136 setTimeout(function() {
2140 }, parseInt(scope.autoclose));
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
2154 hammerElem.on('swipe', function() {
2163 zfNotificationStatic.$inject = ['FoundationApi'];
2165 function zfNotificationStatic(foundationApi) {
2168 templateUrl: 'components/notification/notification-static.html',
2183 function compile() {
2184 var type = 'notification';
2191 function preLink(scope, iElement, iAttrs, controller) {
2192 iAttrs.$set('zf-closable', type);
2195 function postLink(scope, element, attrs, controller) {
2196 scope.position = attrs.position ? attrs.position.split(' ').join('-') : 'top-right';
2198 var animationIn = attrs.animationIn || 'fadeIn';
2199 var animationOut = attrs.animationOut || 'fadeOut';
2202 foundationApi.subscribe(attrs.id, function(msg) {
2203 if(msg == 'show' || msg == 'open') {
2205 // close if autoclose
2206 if (scope.autoclose) {
2207 setTimeout(function() {
2211 }, parseInt(scope.autoclose));
2213 } else if (msg == 'close' || msg == 'hide') {
2215 } else if (msg == 'toggle') {
2217 // close if autoclose
2218 if (scope.autoclose) {
2219 setTimeout(function() {
2223 }, parseInt(scope.autoclose));
2227 foundationApi.animate(element, scope.active, animationIn, animationOut);
2233 scope.hide = function() {
2234 scope.active = false;
2235 foundationApi.animate(element, scope.active, animationIn, animationOut);
2239 scope.show = function() {
2240 scope.active = true;
2241 foundationApi.animate(element, scope.active, animationIn, animationOut);
2245 scope.toggle = function() {
2246 scope.active = !scope.active;
2247 foundationApi.animate(element, scope.active, animationIn, animationOut);
2255 zfNotify.$inject = ['FoundationApi'];
2257 function zfNotify(foundationApi) {
2272 function link(scope, element, attrs, controller) {
2273 element.on('click', function(e) {
2274 foundationApi.publish(attrs.zfNotify, {
2276 content: scope.content,
2279 autoclose: scope.autoclose
2286 NotificationFactory.$inject = ['$http', '$templateCache', '$rootScope', '$compile', '$timeout', 'FoundationApi'];
2288 function NotificationFactory($http, $templateCache, $rootScope, $compile, $timeout, foundationApi) {
2289 return notificationFactory;
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(),
2307 assembleDirective();
2309 self.addNotification = addNotification;
2310 self.clearAll = clearAll;
2311 self.destroy = destroy;
2314 addNotification: addNotification,
2319 function checkStatus() {
2321 throw "Error: Notification Set was destroyed. Delete the object and create a new NotificationFactory instance."
2325 function addNotification(notification) {
2327 $timeout(function() {
2328 foundationApi.publish(id, notification);
2332 function clearAll() {
2334 $timeout(function() {
2335 foundationApi.publish(id, 'clearall');
2339 function init(state) {
2340 if(!attached && html.length > 0) {
2341 var modalEl = container.append(element);
2343 scope.active = state;
2344 $compile(element)(scope);
2350 function assembleDirective() {
2351 // check for duplicate element to prevent factory from cloning notification sets
2352 if (document.getElementById(id)) {
2355 html = '<zf-notification-set id="' + id + '"></zf-notification-set>';
2357 element = angular.element(html);
2359 scope = $rootScope.$new();
2361 for(var i = 0; i < props.length; i++) {
2362 if(config[props[i]]) {
2363 element.attr(props[i], config[props[i]]);
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];
2379 function destroy() {
2381 setTimeout(function() {
2386 foundationApi.unsubscribe(id);
2397 angular.module('foundation.offcanvas', ['foundation.core'])
2398 .directive('zfOffcanvas', zfOffcanvas)
2399 .service('FoundationOffcanvas', FoundationOffcanvas)
2402 FoundationOffcanvas.$inject = ['FoundationApi'];
2404 function FoundationOffcanvas(foundationApi) {
2407 service.activate = activate;
2408 service.deactivate = deactivate;
2412 //target should be element ID
2413 function activate(target) {
2414 foundationApi.publish(target, 'show');
2417 //target should be element ID
2418 function deactivate(target) {
2419 foundationApi.publish(target, 'hide');
2422 function toggle(target) {
2423 foundationApi.publish(target, 'toggle');
2427 zfOffcanvas.$inject = ['FoundationApi'];
2429 function zfOffcanvas(foundationApi) {
2432 templateUrl: 'components/offcanvas/offcanvas.html',
2443 function compile(tElement, tAttrs, transclude) {
2444 var type = 'offcanvas';
2451 function preLink(scope, iElement, iAttrs, controller) {
2452 iAttrs.$set('zf-closable', type);
2453 document.body.classList.add('has-off-canvas');
2456 function postLink(scope, element, attrs) {
2457 scope.position = scope.position || 'left';
2459 scope.active = false;
2461 foundationApi.subscribe(attrs.id, function(msg) {
2462 if(msg === 'show' || msg === 'open') {
2464 } else if (msg === 'close' || msg === 'hide') {
2466 } else if (msg === 'toggle') {
2470 if (!scope.$root.$$phase) {
2477 scope.hide = function() {
2478 scope.active = false;
2482 scope.show = function() {
2483 scope.active = true;
2487 scope.toggle = function() {
2488 scope.active = !scope.active;
2500 angular.module('foundation.panel', ['foundation.core'])
2501 .directive('zfPanel', zfPanel)
2502 .service('FoundationPanel', FoundationPanel)
2505 FoundationPanel.$inject = ['FoundationApi'];
2507 function FoundationPanel(foundationApi) {
2510 service.activate = activate;
2511 service.deactivate = deactivate;
2515 //target should be element ID
2516 function activate(target) {
2517 foundationApi.publish(target, 'show');
2520 //target should be element ID
2521 function deactivate(target) {
2522 foundationApi.publish(target, 'hide');
2526 zfPanel.$inject = ['FoundationApi', '$window'];
2528 function zfPanel(foundationApi, $window) {
2531 templateUrl: 'components/panel/panel.html',
2542 function compile(tElement, tAttrs, transclude) {
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;
2556 function postLink(scope, element, attrs) {
2557 scope.active = false;
2558 var animationIn, animationOut;
2559 var globalQueries = foundationApi.getSettings().mediaQueries;
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';
2578 foundationApi.subscribe(attrs.id, function(msg) {
2579 var panelPosition = $window.getComputedStyle(element[0]).getPropertyValue("position");
2581 // patch to prevent panel animation on larger screen devices
2582 if (panelPosition !== 'absolute') {
2586 if(msg == 'show' || msg == 'open') {
2588 } else if (msg == 'close' || msg == 'hide') {
2590 } else if (msg == 'toggle') {
2594 if (!scope.$root.$$phase) {
2601 scope.hide = function() {
2603 scope.active = false;
2604 foundationApi.animate(element, scope.active, animationIn, animationOut);
2610 scope.show = function() {
2612 scope.active = true;
2613 foundationApi.animate(element, scope.active, animationIn, animationOut);
2619 scope.toggle = function() {
2620 scope.active = !scope.active;
2621 foundationApi.animate(element, scope.active, animationIn, animationOut);
2626 element.on('click', function(e) {
2628 var srcEl = e.srcElement;
2630 if(!matchMedia(globalQueries.medium).matches && srcEl.href && srcEl.href.length > 0) {
2631 //hide element if it can't match at least medium
2633 foundationApi.animate(element, scope.active, animationIn, animationOut);
2645 angular.module('foundation.popup', ['foundation.core'])
2646 .directive('zfPopup', zfPopup)
2647 .directive('zfPopupToggle', zfPopupToggle)
2648 .service('FoundationPopup', FoundationPopup)
2651 FoundationPopup.$inject = ['FoundationApi'];
2653 function FoundationPopup(foundationApi) {
2656 service.activate = activate;
2657 service.deactivate = deactivate;
2661 //target should be element ID
2662 function activate(target) {
2663 foundationApi.publish(target, 'show');
2666 //target should be element ID
2667 function deactivate(target) {
2668 foundationApi.publish(target, 'hide');
2671 function toggle(target, popupTarget) {
2672 foundationApi.publish(target, ['toggle', popupTarget]);
2676 zfPopup.$inject = ['FoundationApi'];
2678 function zfPopup(foundationApi) {
2683 templateUrl: 'components/popup/popup.html',
2694 function compile() {
2700 function preLink(scope, iElement, iAttrs) {
2701 iAttrs.$set('zf-closable', 'popup');
2704 function postLink(scope, element, attrs) {
2705 scope.active = false;
2706 scope.target = scope.target || false;
2708 var attachment = scope.pinTo || 'top center';
2709 var targetAttachment = scope.pinAt || 'bottom center';
2710 var tetherInit = false;
2714 foundationApi.subscribe(attrs.id, function(msg) {
2715 if(msg[0] === 'show' || msg[0] === 'open') {
2717 } else if (msg[0] === 'close' || msg[0] === 'hide') {
2719 } else if (msg[0] === 'toggle') {
2720 scope.toggle(msg[1]);
2729 scope.hide = function() {
2730 scope.active = false;
2736 scope.show = function(newTarget) {
2737 scope.active = true;
2738 tetherElement(newTarget);
2744 scope.toggle = function(newTarget) {
2745 scope.active = !scope.active;
2746 tetherElement(newTarget);
2757 function tetherElement(target) {
2762 scope.target = scope.target ? document.getElementById(scope.target) : document.getElementById(target);
2764 tether = new Tether({
2765 element: element[0],
2766 target: scope.target,
2767 attachment: attachment,
2768 targetAttachment: targetAttachment,
2779 zfPopupToggle.$inject = ['FoundationApi'];
2781 function zfPopupToggle(foundationApi) {
2789 function link(scope, element, attrs) {
2790 var target = attrs.zfPopupToggle;
2791 var id = attrs.id || foundationApi.generateUuid();
2792 attrs.$set('id', id);
2794 element.on('click', function(e) {
2795 foundationApi.publish(target, ['toggle', id]);
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)
2818 FoundationTabs.$inject = ['FoundationApi'];
2820 function FoundationTabs(foundationApi) {
2823 service.activate = activate;
2827 //target should be element ID
2828 function activate(target) {
2829 foundationApi.publish(target, 'show');
2834 ZfTabsController.$inject = ['$scope', 'FoundationApi'];
2836 function ZfTabsController($scope, foundationApi) {
2837 var controller = this;
2838 var tabs = controller.tabs = $scope.tabs = [];
2841 controller.select = function(selectTab) {
2842 tabs.forEach(function(tab) {
2844 tab.scope.active = false;
2846 if(tab.scope === selectTab) {
2847 foundationApi.publish(id, ['activate', tab]);
2850 tab.scope.active = true;
2856 controller.addTab = function addTab(tabScope) {
2857 tabs.push({ scope: tabScope, active: false, parentContent: controller.id });
2859 if(tabs.length === 1) {
2860 tabs[0].active = true;
2861 tabScope.active = true;
2865 controller.getId = function() {
2869 controller.setId = function(newId) {
2874 zfTabs.$inject = ['FoundationApi'];
2876 function zfTabs(foundationApi) {
2881 templateUrl: 'components/tabs/tabs.html',
2882 controller: 'ZfTabsController',
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);
2897 //update tabs in case tab-content doesn't have them
2898 var updateTabs = function() {
2899 foundationApi.publish(scope.id + '-tabs', scope.tabs);
2902 foundationApi.subscribe(scope.id + '-get-tabs', function() {
2908 zfTabContent.$inject = ['FoundationApi'];
2910 function zfTabContent(foundationApi) {
2919 templateUrl: 'components/tabs/tab-content.html',
2925 function link(scope, element, attrs, ctrl) {
2926 scope.tabs = scope.tabs || [];
2927 var id = scope.target;
2929 foundationApi.subscribe(id, function(msg) {
2930 if(msg[0] === 'activate') {
2932 scope.tabs.forEach(function (tab) {
2933 tab.scope.active = false;
2936 if(tab.scope.id === id) {
2937 tab.scope.active = true;
2944 //if tabs empty, request tabs
2945 if(scope.tabs.length === 0) {
2946 foundationApi.subscribe(id + '-tabs', function(tabs) {
2950 foundationApi.publish(id + '-get-tabs', '');
2955 zfTab.$inject = ['FoundationApi'];
2957 function zfTab(foundationApi) {
2960 templateUrl: 'components/tabs/tab.html',
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);
2978 foundationApi.subscribe(scope.id, function(msg) {
2979 if(msg === 'show' || msg === 'open' || msg === 'activate') {
2984 scope.makeActive = function() {
2985 controller.select(scope);
2990 zfTabIndividual.$inject = ['FoundationApi'];
2992 function zfTabIndividual(foundationApi) {
3001 function link(scope, element, attrs, ctrl, transclude) {
3002 var tab = scope.$eval(attrs.tab);
3003 var id = tab.scope.id;
3005 tab.scope.transcludeFn(tab.scope, function(tabContent) {
3006 element.append(tabContent);
3009 foundationApi.subscribe(tab.scope.id, function(msg) {
3010 foundationApi.publish(tab.parentContent, ['activate', tab.scope.id]);
3019 zfTabHref.$inject = ['FoundationApi'];
3021 function zfTabHref(foundationApi) {
3030 function link(scope, element, attrs, ctrl) {
3031 var target = attrs.zfTabHref;
3033 foundationApi.subscribe(target, function(msg) {
3034 if(msg === 'activate' || msg === 'show' || msg === 'open') {
3040 element.on('click', function(e) {
3041 foundationApi.publish(target, 'activate');
3046 function makeActive() {
3047 element.parent().children().removeClass('is-active');
3048 element.addClass('is-active');
3053 zfTabCustom.$inject = ['FoundationApi'];
3055 function zfTabCustom(foundationApi) {
3064 function link(scope, element, attrs, ctrl, transclude) {
3065 var children = element.children();
3066 angular.element(children[0]).addClass('is-active');
3070 zfTabContentCustom.$inject = ['FoundationApi'];
3072 function zfTabContentCustom(foundationApi) {
3078 function link(scope, element, attrs) {
3080 var children = element.children();
3082 angular.forEach(children, function(node) {
3084 var tabId = node.id;
3086 foundationApi.subscribe(tabId, function(msg) {
3087 if(msg === 'activate' || msg === 'show' || msg === 'open') {
3088 activateTabs(tabId);
3092 if(tabs.length === 1) {
3093 var el = angular.element(node);
3094 el.addClass('is-active');
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');
3118 // imports all components and dependencies under a single namespace
3120 angular.module('foundation', [
3122 'foundation.mediaquery',
3123 'foundation.accordion',
3124 'foundation.actionsheet',
3125 'foundation.common',
3126 'foundation.iconic',
3127 'foundation.interchange',
3129 'foundation.notification',
3130 'foundation.offcanvas',