Skip to content

Commit

Permalink
refactor: declare components as global variables inside a self-execut…
Browse files Browse the repository at this point in the history
…ing function

The service and the directive are now global variables. By this way, the components are more testable (specifically utils functions).
A self-executing function is added by Grunt to avoid name collisions.
  • Loading branch information
ncuillery committed May 1, 2014
1 parent dccc5e2 commit ab2ae1a
Show file tree
Hide file tree
Showing 4 changed files with 302 additions and 288 deletions.
7 changes: 4 additions & 3 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ module.exports = function (grunt) {
// Task configuration.
concat: {
options: {
banner: '<%= banner %>',
banner: '<%= banner %>\n(function (window, angular, undefined) {\n',
footer: '})(window, window.angular);\n',
stripBanners: true
},
dist: {
Expand Down Expand Up @@ -104,7 +105,7 @@ module.exports = function (grunt) {
flatten: true,
expand: true,
src: [
'src/angular-breadcrumb.js',
'dist/angular-breadcrumb.js',
'bower_components/angular/angular.js',
'bower_components/angular-ui-router/release/angular-ui-router.js',
'bower_components/angular-ui-bootstrap-bower/ui-bootstrap-tpls.js',
Expand Down Expand Up @@ -166,6 +167,6 @@ module.exports = function (grunt) {

grunt.registerTask('test', ['jshint', 'karma']);

grunt.registerTask('sample', ['copy:asset', 'copy:img', 'connect:livereload', 'open', 'watch']);
grunt.registerTask('sample', ['concat', 'copy:asset', 'copy:img', 'connect:livereload', 'open', 'watch']);

};
290 changes: 149 additions & 141 deletions dist/angular-breadcrumb.js
Original file line number Diff line number Diff line change
@@ -1,165 +1,173 @@
/*! angular-breadcrumb - v0.1.0 - 2014-04-28
/*! angular-breadcrumb - v0.1.0 - 2014-05-01
* https://github.com/ncuillery/angular-breadcrumb
* Copyright (c) 2014 Nicolas Cuillery; Licensed MIT */
angular.module('ncy-angular-breadcrumb', ['ui.router.state'])
.provider('$breadcrumb', function() {

var $$options = {
prefixStateName: null,
template: 'bootstrap3',
templateUrl: null
};
(function (window, angular, undefined) {
function $Breadcrumb() {

var $$options = {
prefixStateName: null,
template: 'bootstrap3',
templateUrl: null
};

this.setOptions = function(options) {
angular.extend($$options, options);
};

this.$get = ['$state', function($state) {

this.setOptions = function(options) {
angular.extend($$options, options);
// Check if a property in state's data is inherited from the parent state
var $$isInherited = function(state, dataProperty) {
var parentState = $$parentState(state);
return angular.isDefined(parentState.data) &&
angular.isDefined(parentState.data[dataProperty]) &&
angular.equals(state.data[dataProperty], parentState.data[dataProperty]);
};

this.$get = ['$state', function($state) {
// Get the parent state
var $$parentState = function(state) {
if (angular.isDefined(state.parent)) {
return $state.get(state.parent);
}

// Check if a property in state's data is inherited from the parent state
var $$isInherited = function(state, dataProperty) {
var parentState = $$parentState(state);
return angular.isDefined(parentState.data) &&
angular.isDefined(parentState.data[dataProperty]) &&
angular.equals(state.data[dataProperty], parentState.data[dataProperty]);
};
var compositeName = /^(.+)\.[^.]+$/.exec(state.name);
if(compositeName) {
return $state.get(compositeName[1]);
}
return null;
};

// Get the parent state
var $$parentState = function(state) {
if (angular.isDefined(state.parent)) {
return $state.get(state.parent);
// Add the state in the chain if not already in and if not abstract
var $$addStateInChain = function(chain, state, prefixStateInserted) {
var stateAlreadyInChain = false;
angular.forEach(chain, function(value) {
if(!stateAlreadyInChain && angular.equals(value, state)) {
stateAlreadyInChain = true;
}

var compositeName = /^(.+)\.[^.]+$/.exec(state.name);
if(compositeName) {
return $state.get(compositeName[1]);
});
if(!stateAlreadyInChain && !state.abstract) {
// Insert at first or second index.
if(prefixStateInserted) {
chain.splice(1, 0, state);
} else {
chain.unshift(state);
}
return null;
};

// Add the state in the chain if not already in and if not abstract
var $$addStateInChain = function(chain, state, prefixStateInserted) {
var stateAlreadyInChain = false;
angular.forEach(chain, function(value) {
if(!stateAlreadyInChain && angular.equals(value, state)) {
stateAlreadyInChain = true;
}
});
if(!stateAlreadyInChain && !state.abstract) {
// Insert at first or second index.
if(prefixStateInserted) {
chain.splice(1, 0, state);
} else {
chain.unshift(state);
}
return true;
}
return false;
};
return true;
}
return false;
};

// Get the state for the parent step in the breadcrumb
var $$breadcrumbParentState = function(state) {
// Get the state for the parent step in the breadcrumb
var $$breadcrumbParentState = function(state) {

if(angular.isDefined(state.data) &&
angular.isDefined(state.data.ncyBreadcrumbParent) &&
!$$isInherited(state, 'ncyBreadcrumbParent')) {
return $state.get(state.data.ncyBreadcrumbParent);
}
if(angular.isDefined(state.data) &&
angular.isDefined(state.data.ncyBreadcrumbParent) &&
!$$isInherited(state, 'ncyBreadcrumbParent')) {
return $state.get(state.data.ncyBreadcrumbParent);
}

return $$parentState(state);
return $$parentState(state);

};
};

return {
return {

getTemplate: function(templates) {
if($$options.templateUrl) {
// templateUrl takes precedence over template
return null;
} else if(templates[$$options.template]) {
// Predefined templates (bootstrap, ...)
return templates[$$options.template];
getTemplate: function(templates) {
if($$options.templateUrl) {
// templateUrl takes precedence over template
return null;
} else if(templates[$$options.template]) {
// Predefined templates (bootstrap, ...)
return templates[$$options.template];
} else {
return $$options.template;
}
},

getTemplateUrl: function() {
return $$options.templateUrl;
},

getStatesChain: function() {
var chain = [];
var prefixStateInserted = false;

// Prefix state treatment
if($$options.prefixStateName) {
var prefixState = $state.get($$options.prefixStateName);
if(prefixState) {
var prefixStep = angular.extend(prefixState, {ncyBreadcrumbLink: $state.href(prefixState)});
prefixStateInserted = $$addStateInChain(chain, prefixStep, prefixStateInserted);
} else {
return $$options.template;
}
},

getTemplateUrl: function() {
return $$options.templateUrl;
},

getStatesChain: function() {
var chain = [];
var prefixStateInserted = false;

// Prefix state treatment
if($$options.prefixStateName) {
var prefixState = $state.get($$options.prefixStateName);
if(prefixState) {
var prefixStep = angular.extend(prefixState, {ncyBreadcrumbLink: $state.href(prefixState)});
prefixStateInserted = $$addStateInChain(chain, prefixStep, prefixStateInserted);
} else {
throw 'Bad configuration : prefixState "' + $$options.prefixStateName + '" unknown';
}
throw 'Bad configuration : prefixState "' + $$options.prefixStateName + '" unknown';
}

// From current state to the root
var state = $state.$current.self;
do {
var step = angular.extend(state, {ncyBreadcrumbLink: $state.href(state.name)});
$$addStateInChain(chain, step, prefixStateInserted);
state = $$breadcrumbParentState(state);
}
while(state && state.name !== '');

return chain;
}
};
}];

})
.directive('ncyBreadcrumb', ['$state', '$interpolate', '$breadcrumb', '$rootScope', function ($state, $interpolate, $breadcrumb, $rootScope) {
this.$$templates = {
bootstrap2: '<ul class="breadcrumb">' +
'<li ng-repeat="step in steps | limitTo:(steps.length-1)">' +
'<a href="{{step.ncyBreadcrumbLink}}">{{step.ncyBreadcrumbLabel}}</a> ' +
'<span class="divider">/</span>' +
'</li>' +
'<li ng-repeat="step in steps | limitTo:-1" class="active">' +
'<span>{{step.ncyBreadcrumbLabel}}</span>' +
'</li>' +
'</ul>',
bootstrap3: '<ol class="breadcrumb">' +
'<li ng-repeat="step in steps | limitTo:(steps.length-1)">' +
'<a href="{{step.ncyBreadcrumbLink}}">{{step.ncyBreadcrumbLabel}}</a> ' +
'</li>' +
'<li ng-repeat="step in steps | limitTo:-1" class="active">' +
'<span>{{step.ncyBreadcrumbLabel}}</span>' +
'</li>' +
'</ol>'
};

return {
restrict: 'AE',
replace: true,
scope: {},
template: $breadcrumb.getTemplate(this.$$templates),
templateUrl: $breadcrumb.getTemplateUrl(),
link: {
post: function postLink(scope) {
$rootScope.$on('$viewContentLoaded', function (event) {
scope.steps = $breadcrumb.getStatesChain();
angular.forEach(scope.steps, function (value) {
if (value.data && value.data.ncyBreadcrumbLabel) {
var parseLabel = $interpolate(value.data.ncyBreadcrumbLabel);
value.ncyBreadcrumbLabel = parseLabel(event.targetScope);
} else {
value.ncyBreadcrumbLabel = value.name;
}
});
});
// From current state to the root
var state = $state.$current.self;
do {
var step = angular.extend(state, {ncyBreadcrumbLink: $state.href(state.name)});
$$addStateInChain(chain, step, prefixStateInserted);
state = $$breadcrumbParentState(state);
}
while(state && state.name !== '');

return chain;
}
};
}]);
}];

}

function BreadcrumbDirective($interpolate, $breadcrumb, $rootScope) {
this.$$templates = {
bootstrap2: '<ul class="breadcrumb">' +
'<li ng-repeat="step in steps | limitTo:(steps.length-1)">' +
'<a href="{{step.ncyBreadcrumbLink}}">{{step.ncyBreadcrumbLabel}}</a> ' +
'<span class="divider">/</span>' +
'</li>' +
'<li ng-repeat="step in steps | limitTo:-1" class="active">' +
'<span>{{step.ncyBreadcrumbLabel}}</span>' +
'</li>' +
'</ul>',
bootstrap3: '<ol class="breadcrumb">' +
'<li ng-repeat="step in steps | limitTo:(steps.length-1)">' +
'<a href="{{step.ncyBreadcrumbLink}}">{{step.ncyBreadcrumbLabel}}</a> ' +
'</li>' +
'<li ng-repeat="step in steps | limitTo:-1" class="active">' +
'<span>{{step.ncyBreadcrumbLabel}}</span>' +
'</li>' +
'</ol>'
};

return {
restrict: 'AE',
replace: true,
scope: {},
template: $breadcrumb.getTemplate(this.$$templates),
templateUrl: $breadcrumb.getTemplateUrl(),
link: {
post: function postLink(scope) {
$rootScope.$on('$viewContentLoaded', function (event) {
scope.steps = $breadcrumb.getStatesChain();
angular.forEach(scope.steps, function (value) {
if (value.data && value.data.ncyBreadcrumbLabel) {
var parseLabel = $interpolate(value.data.ncyBreadcrumbLabel);
value.ncyBreadcrumbLabel = parseLabel(event.targetScope);
} else {
value.ncyBreadcrumbLabel = value.name;
}
});
});
}
}
};
}
BreadcrumbDirective.$inject = ['$interpolate', '$breadcrumb', '$rootScope'];

angular.module('ncy-angular-breadcrumb', ['ui.router.state'])
.provider('$breadcrumb', $Breadcrumb)
.directive('ncyBreadcrumb', BreadcrumbDirective);

})(window, window.angular);
4 changes: 2 additions & 2 deletions dist/angular-breadcrumb.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit ab2ae1a

Please sign in to comment.