diff --git a/Gruntfile.js b/Gruntfile.js
index 8f13608..f1cf03e 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -1,9 +1,14 @@
'use strict';
module.exports = function(grunt) {
+ var npmTasks = [
+ 'grunt-contrib-uglify',
+ 'grunt-karma',
+ 'grunt-ng-annotate'
+ ];
+
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
-
karma: {
unit: {
options: {
@@ -27,7 +32,23 @@ module.exports = function(grunt) {
singleRun: true
}
},
-
+ ngAnnotate: {
+ options: {
+ singleQuotes: true
+ },
+ angularCss: {
+ files: {
+ 'angular-css.js': [
+ 'src/prefix.js',
+ 'src/$css-provider.js',
+ 'src/$cssLinks-filter.js',
+ 'src/angularCSS-module.js',
+ 'src/angularHack.js',
+ 'src/suffix.js'
+ ]
+ }
+ }
+ },
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= pkg.version %> | Copyright (c) <%= grunt.template.today("yyyy") %> DOOR3, Alex Castillo | MIT License */'
@@ -40,12 +61,14 @@ module.exports = function(grunt) {
}
});
- grunt.loadNpmTasks('grunt-contrib-uglify');
- grunt.loadNpmTasks('grunt-karma');
+ npmTasks.forEach(function (task) {
+ grunt.loadNpmTasks(task);
+ });
grunt.registerTask('test', ['karma']);
grunt.registerTask('default', [
+ 'ngAnnotate',
'test',
'uglify'
]);
diff --git a/angular-css.js b/angular-css.js
index 26f4f61..57692ee 100644
--- a/angular-css.js
+++ b/angular-css.js
@@ -10,33 +10,19 @@
(function (angular) {
- /**
- * AngularCSS Module
- * Contains: config, constant, provider and run
- **/
- var angularCSS = angular.module('door3.css', []);
-
- // Config
- angularCSS.config(['$logProvider', function ($logProvider) {
- // Turn off/on in order to see console logs during dev mode
- $logProvider.debugEnabled(false);
- }]);
-
- // Provider
- angularCSS.provider('$css', [function $cssProvider() {
-
- // Defaults - default options that can be overridden from application config
- var defaults = this.defaults = {
- element: 'link',
- rel: 'stylesheet',
- type: 'text/css',
- container: 'head',
- method: 'append',
- weight: 0
- };
+function $cssProvider() {
+
+ // Defaults - default options that can be overridden from application config
+ var defaults = this.defaults = {
+ element: 'link',
+ rel: 'stylesheet',
+ type: 'text/css',
+ container: 'head',
+ method: 'append',
+ weight: 0
+ };
- this.$get = ['$rootScope','$injector','$q','$window','$timeout','$compile','$http','$filter','$log',
- function $get($rootScope, $injector, $q, $window, $timeout, $compile, $http, $filter, $log) {
+ this.$get = /** @ngInject */ ['$rootScope', '$injector', '$q', '$window', '$timeout', '$compile', '$http', '$filter', '$log', function $get($rootScope, $injector, $q, $window, $timeout, $compile, $http, $filter, $log) {
var $css = {};
@@ -105,7 +91,7 @@
stylesheet.media = options.breakpoints[stylesheet.breakpoint];
}
delete stylesheet.breakpoints;
- }
+ }
}
/**
@@ -199,7 +185,7 @@
// Media query object
mediaQuery[stylesheet.href] = $window.matchMedia(stylesheet.media);
// Media Query Listener function
- mediaQueryListener[stylesheet.href] = function(mediaQuery) {
+ mediaQueryListener[stylesheet.href] = function (mediaQuery) {
// Trigger digest
$timeout(function () {
if (mediaQuery.matches) {
@@ -246,11 +232,11 @@
}
return !!(
// Check for media query setting
- stylesheet.media
+ stylesheet.media
// Check for media queries to be ignored
- && (mediaQueriesToIgnore.indexOf(stylesheet.media) === -1)
+ && (mediaQueriesToIgnore.indexOf(stylesheet.media) === -1)
// Check for matchMedia support
- && $window.matchMedia
+ && $window.matchMedia
);
}
@@ -349,12 +335,12 @@
if (angular.isDefined(state.css)) {
// For multiple stylesheets
if (angular.isArray(state.css)) {
- angular.forEach(state.css, function (itemCss) {
- if (angular.isFunction(itemCss)) {
- dynamicPaths.push(parse(itemCss));
- }
- result.push(parse(itemCss));
- });
+ angular.forEach(state.css, function (itemCss) {
+ if (angular.isFunction(itemCss)) {
+ dynamicPaths.push(parse(itemCss));
+ }
+ result.push(parse(itemCss));
+ });
// For single stylesheets
} else {
if (angular.isFunction(state.css)) {
@@ -413,7 +399,7 @@
stylesheets = [stylesheets];
}
var stylesheetLoadPromises = [];
- angular.forEach(stylesheets, function(stylesheet, key) {
+ angular.forEach(stylesheets, function (stylesheet, key) {
stylesheet = stylesheets[key] = parse(stylesheet);
stylesheetLoadPromises.push(
// Preload via ajax request
@@ -432,7 +418,7 @@
/**
* Bind: binds css in scope with own scope create/destroy events
**/
- $css.bind = function (css, $scope) {
+ $css.bind = function (css, $scope) {
if (!css || !$scope) {
return $log.error('No scope or stylesheets provided');
}
@@ -451,7 +437,7 @@
$css.remove(result);
$log.debug('$css.bind(): Removed', result);
});
- };
+ };
/**
* Add: adds stylesheets to scope
@@ -463,10 +449,10 @@
if (!angular.isArray(stylesheets)) {
stylesheets = [stylesheets];
}
- angular.forEach(stylesheets, function(stylesheet) {
+ angular.forEach(stylesheets, function (stylesheet) {
stylesheet = parse(stylesheet);
// Avoid adding duplicate stylesheets
- if (stylesheet.href && !$filter('filter')($rootScope.stylesheets, { href: stylesheet.href }).length) {
+ if (stylesheet.href && !$filter('filter')($rootScope.stylesheets, {href: stylesheet.href}).length) {
// Bust Cache feature
bustCache(stylesheet)
// Media Query add support check
@@ -497,7 +483,7 @@
stylesheets = $filter('filter')(stylesheets, function (stylesheet) {
return !stylesheet.persist;
});
- angular.forEach(stylesheets, function(stylesheet) {
+ angular.forEach(stylesheets, function (stylesheet) {
stylesheet = parse(stylesheet);
// Get index of current item to be removed based on href
var index = $rootScope.stylesheets.indexOf($filter('filter')($rootScope.stylesheets, {
@@ -533,81 +519,89 @@
}];
- }]);
+}
- /**
- * Links filter - renders the stylesheets array in html format
- **/
- angularCSS.filter('$cssLinks', function () {
- return function (stylesheets) {
- if (!stylesheets || !angular.isArray(stylesheets)) {
- return stylesheets;
- }
- var result = '';
- angular.forEach(stylesheets, function (stylesheet) {
- result += '\n\n';
- });
- return result;
+/**
+ * Links filter - renders the stylesheets array in html format
+ **/
+function $cssLinksFilter() {
+ return function $cssLinks(stylesheets) {
+ if (!stylesheets || !angular.isArray(stylesheets)) {
+ return stylesheets;
}
- });
+ var result = '';
+ angular.forEach(stylesheets, function (stylesheet) {
+ result += '\n\n';
+ });
+ return result;
+ };
+}
+angular
+ .module('door3.css', [])
+ .config(['$logProvider', function ($logProvider) {
+ // Turn off/on in order to see console logs during dev mode
+ $logProvider.debugEnabled(false);
+ }])
/**
* Run - auto instantiate the $css provider by injecting it in the run phase of this module
**/
- angularCSS.run(['$css', function ($css) { } ]);
+ .run(['$css', function ($css) { }])
+ .provider('$css', [$cssProvider])
+ .filter('$cssLinks', $cssLinksFilter);
- /**
- * AngularJS hack - This way we can get and decorate all custom directives
- * in order to broadcast a custom $directiveAdd event
- **/
- var $directives = [];
- var originalModule = angular.module;
- angular.module = function () {
- var module = originalModule.apply(this, arguments);
- var originalDirective = module.directive;
- module.directive = function(directiveName, directiveFactory) {
- var originalDirectiveFactory = angular.isFunction(directiveFactory) ?
+/**
+ * AngularJS hack - This way we can get and decorate all custom directives
+ * in order to broadcast a custom $directiveAdd event
+ **/
+var $directives = [];
+var originalModule = angular.module;
+angular.module = function () {
+ var module = originalModule.apply(this, arguments);
+ var originalDirective = module.directive;
+ module.directive = function (directiveName, directiveFactory) {
+ var originalDirectiveFactory = angular.isFunction(directiveFactory) ?
directiveFactory : directiveFactory[directiveFactory.length - 1];
- try {
- var directive = angular.copy(originalDirectiveFactory)();
- directive.directiveName = directiveName;
- if (directive.hasOwnProperty('css')) {
- $directives.push(directive);
- }
- } catch (e) { }
- return originalDirective.apply(this, arguments);
- };
- module.config(['$provide','$injector', function ($provide, $injector) {
- angular.forEach($directives, function ($directive) {
- var dirProvider = $directive.directiveName + 'Directive';
- if ($injector.has(dirProvider)) {
- $provide.decorator(dirProvider, ['$delegate', '$rootScope', '$timeout', function ($delegate, $rootScope, $timeout) {
- var directive = $delegate[0];
- var compile = directive.compile;
- if (directive.css) {
- $directive.css = directive.css;
- }
- directive.compile = function() {
- var link = compile ? compile.apply(this, arguments): false;
- return function(scope) {
- var linkArgs = arguments;
- $timeout(function () {
- if (link) {
- link.apply(this, linkArgs);
- }
- });
- $rootScope.$broadcast('$directiveAdd', directive, scope);
- };
- };
- return $delegate;
- }]);
- }
- });
- }]);
- return module;
+ try {
+ var directive = angular.copy(originalDirectiveFactory)();
+ directive.directiveName = directiveName;
+ if (directive.hasOwnProperty('css')) {
+ $directives.push(directive);
+ }
+ } catch (e) {
+ }
+ return originalDirective.apply(this, arguments);
};
- /* End of hack */
+ module.config(['$provide', '$injector', function ($provide, $injector) {
+ angular.forEach($directives, function ($directive) {
+ var dirProvider = $directive.directiveName + 'Directive';
+ if ($injector.has(dirProvider)) {
+ $provide.decorator(dirProvider, ['$delegate', '$rootScope', '$timeout', function ($delegate, $rootScope, $timeout) {
+ var directive = $delegate[0];
+ var compile = directive.compile;
+ if (directive.css) {
+ $directive.css = directive.css;
+ }
+ directive.compile = function () {
+ var link = compile ? compile.apply(this, arguments) : false;
+ return function (scope) {
+ var linkArgs = arguments;
+ $timeout(function () {
+ if (link) {
+ link.apply(this, linkArgs);
+ }
+ });
+ $rootScope.$broadcast('$directiveAdd', directive, scope);
+ };
+ };
+ return $delegate;
+ }]);
+ }
+ });
+ }]);
+ return module;
+};
})(angular);
diff --git a/angular-css.min.js b/angular-css.min.js
index 07f2f84..e7ef6b0 100644
--- a/angular-css.min.js
+++ b/angular-css.min.js
@@ -1 +1 @@
-/*! angular-css 1.0.7 | Copyright (c) 2015 DOOR3, Alex Castillo | MIT License */"use strict";!function(a){var b=a.module("door3.css",[]);b.config(["$logProvider",function(a){a.debugEnabled(!1)}]),b.provider("$css",[function(){var b=this.defaults={element:"link",rel:"stylesheet",type:"text/css",container:"head",method:"append",weight:0};this.$get=["$rootScope","$injector","$q","$window","$timeout","$compile","$http","$filter","$log",function(d,e,f,g,h,i,j,k,l){function m(a,b,c){c&&b.hasOwnProperty("css")&&w.bind([q(b.css)],c)}function n(a,b,c){c&&(w.remove(w.getFromRoute(c).concat(D)),D.length=0),b&&w.add(w.getFromRoute(b))}function o(a,b,c,d){d&&(w.remove(w.getFromState(d).concat(D)),D.length=0),b&&w.add(w.getFromState(b))}function p(b){a.isDefined(B.breakpoints)&&(b.breakpoint in B.breakpoints&&(b.media=B.breakpoints[b.breakpoint]),delete b.breakpoints)}function q(b){return b?(a.isFunction(b)&&(b=a.copy(e.invoke(b))),a.isString(b)&&(b=a.extend({href:b},B)),a.isArray(b)&&a.isString(b[0])&&a.forEach(b,function(c){b=a.extend({href:c},B)}),a.isObject(b)&&!a.isArray(b)&&(b=a.extend(b,B)),a.isArray(b)&&a.isObject(b[0])&&a.forEach(b,function(c){b=a.extend(c,B)}),p(b),b):void 0}function r(a){if(!a)return l.error("No stylesheets provided");var b="?cache=";-1===a.href.indexOf(b)&&(a.href=a.href+(a.bustCache?b+(new Date).getTime():""))}function s(a,b){return a&&b?k("filter")(a,function(a){return a[b]}):l.error("filterBy: missing array or property")}function t(a){return a?(y[a.href]=g.matchMedia(a.media),z[a.href]=function(b){h(function(){if(b.matches)d.stylesheets.push(a);else{var c=d.stylesheets.indexOf(k("filter")(d.stylesheets,{href:a.href})[0]);-1!==c&&d.stylesheets.splice(c,1)}})},y[a.href].addListener(z[a.href]),void z[a.href](y[a.href])):l.error("No stylesheet provided")}function u(b){return b?void(d&&a.isDefined(y)&&y[b.href]&&a.isDefined(z)&&y[b.href].removeListener(z[b.href])):l.error("No stylesheet provided")}function v(a){return a?!(!a.media||-1!==A.indexOf(a.media)||!g.matchMedia):l.error("No stylesheet provided")}var w={},x='',y={},z={},A=["print"],B=a.extend({},b),C=a.element(document.querySelector?document.querySelector(B.container):document.getElementsByTagName(B.container)[0]),D=[];return a.forEach(c,function(a,b){a.hasOwnProperty("css")&&(c[b]=q(a.css))}),d.stylesheets=[],C[B.method](i(x)(d)),d.$on("$directiveAdd",m),d.$on("$routeChangeSuccess",n),d.$on("$stateChangeSuccess",o),w.getFromRoute=function(b){if(!b)return l.error("Get From Route: No route provided");var c=null,d=[];return b.$$route&&b.$$route.css?c=b.$$route.css:b.css&&(c=b.css),c&&(a.isArray(c)?a.forEach(c,function(b){a.isFunction(b)&&D.push(q(b)),d.push(q(b))}):(a.isFunction(c)&&D.push(q(c)),d.push(q(c)))),d},w.getFromRoutes=function(b){if(!b)return l.error("Get From Routes: No routes provided");var c=[];return a.forEach(b,function(a){var b=w.getFromRoute(a);b.length&&c.push(b[0])}),c},w.getFromState=function(b){if(!b)return l.error("Get From State: No state provided");var c=[];return a.isDefined(b.views)&&a.forEach(b.views,function(b){b.css&&(a.isFunction(b.css)&&D.push(q(b.css)),c.push(q(b.css)))}),a.isDefined(b.children)&&a.forEach(b.children,function(b){b.css&&(a.isFunction(b.css)&&D.push(q(b.css)),c.push(q(b.css))),a.isDefined(b.children)&&a.forEach(b.children,function(b){b.css&&(a.isFunction(b.css)&&D.push(q(b.css)),c.push(q(b.css)))})}),a.isDefined(b.css)&&(a.isArray(b.css)?a.forEach(b.css,function(b){a.isFunction(b)&&D.push(q(b)),c.push(q(b))}):(a.isFunction(b.css)&&D.push(q(b.css)),c.push(q(b.css)))),c},w.getFromStates=function(b){if(!b)return l.error("Get From States: No states provided");var c=[];return a.forEach(b,function(b){var d=w.getFromState(b);a.isArray(d)?a.forEach(d,function(a){c.push(a)}):c.push(d)}),c},w.preload=function(b,d){b||(b=[],c.length&&Array.prototype.push.apply(b,c),e.has("$route")&&Array.prototype.push.apply(b,w.getFromRoutes(e.get("$route").routes)),e.has("$state")&&Array.prototype.push.apply(b,w.getFromStates(e.get("$state").get())),b=s(b,"preload")),a.isArray(b)||(b=[b]);var g=[];a.forEach(b,function(a,c){a=b[c]=q(a),g.push(j.get(a.href).error(function(){l.error("AngularCSS: Incorrect path for "+a.href)}))}),a.isFunction(d)&&f.all(g).then(function(){d(b)})},w.bind=function(b,c){if(!b||!c)return l.error("No scope or stylesheets provided");var d=[];a.isArray(b)?a.forEach(b,function(a){d.push(q(a))}):d.push(q(b)),w.add(d),l.debug("$css.bind(): Added",d),c.$on("$destroy",function(){w.remove(d),l.debug("$css.bind(): Removed",d)})},w.add=function(b){return b?(a.isArray(b)||(b=[b]),a.forEach(b,function(a){a=q(a),a.href&&!k("filter")(d.stylesheets,{href:a.href}).length&&(r(a),v(a)?t(a):d.stylesheets.push(a),l.debug("$css.add(): "+a.href))}),void d.$broadcast("$cssAdd",b,d.stylesheets)):l.error("No stylesheets provided")},w.remove=function(b){return b?(a.isArray(b)||(b=[b]),b=k("filter")(b,function(a){return!a.persist}),a.forEach(b,function(a){a=q(a);var b=d.stylesheets.indexOf(k("filter")(d.stylesheets,{href:a.href})[0]);-1!==b&&d.stylesheets.splice(b,1),u(a),l.debug("$css.remove(): "+a.href)}),void d.$broadcast("$cssRemove",b,d.stylesheets)):l.error("No stylesheets provided")},w.removeAll=function(){d&&d.hasOwnProperty("stylesheets")&&(d.stylesheets.length=0),l.debug("all stylesheets removed")},w.preload(),w}]}]),b.filter("$cssLinks",function(){return function(b){if(!b||!a.isArray(b))return b;var c="";return a.forEach(b,function(a){c+='\n\n"}),c}}),b.run(["$css",function(){}]);var c=[],d=a.module;a.module=function(){var b=d.apply(this,arguments),e=b.directive;return b.directive=function(b,d){var f=a.isFunction(d)?d:d[d.length-1];try{var g=a.copy(f)();g.directiveName=b,g.hasOwnProperty("css")&&c.push(g)}catch(h){}return e.apply(this,arguments)},b.config(["$provide","$injector",function(b,d){a.forEach(c,function(a){var c=a.directiveName+"Directive";d.has(c)&&b.decorator(c,["$delegate","$rootScope","$timeout",function(b,c,d){var e=b[0],f=e.compile;return e.css&&(a.css=e.css),e.compile=function(){var a=f?f.apply(this,arguments):!1;return function(b){var f=arguments;d(function(){a&&a.apply(this,f)}),c.$broadcast("$directiveAdd",e,b)}},b}])})}]),b}}(angular);
\ No newline at end of file
+/*! angular-css 1.0.7 | Copyright (c) 2015 DOOR3, Alex Castillo | MIT License */"use strict";!function(a){function b(){var b=this.defaults={element:"link",rel:"stylesheet",type:"text/css",container:"head",method:"append",weight:0};this.$get=["$rootScope","$injector","$q","$window","$timeout","$compile","$http","$filter","$log",function(c,e,f,g,h,i,j,k,l){function m(a,b,c){c&&b.hasOwnProperty("css")&&w.bind([q(b.css)],c)}function n(a,b,c){c&&(w.remove(w.getFromRoute(c).concat(D)),D.length=0),b&&w.add(w.getFromRoute(b))}function o(a,b,c,d){d&&(w.remove(w.getFromState(d).concat(D)),D.length=0),b&&w.add(w.getFromState(b))}function p(b){a.isDefined(B.breakpoints)&&(b.breakpoint in B.breakpoints&&(b.media=B.breakpoints[b.breakpoint]),delete b.breakpoints)}function q(b){return b?(a.isFunction(b)&&(b=a.copy(e.invoke(b))),a.isString(b)&&(b=a.extend({href:b},B)),a.isArray(b)&&a.isString(b[0])&&a.forEach(b,function(c){b=a.extend({href:c},B)}),a.isObject(b)&&!a.isArray(b)&&(b=a.extend(b,B)),a.isArray(b)&&a.isObject(b[0])&&a.forEach(b,function(c){b=a.extend(c,B)}),p(b),b):void 0}function r(a){if(!a)return l.error("No stylesheets provided");var b="?cache=";-1===a.href.indexOf(b)&&(a.href=a.href+(a.bustCache?b+(new Date).getTime():""))}function s(a,b){return a&&b?k("filter")(a,function(a){return a[b]}):l.error("filterBy: missing array or property")}function t(a){return a?(y[a.href]=g.matchMedia(a.media),z[a.href]=function(b){h(function(){if(b.matches)c.stylesheets.push(a);else{var d=c.stylesheets.indexOf(k("filter")(c.stylesheets,{href:a.href})[0]);-1!==d&&c.stylesheets.splice(d,1)}})},y[a.href].addListener(z[a.href]),void z[a.href](y[a.href])):l.error("No stylesheet provided")}function u(b){return b?void(c&&a.isDefined(y)&&y[b.href]&&a.isDefined(z)&&y[b.href].removeListener(z[b.href])):l.error("No stylesheet provided")}function v(a){return a?!(!a.media||-1!==A.indexOf(a.media)||!g.matchMedia):l.error("No stylesheet provided")}var w={},x='',y={},z={},A=["print"],B=a.extend({},b),C=a.element(document.querySelector?document.querySelector(B.container):document.getElementsByTagName(B.container)[0]),D=[];return a.forEach(d,function(a,b){a.hasOwnProperty("css")&&(d[b]=q(a.css))}),c.stylesheets=[],C[B.method](i(x)(c)),c.$on("$directiveAdd",m),c.$on("$routeChangeSuccess",n),c.$on("$stateChangeSuccess",o),w.getFromRoute=function(b){if(!b)return l.error("Get From Route: No route provided");var c=null,d=[];return b.$$route&&b.$$route.css?c=b.$$route.css:b.css&&(c=b.css),c&&(a.isArray(c)?a.forEach(c,function(b){a.isFunction(b)&&D.push(q(b)),d.push(q(b))}):(a.isFunction(c)&&D.push(q(c)),d.push(q(c)))),d},w.getFromRoutes=function(b){if(!b)return l.error("Get From Routes: No routes provided");var c=[];return a.forEach(b,function(a){var b=w.getFromRoute(a);b.length&&c.push(b[0])}),c},w.getFromState=function(b){if(!b)return l.error("Get From State: No state provided");var c=[];return a.isDefined(b.views)&&a.forEach(b.views,function(b){b.css&&(a.isFunction(b.css)&&D.push(q(b.css)),c.push(q(b.css)))}),a.isDefined(b.children)&&a.forEach(b.children,function(b){b.css&&(a.isFunction(b.css)&&D.push(q(b.css)),c.push(q(b.css))),a.isDefined(b.children)&&a.forEach(b.children,function(b){b.css&&(a.isFunction(b.css)&&D.push(q(b.css)),c.push(q(b.css)))})}),a.isDefined(b.css)&&(a.isArray(b.css)?a.forEach(b.css,function(b){a.isFunction(b)&&D.push(q(b)),c.push(q(b))}):(a.isFunction(b.css)&&D.push(q(b.css)),c.push(q(b.css)))),c},w.getFromStates=function(b){if(!b)return l.error("Get From States: No states provided");var c=[];return a.forEach(b,function(b){var d=w.getFromState(b);a.isArray(d)?a.forEach(d,function(a){c.push(a)}):c.push(d)}),c},w.preload=function(b,c){b||(b=[],d.length&&Array.prototype.push.apply(b,d),e.has("$route")&&Array.prototype.push.apply(b,w.getFromRoutes(e.get("$route").routes)),e.has("$state")&&Array.prototype.push.apply(b,w.getFromStates(e.get("$state").get())),b=s(b,"preload")),a.isArray(b)||(b=[b]);var g=[];a.forEach(b,function(a,c){a=b[c]=q(a),g.push(j.get(a.href).error(function(){l.error("AngularCSS: Incorrect path for "+a.href)}))}),a.isFunction(c)&&f.all(g).then(function(){c(b)})},w.bind=function(b,c){if(!b||!c)return l.error("No scope or stylesheets provided");var d=[];a.isArray(b)?a.forEach(b,function(a){d.push(q(a))}):d.push(q(b)),w.add(d),l.debug("$css.bind(): Added",d),c.$on("$destroy",function(){w.remove(d),l.debug("$css.bind(): Removed",d)})},w.add=function(b){return b?(a.isArray(b)||(b=[b]),a.forEach(b,function(a){a=q(a),a.href&&!k("filter")(c.stylesheets,{href:a.href}).length&&(r(a),v(a)?t(a):c.stylesheets.push(a),l.debug("$css.add(): "+a.href))}),void c.$broadcast("$cssAdd",b,c.stylesheets)):l.error("No stylesheets provided")},w.remove=function(b){return b?(a.isArray(b)||(b=[b]),b=k("filter")(b,function(a){return!a.persist}),a.forEach(b,function(a){a=q(a);var b=c.stylesheets.indexOf(k("filter")(c.stylesheets,{href:a.href})[0]);-1!==b&&c.stylesheets.splice(b,1),u(a),l.debug("$css.remove(): "+a.href)}),void c.$broadcast("$cssRemove",b,c.stylesheets)):l.error("No stylesheets provided")},w.removeAll=function(){c&&c.hasOwnProperty("stylesheets")&&(c.stylesheets.length=0),l.debug("all stylesheets removed")},w.preload(),w}]}function c(){return function(b){if(!b||!a.isArray(b))return b;var c="";return a.forEach(b,function(a){c+='\n\n"}),c}}a.module("door3.css",[]).config(["$logProvider",function(a){a.debugEnabled(!1)}]).run(["$css",function(){}]).provider("$css",[b]).filter("$cssLinks",c);var d=[],e=a.module;a.module=function(){var b=e.apply(this,arguments),c=b.directive;return b.directive=function(b,e){var f=a.isFunction(e)?e:e[e.length-1];try{var g=a.copy(f)();g.directiveName=b,g.hasOwnProperty("css")&&d.push(g)}catch(h){}return c.apply(this,arguments)},b.config(["$provide","$injector",function(b,c){a.forEach(d,function(a){var d=a.directiveName+"Directive";c.has(d)&&b.decorator(d,["$delegate","$rootScope","$timeout",function(b,c,d){var e=b[0],f=e.compile;return e.css&&(a.css=e.css),e.compile=function(){var a=f?f.apply(this,arguments):!1;return function(b){var f=arguments;d(function(){a&&a.apply(this,f)}),c.$broadcast("$directiveAdd",e,b)}},b}])})}]),b}}(angular);
\ No newline at end of file
diff --git a/bower.json b/bower.json
index 0bb696a..b472bf1 100644
--- a/bower.json
+++ b/bower.json
@@ -9,9 +9,9 @@
"description": "CSS on-demand for AngularJS",
"keywords": [
"angularjs",
+ "css",
"ng-route",
- "ui-router",
- "css"
+ "ui-router"
],
"dependencies": {
"angular": ">= 1.3.0"
@@ -33,4 +33,4 @@
"test",
"tests"
]
-}
\ No newline at end of file
+}
diff --git a/package.json b/package.json
index d91c523..613bd88 100644
--- a/package.json
+++ b/package.json
@@ -51,10 +51,18 @@
"test": "grunt test"
},
"devDependencies": {
+ "angular": "^1.3.13",
+ "angular-mocks": "^1.3.13",
+ "chai": "^2.0.0",
"grunt": "~0.4.1",
+ "grunt-cli": "~0.1.11",
"grunt-contrib-uglify": "~0.2.4",
"grunt-karma": "~0.7.1",
+ "grunt-ng-annotate": "^0.10.0",
+ "karma-chrome-launcher": "0.1.7",
+ "karma-firefox-launcher": "0.1.4",
"karma-mocha": "~0.1.0",
- "grunt-cli": "~0.1.11"
+ "karma-phantomjs-launcher": "0.1.4",
+ "ng-annotate": "^0.15.4"
}
}
diff --git a/src/$css-provider.js b/src/$css-provider.js
new file mode 100644
index 0000000..0faf59d
--- /dev/null
+++ b/src/$css-provider.js
@@ -0,0 +1,510 @@
+function $cssProvider() {
+
+ // Defaults - default options that can be overridden from application config
+ var defaults = this.defaults = {
+ element: 'link',
+ rel: 'stylesheet',
+ type: 'text/css',
+ container: 'head',
+ method: 'append',
+ weight: 0
+ };
+
+ this.$get = /** @ngInject */ function $get($rootScope, $injector, $q, $window, $timeout, $compile, $http, $filter, $log) {
+
+ var $css = {};
+
+ var template = '';
+
+ // Variables - default options that can be overridden from application config
+ var mediaQuery = {}, mediaQueryListener = {}, mediaQueriesToIgnore = ['print'], options = angular.extend({}, defaults),
+ container = angular.element(document.querySelector ? document.querySelector(options.container) : document.getElementsByTagName(options.container)[0]),
+ dynamicPaths = [];
+
+ // Parse all directives
+ angular.forEach($directives, function (directive, key) {
+ if (directive.hasOwnProperty('css')) {
+ $directives[key] = parse(directive.css);
+ }
+ });
+
+ /**
+ * Listen for directive add event in order to add stylesheet(s)
+ **/
+ function $directiveAddEventListener(event, directive, scope) {
+ // Binds directive's css
+ if (scope && directive.hasOwnProperty('css')) {
+ $css.bind([parse(directive.css)], scope);
+ }
+ }
+
+ /**
+ * Listen for route change event and add/remove stylesheet(s)
+ **/
+ function $routeEventListener(event, current, prev) {
+ // Removes previously added css rules
+ if (prev) {
+ $css.remove($css.getFromRoute(prev).concat(dynamicPaths));
+ // Reset dynamic paths array
+ dynamicPaths.length = 0;
+ }
+ // Adds current css rules
+ if (current) {
+ $css.add($css.getFromRoute(current));
+ }
+ }
+
+ /**
+ * Listen for state change event and add/remove stylesheet(s)
+ **/
+ function $stateEventListener(event, current, params, prev) {
+ // Removes previously added css rules
+ if (prev) {
+ $css.remove($css.getFromState(prev).concat(dynamicPaths));
+ // Reset dynamic paths array
+ dynamicPaths.length = 0;
+ }
+ // Adds current css rules
+ if (current) {
+ $css.add($css.getFromState(current));
+ }
+ }
+
+ /**
+ * Map breakpoitns defined in defaults to stylesheet media attribute
+ **/
+ function mapBreakpointToMedia(stylesheet) {
+ if (angular.isDefined(options.breakpoints)) {
+ if (stylesheet.breakpoint in options.breakpoints) {
+ stylesheet.media = options.breakpoints[stylesheet.breakpoint];
+ }
+ delete stylesheet.breakpoints;
+ }
+ }
+
+ /**
+ * Parse: returns array with full all object based on defaults
+ **/
+ function parse(obj) {
+ if (!obj) {
+ return;
+ }
+ // Function syntax
+ if (angular.isFunction(obj)) {
+ obj = angular.copy($injector.invoke(obj));
+ }
+ // String syntax
+ if (angular.isString(obj)) {
+ obj = angular.extend({
+ href: obj
+ }, options);
+ }
+ // Array of strings syntax
+ if (angular.isArray(obj) && angular.isString(obj[0])) {
+ angular.forEach(obj, function (item) {
+ obj = angular.extend({
+ href: item
+ }, options);
+ });
+ }
+ // Object syntax
+ if (angular.isObject(obj) && !angular.isArray(obj)) {
+ obj = angular.extend(obj, options);
+ }
+ // Array of objects syntax
+ if (angular.isArray(obj) && angular.isObject(obj[0])) {
+ angular.forEach(obj, function (item) {
+ obj = angular.extend(item, options);
+ });
+ }
+ // Map breakpoint to media attribute
+ mapBreakpointToMedia(obj);
+ return obj;
+ }
+
+ // Add stylesheets to scope
+ $rootScope.stylesheets = [];
+
+ // Adds compiled link tags to container element
+ container[options.method]($compile(template)($rootScope));
+
+ // Directive event listener (emulated internally)
+ $rootScope.$on('$directiveAdd', $directiveAddEventListener);
+
+ // Routes event listener ($route required)
+ $rootScope.$on('$routeChangeSuccess', $routeEventListener);
+
+ // States event listener ($state required)
+ $rootScope.$on('$stateChangeSuccess', $stateEventListener);
+
+ /**
+ * Bust Cache
+ **/
+ function bustCache(stylesheet) {
+ if (!stylesheet) {
+ return $log.error('No stylesheets provided');
+ }
+ var queryString = '?cache=';
+ // Append query string for bust cache only once
+ if (stylesheet.href.indexOf(queryString) === -1) {
+ stylesheet.href = stylesheet.href + (stylesheet.bustCache ? queryString + (new Date().getTime()) : '');
+ }
+ }
+
+ /**
+ * Filter By: returns an array of routes based on a property option
+ **/
+ function filterBy(array, prop) {
+ if (!array || !prop) {
+ return $log.error('filterBy: missing array or property');
+ }
+ return $filter('filter')(array, function (item) {
+ return item[prop];
+ });
+ }
+
+ /**
+ * Add Media Query
+ **/
+ function addViaMediaQuery(stylesheet) {
+ if (!stylesheet) {
+ return $log.error('No stylesheet provided');
+ }
+ // Media query object
+ mediaQuery[stylesheet.href] = $window.matchMedia(stylesheet.media);
+ // Media Query Listener function
+ mediaQueryListener[stylesheet.href] = function (mediaQuery) {
+ // Trigger digest
+ $timeout(function () {
+ if (mediaQuery.matches) {
+ // Add stylesheet
+ $rootScope.stylesheets.push(stylesheet);
+ } else {
+ var index = $rootScope.stylesheets.indexOf($filter('filter')($rootScope.stylesheets, {
+ href: stylesheet.href
+ })[0]);
+ // Remove stylesheet
+ if (index !== -1) {
+ $rootScope.stylesheets.splice(index, 1);
+ }
+ }
+ });
+ }
+ // Listen for media query changes
+ mediaQuery[stylesheet.href].addListener(mediaQueryListener[stylesheet.href]);
+ // Invoke first media query check
+ mediaQueryListener[stylesheet.href](mediaQuery[stylesheet.href]);
+ }
+
+ /**
+ * Remove Media Query
+ **/
+ function removeViaMediaQuery(stylesheet) {
+ if (!stylesheet) {
+ return $log.error('No stylesheet provided');
+ }
+ // Remove media query listener
+ if ($rootScope && angular.isDefined(mediaQuery)
+ && mediaQuery[stylesheet.href]
+ && angular.isDefined(mediaQueryListener)) {
+ mediaQuery[stylesheet.href].removeListener(mediaQueryListener[stylesheet.href]);
+ }
+ }
+
+ /**
+ * Is Media Query: checks for media settings, media queries to be ignore and match media support
+ **/
+ function isMediaQuery(stylesheet) {
+ if (!stylesheet) {
+ return $log.error('No stylesheet provided');
+ }
+ return !!(
+ // Check for media query setting
+ stylesheet.media
+ // Check for media queries to be ignored
+ && (mediaQueriesToIgnore.indexOf(stylesheet.media) === -1)
+ // Check for matchMedia support
+ && $window.matchMedia
+ );
+ }
+
+ /**
+ * Get From Route: returns array of css objects from single route
+ **/
+ $css.getFromRoute = function (route) {
+ if (!route) {
+ return $log.error('Get From Route: No route provided');
+ }
+ var css = null, result = [];
+ if (route.$$route && route.$$route.css) {
+ css = route.$$route.css;
+ }
+ else if (route.css) {
+ css = route.css;
+ }
+ // Adds route css rules to array
+ if (css) {
+ if (angular.isArray(css)) {
+ angular.forEach(css, function (cssItem) {
+ if (angular.isFunction(cssItem)) {
+ dynamicPaths.push(parse(cssItem));
+ }
+ result.push(parse(cssItem));
+ });
+ } else {
+ if (angular.isFunction(css)) {
+ dynamicPaths.push(parse(css));
+ }
+ result.push(parse(css));
+ }
+ }
+ return result;
+ };
+
+ /**
+ * Get From Routes: returns array of css objects from ng routes
+ **/
+ $css.getFromRoutes = function (routes) {
+ if (!routes) {
+ return $log.error('Get From Routes: No routes provided');
+ }
+ var result = [];
+ // Make array of all routes
+ angular.forEach(routes, function (route) {
+ var css = $css.getFromRoute(route);
+ if (css.length) {
+ result.push(css[0]);
+ }
+ });
+ return result;
+ };
+
+ /**
+ * Get From State: returns array of css objects from single state
+ **/
+ $css.getFromState = function (state) {
+ if (!state) {
+ return $log.error('Get From State: No state provided');
+ }
+ var result = [];
+ // State "views" notation
+ if (angular.isDefined(state.views)) {
+ angular.forEach(state.views, function (item) {
+ if (item.css) {
+ if (angular.isFunction(item.css)) {
+ dynamicPaths.push(parse(item.css));
+ }
+ result.push(parse(item.css));
+ }
+ });
+ }
+ // State "children" notation
+ if (angular.isDefined(state.children)) {
+ angular.forEach(state.children, function (child) {
+ if (child.css) {
+ if (angular.isFunction(child.css)) {
+ dynamicPaths.push(parse(child.css));
+ }
+ result.push(parse(child.css));
+ }
+ if (angular.isDefined(child.children)) {
+ angular.forEach(child.children, function (childChild) {
+ if (childChild.css) {
+ if (angular.isFunction(childChild.css)) {
+ dynamicPaths.push(parse(childChild.css));
+ }
+ result.push(parse(childChild.css));
+ }
+ });
+ }
+ });
+ }
+ // State default notation
+ if (angular.isDefined(state.css)) {
+ // For multiple stylesheets
+ if (angular.isArray(state.css)) {
+ angular.forEach(state.css, function (itemCss) {
+ if (angular.isFunction(itemCss)) {
+ dynamicPaths.push(parse(itemCss));
+ }
+ result.push(parse(itemCss));
+ });
+ // For single stylesheets
+ } else {
+ if (angular.isFunction(state.css)) {
+ dynamicPaths.push(parse(state.css));
+ }
+ result.push(parse(state.css));
+ }
+ }
+ return result;
+ };
+
+ /**
+ * Get From States: returns array of css objects from states
+ **/
+ $css.getFromStates = function (states) {
+ if (!states) {
+ return $log.error('Get From States: No states provided');
+ }
+ var result = [];
+ // Make array of all routes
+ angular.forEach(states, function (state) {
+ var css = $css.getFromState(state);
+ if (angular.isArray(css)) {
+ angular.forEach(css, function (cssItem) {
+ result.push(cssItem);
+ });
+ } else {
+ result.push(css);
+ }
+ });
+ return result;
+ };
+
+ /**
+ * Preload: preloads css via http request
+ **/
+ $css.preload = function (stylesheets, callback) {
+ // If no stylesheets provided, then preload all
+ if (!stylesheets) {
+ stylesheets = [];
+ // Add all stylesheets from custom directives to array
+ if ($directives.length) {
+ Array.prototype.push.apply(stylesheets, $directives);
+ }
+ // Add all stylesheets from ngRoute to array
+ if ($injector.has('$route')) {
+ Array.prototype.push.apply(stylesheets, $css.getFromRoutes($injector.get('$route').routes));
+ }
+ // Add all stylesheets from UI Router to array
+ if ($injector.has('$state')) {
+ Array.prototype.push.apply(stylesheets, $css.getFromStates($injector.get('$state').get()));
+ }
+ stylesheets = filterBy(stylesheets, 'preload');
+ }
+ if (!angular.isArray(stylesheets)) {
+ stylesheets = [stylesheets];
+ }
+ var stylesheetLoadPromises = [];
+ angular.forEach(stylesheets, function (stylesheet, key) {
+ stylesheet = stylesheets[key] = parse(stylesheet);
+ stylesheetLoadPromises.push(
+ // Preload via ajax request
+ $http.get(stylesheet.href).error(function (response) {
+ $log.error('AngularCSS: Incorrect path for ' + stylesheet.href);
+ })
+ );
+ });
+ if (angular.isFunction(callback)) {
+ $q.all(stylesheetLoadPromises).then(function () {
+ callback(stylesheets);
+ });
+ }
+ };
+
+ /**
+ * Bind: binds css in scope with own scope create/destroy events
+ **/
+ $css.bind = function (css, $scope) {
+ if (!css || !$scope) {
+ return $log.error('No scope or stylesheets provided');
+ }
+ var result = [];
+ // Adds route css rules to array
+ if (angular.isArray(css)) {
+ angular.forEach(css, function (cssItem) {
+ result.push(parse(cssItem));
+ });
+ } else {
+ result.push(parse(css));
+ }
+ $css.add(result);
+ $log.debug('$css.bind(): Added', result);
+ $scope.$on('$destroy', function () {
+ $css.remove(result);
+ $log.debug('$css.bind(): Removed', result);
+ });
+ };
+
+ /**
+ * Add: adds stylesheets to scope
+ **/
+ $css.add = function (stylesheets, callback) {
+ if (!stylesheets) {
+ return $log.error('No stylesheets provided');
+ }
+ if (!angular.isArray(stylesheets)) {
+ stylesheets = [stylesheets];
+ }
+ angular.forEach(stylesheets, function (stylesheet) {
+ stylesheet = parse(stylesheet);
+ // Avoid adding duplicate stylesheets
+ if (stylesheet.href && !$filter('filter')($rootScope.stylesheets, {href: stylesheet.href}).length) {
+ // Bust Cache feature
+ bustCache(stylesheet)
+ // Media Query add support check
+ if (isMediaQuery(stylesheet)) {
+ addViaMediaQuery(stylesheet);
+ }
+ else {
+ $rootScope.stylesheets.push(stylesheet);
+ }
+ $log.debug('$css.add(): ' + stylesheet.href);
+ }
+ });
+ // Broadcasts custom event for css add
+ $rootScope.$broadcast('$cssAdd', stylesheets, $rootScope.stylesheets);
+ };
+
+ /**
+ * Remove: removes stylesheets from scope
+ **/
+ $css.remove = function (stylesheets, callback) {
+ if (!stylesheets) {
+ return $log.error('No stylesheets provided');
+ }
+ if (!angular.isArray(stylesheets)) {
+ stylesheets = [stylesheets];
+ }
+ // Only proceed based on persist setting
+ stylesheets = $filter('filter')(stylesheets, function (stylesheet) {
+ return !stylesheet.persist;
+ });
+ angular.forEach(stylesheets, function (stylesheet) {
+ stylesheet = parse(stylesheet);
+ // Get index of current item to be removed based on href
+ var index = $rootScope.stylesheets.indexOf($filter('filter')($rootScope.stylesheets, {
+ href: stylesheet.href
+ })[0]);
+ // Remove stylesheet from scope (if found)
+ if (index !== -1) {
+ $rootScope.stylesheets.splice(index, 1);
+ }
+ // Remove stylesheet via media query
+ removeViaMediaQuery(stylesheet);
+ $log.debug('$css.remove(): ' + stylesheet.href);
+ });
+ // Broadcasts custom event for css remove
+ $rootScope.$broadcast('$cssRemove', stylesheets, $rootScope.stylesheets);
+ };
+
+ /**
+ * Remove All: removes all style tags from the DOM
+ **/
+ $css.removeAll = function () {
+ // Remove all stylesheets from scope
+ if ($rootScope && $rootScope.hasOwnProperty('stylesheets')) {
+ $rootScope.stylesheets.length = 0;
+ }
+ $log.debug('all stylesheets removed');
+ };
+
+ // Preload all stylesheets
+ $css.preload();
+
+ return $css;
+
+ };
+
+}
diff --git a/src/$cssLinks-filter.js b/src/$cssLinks-filter.js
new file mode 100644
index 0000000..9c2344a
--- /dev/null
+++ b/src/$cssLinks-filter.js
@@ -0,0 +1,17 @@
+/**
+ * Links filter - renders the stylesheets array in html format
+ **/
+function $cssLinksFilter() {
+ return function $cssLinks(stylesheets) {
+ if (!stylesheets || !angular.isArray(stylesheets)) {
+ return stylesheets;
+ }
+ var result = '';
+ angular.forEach(stylesheets, function (stylesheet) {
+ result += '\n\n';
+ });
+ return result;
+ };
+}
diff --git a/src/angularCSS-module.js b/src/angularCSS-module.js
new file mode 100644
index 0000000..9664317
--- /dev/null
+++ b/src/angularCSS-module.js
@@ -0,0 +1,12 @@
+angular
+ .module('door3.css', [])
+ .config(function ($logProvider) {
+ // Turn off/on in order to see console logs during dev mode
+ $logProvider.debugEnabled(false);
+ })
+ /**
+ * Run - auto instantiate the $css provider by injecting it in the run phase of this module
+ **/
+ .run(function ($css) { })
+ .provider('$css', [$cssProvider])
+ .filter('$cssLinks', $cssLinksFilter);
diff --git a/src/angularHack.js b/src/angularHack.js
new file mode 100644
index 0000000..f347597
--- /dev/null
+++ b/src/angularHack.js
@@ -0,0 +1,51 @@
+/**
+ * AngularJS hack - This way we can get and decorate all custom directives
+ * in order to broadcast a custom $directiveAdd event
+ **/
+var $directives = [];
+var originalModule = angular.module;
+angular.module = function () {
+ var module = originalModule.apply(this, arguments);
+ var originalDirective = module.directive;
+ module.directive = function (directiveName, directiveFactory) {
+ var originalDirectiveFactory = angular.isFunction(directiveFactory) ?
+ directiveFactory : directiveFactory[directiveFactory.length - 1];
+ try {
+ var directive = angular.copy(originalDirectiveFactory)();
+ directive.directiveName = directiveName;
+ if (directive.hasOwnProperty('css')) {
+ $directives.push(directive);
+ }
+ } catch (e) {
+ }
+ return originalDirective.apply(this, arguments);
+ };
+ module.config(['$provide', '$injector', function ($provide, $injector) {
+ angular.forEach($directives, function ($directive) {
+ var dirProvider = $directive.directiveName + 'Directive';
+ if ($injector.has(dirProvider)) {
+ $provide.decorator(dirProvider, ['$delegate', '$rootScope', '$timeout', function ($delegate, $rootScope, $timeout) {
+ var directive = $delegate[0];
+ var compile = directive.compile;
+ if (directive.css) {
+ $directive.css = directive.css;
+ }
+ directive.compile = function () {
+ var link = compile ? compile.apply(this, arguments) : false;
+ return function (scope) {
+ var linkArgs = arguments;
+ $timeout(function () {
+ if (link) {
+ link.apply(this, linkArgs);
+ }
+ });
+ $rootScope.$broadcast('$directiveAdd', directive, scope);
+ };
+ };
+ return $delegate;
+ }]);
+ }
+ });
+ }]);
+ return module;
+};
diff --git a/src/prefix.js b/src/prefix.js
new file mode 100644
index 0000000..ca6f262
--- /dev/null
+++ b/src/prefix.js
@@ -0,0 +1,11 @@
+/**
+ * AngularCSS - CSS on-demand for AngularJS
+ * @version v1.0.7
+ * @author DOOR3, Alex Castillo
+ * @link http://door3.github.io/angular-css
+ * @license MIT License, http://www.opensource.org/licenses/MIT
+ */
+
+'use strict';
+
+(function (angular) {
diff --git a/src/suffix.js b/src/suffix.js
new file mode 100644
index 0000000..d1bb767
--- /dev/null
+++ b/src/suffix.js
@@ -0,0 +1 @@
+})(angular);