diff options
| -rw-r--r-- | docs/component-spec/versionJumpSpec.js | 125 | ||||
| -rw-r--r-- | docs/src/appCache.js | 2 | ||||
| -rwxr-xr-x | docs/src/gen-docs.js | 10 | ||||
| -rw-r--r-- | docs/src/ngdoc.js | 19 | ||||
| -rw-r--r-- | docs/src/templates/css/docs.css | 4 | ||||
| -rw-r--r-- | docs/src/templates/index.html | 123 | ||||
| -rw-r--r-- | docs/src/templates/js/docs.js | 66 | ||||
| -rw-r--r-- | karma-docs.conf.js | 2 | ||||
| -rw-r--r-- | lib/grunt/plugins.js | 3 | ||||
| -rw-r--r-- | lib/grunt/utils.js | 5 | 
10 files changed, 292 insertions, 67 deletions
diff --git a/docs/component-spec/versionJumpSpec.js b/docs/component-spec/versionJumpSpec.js new file mode 100644 index 00000000..d5a54975 --- /dev/null +++ b/docs/component-spec/versionJumpSpec.js @@ -0,0 +1,125 @@ +describe('DocsApp', function() { + +  beforeEach(module('docsApp')); + +  describe('DocsVersionsCtrl', function() { +    var $scope, ctrl, window, version = '9.8.7'; + +    beforeEach(function() { +      module(function($provide) { +        $provide.value('NG_VERSIONS',[ +          '1.0.0', +          '1.0.1', +          '1.0.2', +          '1.0.3', +          '1.0.4', +          '1.0.5', +          '1.0.6', +          '1.1.0', +          '1.1.1', +          '1.1.2', +          '1.1.3', +          '1.1.4', +          '2.1.3' +        ]); +        $provide.value('$window', window = angular.mock.createMockWindow()); +      }); +      inject(function($controller, $rootScope) { +        $scope = $rootScope.$new(); +        $scope.version = version; +        ctrl = $controller('DocsVersionsCtrl',{ +          $scope : $scope, +          $window : window +        }); +      }); +    }); + +    it('should have the correct version of angular', function() { +      expect(version).toBe($scope.version); +    }); + +    it('should order versions in decending order', function() { +      expect($scope.versions.length).toBeGreaterThan(0); + +      var one = $scope.versions[0].version; +      var two = $scope.versions[1].version; + +      expect(one).toBeGreaterThan(two); +    }); + +    it('should list unstable versions at the top of the list', function() { +      expect($scope.versions[0].stable).toBe(false); +    }); + +    it('should list all items below the last stable as stable regardless of version number', function() { +      var limit = $scope.versions.length - 1, +          lastUnstableIndex = 0; + +      while(lastUnstableIndex <= limit) { +        if($scope.versions[lastUnstableIndex++].stable) break; +      } + +      for(var i=lastUnstableIndex;i<=limit;i++) { +        expect($scope.versions[i].stable).toBe(true); +      } +    }); + +    describe('changing the URL', function() { +      it('should not support the old < 1.0 docs pages', function() { +        window.location = 'old'; + +        $scope.versions.unshift({ +          stable : true, +          version : '0.9.10' +        }); +        $scope.jumpToDocsVersion('0.9.10'); +        expect(window.location).toBe('old'); + +        $scope.versions.unshift({ +          stable : true, +          version : '0.10.1' +        }); +        $scope.jumpToDocsVersion('0.10.1'); +        expect(window.location).toBe('old'); + +        $scope.jumpToDocsVersion('2.1.3'); +        expect(window.location).toBe('http://code.angularjs.org/2.1.3/docs'); +      });  + +      it('should jump to the older versions of current docs for version >= 1.0.2', function() { +        $scope.jumpToDocsVersion('1.0.1'); +        expect(window.location).toBe('http://code.angularjs.org/1.0.1/docs-1.0.1'); + +        $scope.jumpToDocsVersion('1.0.2'); +        expect(window.location).toBe('http://code.angularjs.org/1.0.2/docs'); + +        $scope.jumpToDocsVersion('1.1.2'); +        expect(window.location).toBe('http://code.angularjs.org/1.1.2/docs'); +      }); + +      it('should use the current docs.angularjs.org page when the selected version is the last stable version', function() { +        $scope.versions = [{ +          stable : true, +          title : 'test', +          version : '1.1.1' +        }]; + +        $scope.jumpToDocsVersion('1.1.1'); +        expect(window.location).toBe('http://docs.angularjs.org'); + +        $scope.versions.unshift({ +          stable : true, +          title : 'test2', +          version : '1.2.1' +        }); + +        $scope.jumpToDocsVersion('1.1.1'); +        expect(window.location).toBe('http://code.angularjs.org/1.1.1/docs'); +        $scope.jumpToDocsVersion('1.2.1'); +        expect(window.location).toBe('http://docs.angularjs.org'); +      }); + +    }); +  }); + +}); diff --git a/docs/src/appCache.js b/docs/src/appCache.js index 7b21624e..01551f2b 100644 --- a/docs/src/appCache.js +++ b/docs/src/appCache.js @@ -61,7 +61,7 @@ function appCacheTemplate() {            "syntaxhighlighter/syntaxhighlighter-combined.js",            "../angular.min.js",            "docs-combined.js", -          "docs-keywords.js", +          "docs-data.js",            "docs-combined.css",            "syntaxhighlighter/syntaxhighlighter-combined.css",            "img/texture_1.png", diff --git a/docs/src/gen-docs.js b/docs/src/gen-docs.js index 52d1f629..f8ca5548 100755 --- a/docs/src/gen-docs.js +++ b/docs/src/gen-docs.js @@ -43,6 +43,8 @@ writer.makeDir('build/docs/', true).then(function() {  function writeTheRest(writesFuture) {    var metadata = ngdoc.metadata(docs); +  var versions = ngdoc.ngVersions(); +  var currentVersion = ngdoc.ngCurrentVersion();    writesFuture.push(writer.symlink('../../docs/content/notes', 'build/docs/notes', 'dir'));    writesFuture.push(writer.symlinkTemplate('css', 'dir')); @@ -90,8 +92,12 @@ function writeTheRest(writesFuture) {    writesFuture.push(writer.copyTemplate('docs-scenario.html')); // will be rewritten, don't symlink    writesFuture.push(writer.output('docs-scenario.js', ngdoc.scenarios(docs))); -  writesFuture.push(writer.output('docs-keywords.js', -                                ['NG_PAGES=', JSON.stringify(metadata).replace(/{/g, '\n{'), ';'])); +  writesFuture.push(writer.output('docs-data.js',[ +    "angular.module('docsData', [])", +    ".value('NG_PAGES'," + JSON.stringify(metadata).replace(/{/g, '\n{') + ")", +    ".value('NG_VERSION'," + JSON.stringify(currentVersion) + ")", +    ".value('NG_VERSIONS'," + JSON.stringify(versions) + ");" +  ]));    writesFuture.push(writer.output('sitemap.xml', new SiteMap(docs).render()));    writesFuture.push(writer.output('robots.txt', 'Sitemap: http://docs.angularjs.org/sitemap.xml\n')); diff --git a/docs/src/ngdoc.js b/docs/src/ngdoc.js index 1870f87c..af78c1e3 100644 --- a/docs/src/ngdoc.js +++ b/docs/src/ngdoc.js @@ -11,6 +11,8 @@ var globalID = 0;  var fs = require('fs');  var fspath = require('path');  var markdown = new Showdown.converter({ extensions : ['table'] }); +var shell = require('shelljs'); +var gruntUtil = require('../../lib/grunt/utils.js');  exports.trim = trim;  exports.metadata = metadata; @@ -18,6 +20,23 @@ exports.scenarios = scenarios;  exports.merge = merge;  exports.Doc = Doc; +exports.ngVersions = function() { +  var line, versions = [], regex = /^v([1-9]\d*(?:\.\d+)+)$/; //only fetch >= 1.0.0 versions +  shell.exec('git tag', {silent: true}).output.split(/\s*\n\s*/) +    .forEach(function(line) { +      var matches = regex.exec(line); +      if(matches && matches.length > 0) { +        versions.push(matches[1]); +      } +    }); +  versions.push(exports.ngCurrentVersion().number); +  return versions; +}; + +exports.ngCurrentVersion = function() { +  return gruntUtil.getVersion(); +}; +  var BOOLEAN_ATTR = {};  ['multiple', 'selected', 'checked', 'disabled', 'readOnly', 'required'].forEach(function(value) {    BOOLEAN_ATTR[value] = true; diff --git a/docs/src/templates/css/docs.css b/docs/src/templates/css/docs.css index 1b87c551..6977d205 100644 --- a/docs/src/templates/css/docs.css +++ b/docs/src/templates/css/docs.css @@ -461,4 +461,8 @@ pre ol li {    box-shadow:-6px 0 5px #555;    display:block;    border-radius:10px; + +.docs-version-jump { +  width:180px; +  margin-bottom:20px;  } diff --git a/docs/src/templates/index.html b/docs/src/templates/index.html index 8e2fbc37..15093d4c 100644 --- a/docs/src/templates/index.html +++ b/docs/src/templates/index.html @@ -48,8 +48,8 @@        addTag('script', {src: 'components/google-code-prettify.js' }, sync);        addTag('script', {src: 'components/' + (debug ? 'lunr.js' : 'lunr.min.js') }, sync);        addTag('script', {src: 'components/' + (debug ? 'showdown.js' : 'showdown.min.js') }, sync); +      addTag('script', {src: 'docs-data.js'}, sync);        addTag('script', {src: 'js/docs.js'}, sync); -      addTag('script', {src: 'docs-keywords.js'}, sync);        function path(name) {          if (production) { @@ -233,74 +233,83 @@      <div class="row">        <div class="span3"> -        <form class="well form-search" ng-submit="submitForm()"> -          <div class="dropdown search" -               ng-class="{open: focused && bestMatch.rank > 0 && bestMatch.page != currentPage}"> -            <input type="text" ng-model="search" placeholder="search the docs" -                   tabindex="1" accesskey="s" class="input-medium search-query" focused="focused"> -            <ul class="dropdown-menu"> -              <li> -                <a href="{{bestMatch.page.url}}">{{bestMatch.page.shortName}}</a> -              </li> -            </ul> +        <div class="well"> +          <div ng-controller="DocsVersionsCtrl"> +            <select ng-options="v.version as v.title group by v.group for v in versions" +                    ng-model="version" +                    ng-change="jumpToDocsVersion(version)" +                    class="docs-version-jump"> +            </select>            </div> +          <form class="form-search" ng-submit="submitForm()"> +            <div class="dropdown search" +                 ng-class="{open: focused && bestMatch.rank > 0 && bestMatch.page != currentPage}"> +              <input type="text" ng-model="search" placeholder="search the docs" +                     tabindex="1" accesskey="s" class="input-medium search-query" focused="focused"> +              <ul class="dropdown-menu"> +                <li> +                  <a href="{{bestMatch.page.url}}">{{bestMatch.page.shortName}}</a> +                </li> +              </ul> +            </div> -          <div class="spacer"></div> -          <div ng-show="search">Filtered results:</div> +            <div class="spacer"></div> +            <div ng-show="search">Filtered results:</div> -          <ul class="nav nav-list" ng-hide="page"> -            <li ng-repeat="page in pages track by page.url" ng-class="navClass(page)" class="api-list-item"> -              <a href="{{page.url}}" tabindex="2">{{page.shortName}}</a> -            </li> -          </ul> +            <ul class="nav nav-list" ng-hide="page"> +              <li ng-repeat="page in pages track by page.url" ng-class="navClass(page)" class="api-list-item"> +                <a href="{{page.url}}" tabindex="2">{{page.shortName}}</a> +              </li> +            </ul> -          <ul class="nav nav-list well" ng-repeat="module in modules track by module.url" class="api-list-item"> -            <li class="nav-header module"> -              <a class="guide" href="{{URL.module}}">module</a> -              <a class="code" href="{{module.url}}">{{module.name}}</a> -            </li> +            <ul class="nav nav-list well" ng-repeat="module in modules track by module.url" class="api-list-item"> +              <li class="nav-header module"> +                <a class="guide" href="{{URL.module}}">module</a> +                <a class="code" href="{{module.url}}">{{module.name}}</a> +              </li> -            <li class="nav-header section" ng-show="module.directives"> -              <a href="{{URL.directive}}" class="guide">directive</a> -            </li> -            <li ng-repeat="page in module.directives track by page.url" ng-class="navClass(page)" ng-animate="'expand'" class="api-list-item"> -              <a href="{{page.url}}" tabindex="2">{{page.shortName}}</a> -            </li> +              <li class="nav-header section" ng-show="module.directives"> +                <a href="{{URL.directive}}" class="guide">directive</a> +              </li> +              <li ng-repeat="page in module.directives track by page.url" ng-class="navClass(page)" ng-animate="'expand'" class="api-list-item"> +                <a href="{{page.url}}" tabindex="2">{{page.shortName}}</a> +              </li> -            <li class="nav-header section" ng-show="module.filters"> -              <a href="{{URL.filter}}" class="guide">filter</a> -            </li> -            <li ng-repeat="page in module.filters track by page.url" ng-class="navClass(page)" ng-animate="'expand'" class="api-list-item"> -              <a href="{{page.url}}" tabindex="2">{{page.shortName}}</a> -            </li> +              <li class="nav-header section" ng-show="module.filters"> +                <a href="{{URL.filter}}" class="guide">filter</a> +              </li> +              <li ng-repeat="page in module.filters track by page.url" ng-class="navClass(page)" ng-animate="'expand'" class="api-list-item"> +                <a href="{{page.url}}" tabindex="2">{{page.shortName}}</a> +              </li> -            <li class="nav-header section" ng-show="module.services"> -              <a href="{{URL.service}}" class="guide">service</a> -            </li> -            <li ng-repeat="service in module.services track by service.instance.url" ng-animate="'expand'" ng-class="navClass(service.instance, service.provider)" class="api-list-item"> -              <a ng-show="service.provider" class="pull-right" href="{{service.provider.url}}" tabindex="2"><i class="icon-cog"></i></a> -              <a href="{{service.instance.url}}" tabindex="2">{{service.name}}</a> -            </li> +              <li class="nav-header section" ng-show="module.services"> +                <a href="{{URL.service}}" class="guide">service</a> +              </li> +              <li ng-repeat="service in module.services track by service.instance.url" ng-animate="'expand'" ng-class="navClass(service.instance, service.provider)" class="api-list-item"> +                <a ng-show="service.provider" class="pull-right" href="{{service.provider.url}}" tabindex="2"><i class="icon-cog"></i></a> +                <a href="{{service.instance.url}}" tabindex="2">{{service.name}}</a> +              </li> -            <li class="nav-header section" ng-show="module.types"> -              <a href="{{URL.type}}" class="guide">Types</a> -            </li> -            <li ng-repeat="page in module.types track by page.url" ng-class="navClass(page)" ng-animate="'expand'" class="api-list-item"> -              <a href="{{page.url}}" tabindex="2">{{page.shortName}}</a> -            </li> +              <li class="nav-header section" ng-show="module.types"> +                <a href="{{URL.type}}" class="guide">Types</a> +              </li> +              <li ng-repeat="page in module.types track by page.url" ng-class="navClass(page)" ng-animate="'expand'" class="api-list-item"> +                <a href="{{page.url}}" tabindex="2">{{page.shortName}}</a> +              </li> -            <li class="nav-header section" ng-show="module.globals"> -              <a href="{{URL.api}}" class="global guide">global APIs</a> -                -            </li> -            <li ng-repeat="page in module.globals track by page.url" ng-class="navClass(page)" class="api-list-item"> -              <a href="{{page.url}}" tabindex="2">{{page.id}}</a> -            </li> +              <li class="nav-header section" ng-show="module.globals"> +                <a href="{{URL.api}}" class="global guide">global APIs</a> +                  +              </li> +              <li ng-repeat="page in module.globals track by page.url" ng-class="navClass(page)" class="api-list-item"> +                <a href="{{page.url}}" tabindex="2">{{page.id}}</a> +              </li> -          </ul> +            </ul> -        </form> +          </form> +        </div>        </div>        <div class="span9"> diff --git a/docs/src/templates/js/docs.js b/docs/src/templates/js/docs.js index 97fea067..ae0f1e58 100644 --- a/docs/src/templates/js/docs.js +++ b/docs/src/templates/js/docs.js @@ -4,6 +4,64 @@ var docsApp = {    serviceFactory: {}  }; +docsApp.controller.DocsVersionsCtrl = ['$scope', '$window', 'NG_VERSIONS', function($scope, $window, NG_VERSIONS) { +  $scope.versions = expandVersions(NG_VERSIONS); +  $scope.version  = ($scope.version || angular.version.full).match(/^([\d\.]+\d+)/)[1]; //match only the number +   +  $scope.jumpToDocsVersion = function(value) { +    var isLastStable, +        version, +        versions = $scope.versions; +    for(var i=versions.length-1;i>=0;i--) { +      var v = versions[i]; +      if(v.version == value) { +        var next = versions[i - 1]; +        isLastStable = v.stable && (!next || next && !next.stable); +        version = v; +        break; +      } +    }; + +    if(version && version.version >= '1.0.0') { +      //the older versions have a different path to the docs within their repo directory +      var docsPath = version.version < '1.0.2' ? 'docs-' + version.version : 'docs'; + +      //the last stable version should redirect to docs.angularjs.org instead of code.angularjs.org +      var url = 'http://' + +                  (isLastStable ? +                    'docs.angularjs.org' : +                    'code.angularjs.org/' + version.version + '/' + docsPath); + +      $window.location = url; +    } +  }; + +  function expandVersions(angularVersions) { +    var unstableVersionStart = 0; +    angularVersions.forEach(function(version) { +      var split = version.split('.'); +      unstableVersionStart = split[1] % 2 == 1 ? +                        Math.max(unstableVersionStart, parseInt(split[0] + '' + split[1])) : +                        unstableVersionStart; +    }); + +    var versions = []; +    for(var i=angularVersions.length-1;i>=0;i--) { +      var version = angularVersions[i]; +      var split = version.split('.'); +      var stable = parseInt(split[0] + '' + split[1]) < unstableVersionStart; +      versions.push({ +        version : version, +        stable : stable, +        title : 'AngularJS - v' + version, +        group : (stable ? 'Stable' : 'Unstable') +      }); +    }; + +    return versions; +  }; +}]; +  docsApp.controller.DocsNavigationCtrl = ['$scope', 'fullTextSearch', '$location', function($scope, fullTextSearch, $location) {    fullTextSearch.init();    $scope.search = function(q) { @@ -47,7 +105,7 @@ docsApp.controller.DocsNavigationCtrl = ['$scope', 'fullTextSearch', '$location'    };  }]; -docsApp.serviceFactory.fullTextSearch = ['$q', '$rootScope', function($q, $rootScope) { +docsApp.serviceFactory.fullTextSearch = ['$q', '$rootScope', 'NG_PAGES', function($q, $rootScope, NG_PAGES) {    return {      dbName : 'docs',      indexName : 'docsindex', @@ -374,7 +432,7 @@ docsApp.serviceFactory.openJsFiddle = function(templateMerge, formPostData, angu  }; -docsApp.serviceFactory.sections = function sections() { +docsApp.serviceFactory.sections = ['NG_PAGES', function sections(NG_PAGES) {    var sections = {      guide: [],      api: [], @@ -407,7 +465,7 @@ docsApp.serviceFactory.sections = function sections() {    });    return sections; -}; +}];  docsApp.controller.DocsController = function($scope, $location, $window, $cookies, sections) { @@ -706,7 +764,7 @@ docsApp.controller.DocsController = function($scope, $location, $window, $cookie  }; -angular.module('docsApp', ['ngResource', 'ngRoute', 'ngCookies', 'ngSanitize', 'bootstrap', 'bootstrapPrettify']). +angular.module('docsApp', ['ngResource', 'ngRoute', 'ngCookies', 'ngSanitize', 'bootstrap', 'bootstrapPrettify', 'docsData']).    config(function($locationProvider) {      $locationProvider.html5Mode(true).hashPrefix('!');    }). diff --git a/karma-docs.conf.js b/karma-docs.conf.js index c65619a1..69192509 100644 --- a/karma-docs.conf.js +++ b/karma-docs.conf.js @@ -20,7 +20,7 @@ files = [    'build/docs/components/angular-bootstrap.js',    'build/docs/components/angular-bootstrap-prettify.js',    'build/docs/js/docs.js', -  'build/docs/docs-keywords.js', +  'build/docs/docs-data.js',    'docs/component-spec/*.js'  ]; diff --git a/lib/grunt/plugins.js b/lib/grunt/plugins.js index b423b61f..ca2d6ec6 100644 --- a/lib/grunt/plugins.js +++ b/lib/grunt/plugins.js @@ -42,7 +42,8 @@ module.exports = function(grunt) {      docs.on('exit', function(code){        if(code !== 0) grunt.fail.warn('Error creating docs');        grunt.file.expand(files).forEach(function(file){ -        grunt.file.write(file, util.process(grunt.file.read(file), grunt.config('NG_VERSION'), false)); +        var content = util.process(grunt.file.read(file), grunt.config('NG_VERSION'), false); +        grunt.file.write(file, content);        });        grunt.log.ok('docs created');        done(); diff --git a/lib/grunt/utils.js b/lib/grunt/utils.js index 3d2ab316..38533f27 100644 --- a/lib/grunt/utils.js +++ b/lib/grunt/utils.js @@ -16,8 +16,11 @@ module.exports = {      var semver = match[1].split('.');      var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', ''); +    var fullVersion = (match[1] + (match[2] ? '-' + hash : '')); +    var numVersion = semver[0] + '.' + semver[1] + '.' + semver[2];      var version = { -      full: (match[1] + (match[2] ? '-' + hash : '')), +      number: numVersion, +      full: fullVersion,        major: semver[0],        minor: semver[1],        dot: semver[2],  | 
