123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- /*! angular-breadcrumb - v0.3.2-dev-2014-12-14
- * http://ncuillery.github.io/angular-breadcrumb
- * Copyright (c) 2014 Nicolas Cuillery; Licensed MIT */
- (function (window, angular, undefined) {
- 'use strict';
- function isAOlderThanB(scopeA, scopeB) {
- if(angular.equals(scopeA.length, scopeB.length)) {
- return scopeA > scopeB;
- } else {
- return scopeA.length > scopeB.length;
- }
- }
- function parseStateRef(ref) {
- var parsed = ref.replace(/\n/g, " ").match(/^([^(]+?)\s*(\((.*)\))?$/);
- if (!parsed || parsed.length !== 4) { throw new Error("Invalid state ref '" + ref + "'"); }
- return { state: parsed[1], paramExpr: parsed[3] || null };
- }
- function $Breadcrumb() {
- var $$options = {
- prefixStateName: null,
- template: 'bootstrap3',
- templateUrl: null,
- includeAbstract : false
- };
- this.setOptions = function(options) {
- angular.extend($$options, options);
- };
- this.$get = ['$state', '$stateParams', '$rootScope', function($state, $stateParams, $rootScope) {
- var $lastViewScope = $rootScope;
- // Early catch of $viewContentLoaded event
- $rootScope.$on('$viewContentLoaded', function (event) {
- // With nested views, the event occur several times, in "wrong" order
- if(isAOlderThanB(event.targetScope.$id, $lastViewScope.$id)) {
- $lastViewScope = event.targetScope;
- }
- });
- // Get the parent state
- var $$parentState = function(state) {
- // Check if state has explicit parent OR we try guess parent from its name
- var name = state.parent || (/^(.+)\.[^.]+$/.exec(state.name) || [])[1];
- // If we were able to figure out parent name then get this state
- return name;
- };
- // Add the state in the chain if not already in and if not abstract
- var $$addStateInChain = function(chain, stateRef) {
- var conf,
- parentParams,
- ref = parseStateRef(stateRef);
- for(var i=0, l=chain.length; i<l; i+=1) {
- if (chain[i].name === ref.state) {
- return;
- }
- }
- conf = $state.get(ref.state);
- if((!conf.abstract || $$options.includeAbstract) && !(conf.ncyBreadcrumb && conf.ncyBreadcrumb.skip)) {
- if(ref.paramExpr) {
- parentParams = $lastViewScope.$eval(ref.paramExpr);
- }
- conf.ncyBreadcrumbLink = $state.href(ref.state, parentParams || $stateParams || {});
- chain.unshift(conf);
- }
- };
- // Get the state for the parent step in the breadcrumb
- var $$breadcrumbParentState = function(stateRef) {
- var ref = parseStateRef(stateRef),
- conf = $state.get(ref.state);
- if(conf.ncyBreadcrumb && conf.ncyBreadcrumb.parent) {
- // Handle the "parent" property of the breadcrumb, override the parent/child relation of the state
- var isFunction = typeof conf.ncyBreadcrumb.parent === 'function';
- var parentStateRef = isFunction ? conf.ncyBreadcrumb.parent($lastViewScope) : conf.ncyBreadcrumb.parent;
- if(parentStateRef) {
- return parentStateRef;
- }
- }
- return $$parentState(conf);
- };
- 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];
- } else {
- return $$options.template;
- }
- },
- getTemplateUrl: function() {
- return $$options.templateUrl;
- },
- getStatesChain: function(exitOnFirst) { // Deliberately undocumented param, see getLastStep
- var chain = [];
- // From current state to the root
- for(var stateRef = $state.$current.self.name; stateRef; stateRef=$$breadcrumbParentState(stateRef)) {
- $$addStateInChain(chain, stateRef);
- if(exitOnFirst && chain.length) {
- return chain;
- }
- }
- // Prefix state treatment
- if($$options.prefixStateName) {
- $$addStateInChain(chain, $$options.prefixStateName);
- }
- return chain;
- },
- getLastStep: function() {
- var chain = this.getStatesChain(true);
- return chain.length ? chain[0] : undefined;
- },
- $getLastViewScope: function() {
- return $lastViewScope;
- }
- };
- }];
- }
- var getExpression = function(interpolationFunction) {
- if(interpolationFunction.expressions) {
- return interpolationFunction.expressions;
- } else {
- var expressions = [];
- angular.forEach(interpolationFunction.parts, function(part) {
- if(angular.isFunction(part)) {
- expressions.push(part.exp);
- }
- });
- return expressions;
- }
- };
- var registerWatchers = function(labelWatcherArray, interpolationFunction, viewScope, step) {
- angular.forEach(getExpression(interpolationFunction), function(expression) {
- var watcher = viewScope.$watch(expression, function() {
- step.ncyBreadcrumbLabel = interpolationFunction(viewScope);
- });
- labelWatcherArray.push(watcher);
- });
- };
- var deregisterWatchers = function(labelWatcherArray) {
- angular.forEach(labelWatcherArray, function(deregisterWatch) {
- deregisterWatch();
- });
- labelWatcherArray = [];
- };
- function BreadcrumbDirective($interpolate, $breadcrumb, $rootScope) {
- var $$templates = {
- bootstrap2: '<ul class="breadcrumb">' +
- '<li ng-repeat="step in steps" ng-switch="$last || !!step.abstract" ng-class="{active: $last}">' +
- '<a ng-switch-when="false" href="{{step.ncyBreadcrumbLink}}">{{step.ncyBreadcrumbLabel}}</a> ' +
- '<span ng-switch-when="true">{{step.ncyBreadcrumbLabel}}</span>' +
- '<span class="divider" ng-hide="$last">/</span>' +
- '</li>' +
- '</ul>',
- bootstrap3: '<ol class="breadcrumb">' +
- '<li ng-repeat="step in steps" ng-class="{active: $last}" ng-switch="$last || !!step.abstract">' +
- '<a ng-switch-when="false" href="{{step.ncyBreadcrumbLink}}">{{step.ncyBreadcrumbLabel}}</a> ' +
- '<span ng-switch-when="true">{{step.ncyBreadcrumbLabel}}</span>' +
- '</li>' +
- '</ol>'
- };
- return {
- restrict: 'AE',
- replace: true,
- scope: {},
- template: $breadcrumb.getTemplate($$templates),
- templateUrl: $breadcrumb.getTemplateUrl(),
- link: {
- post: function postLink(scope) {
- var labelWatchers = [];
- var renderBreadcrumb = function() {
- deregisterWatchers(labelWatchers);
- var viewScope = $breadcrumb.$getLastViewScope();
- scope.steps = $breadcrumb.getStatesChain();
- angular.forEach(scope.steps, function (step) {
- if (step.ncyBreadcrumb && step.ncyBreadcrumb.label) {
- var parseLabel = $interpolate(step.ncyBreadcrumb.label);
- step.ncyBreadcrumbLabel = parseLabel(viewScope);
- // Watcher for further viewScope updates
- registerWatchers(labelWatchers, parseLabel, viewScope, step);
- } else {
- step.ncyBreadcrumbLabel = step.name;
- }
- });
- };
- $rootScope.$on('$viewContentLoaded', function () {
- renderBreadcrumb();
- });
- // View(s) may be already loaded while the directive's linking
- renderBreadcrumb();
- }
- }
- };
- }
- BreadcrumbDirective.$inject = ['$interpolate', '$breadcrumb', '$rootScope'];
- function BreadcrumbLastDirective($interpolate, $breadcrumb, $rootScope) {
- return {
- restrict: 'A',
- scope: {},
- template: '{{ncyBreadcrumbLabel}}',
- compile: function(cElement, cAttrs) {
- // Override the default template if ncyBreadcrumbLast has a value
- var template = cElement.attr(cAttrs.$attr.ncyBreadcrumbLast);
- if(template) {
- cElement.html(template);
- }
- return {
- post: function postLink(scope) {
- var labelWatchers = [];
- var renderLabel = function() {
- deregisterWatchers(labelWatchers);
- var viewScope = $breadcrumb.$getLastViewScope();
- var lastStep = $breadcrumb.getLastStep();
- if(lastStep) {
- scope.ncyBreadcrumbLink = lastStep.ncyBreadcrumbLink;
- if (lastStep.ncyBreadcrumb && lastStep.ncyBreadcrumb.label) {
- var parseLabel = $interpolate(lastStep.ncyBreadcrumb.label);
- scope.ncyBreadcrumbLabel = parseLabel(viewScope);
- // Watcher for further viewScope updates
- // Tricky last arg: the last step is the entire scope of the directive !
- registerWatchers(labelWatchers, parseLabel, viewScope, scope);
- } else {
- scope.ncyBreadcrumbLabel = lastStep.name;
- }
- }
- };
- $rootScope.$on('$viewContentLoaded', function () {
- renderLabel();
- });
- // View(s) may be already loaded while the directive's linking
- renderLabel();
- }
- };
- }
- };
- }
- BreadcrumbLastDirective.$inject = ['$interpolate', '$breadcrumb', '$rootScope'];
- angular.module('ncy-angular-breadcrumb', ['ui.router.state'])
- .provider('$breadcrumb', $Breadcrumb)
- .directive('ncyBreadcrumb', BreadcrumbDirective)
- .directive('ncyBreadcrumbLast', BreadcrumbLastDirective);
- })(window, window.angular);
|