aboutsummaryrefslogtreecommitdiffstats
path: root/docs/app/src
diff options
context:
space:
mode:
authorPeter Bacon Darwin2014-02-12 22:47:42 +0000
committerPeter Bacon Darwin2014-02-16 19:03:41 +0000
commit389d4879da4aa620ee95d789b19ff9be44eb730a (patch)
tree93352ddc8738a975904a1774d51b93d585ca1075 /docs/app/src
parenta564160511bf1bbed5a4fe5d2981fae1bb664eca (diff)
downloadangular.js-389d4879da4aa620ee95d789b19ff9be44eb730a.tar.bz2
chore(doc-gen): new docs
chore(doc-gen): implement dgeni
Diffstat (limited to 'docs/app/src')
-rw-r--r--docs/app/src/directives.js35
-rw-r--r--docs/app/src/docs.js128
-rw-r--r--docs/app/src/errors.js62
-rw-r--r--docs/app/src/examples.js266
-rw-r--r--docs/app/src/navigationService.js24
-rw-r--r--docs/app/src/search.js149
-rw-r--r--docs/app/src/tutorials.js58
-rw-r--r--docs/app/src/versions.js15
8 files changed, 737 insertions, 0 deletions
diff --git a/docs/app/src/directives.js b/docs/app/src/directives.js
new file mode 100644
index 00000000..15bef69b
--- /dev/null
+++ b/docs/app/src/directives.js
@@ -0,0 +1,35 @@
+angular.module('directives', [])
+
+.directive('code', function() {
+ return { restrict:'E', terminal: true };
+})
+
+/**
+ * backToTop Directive
+ * @param {Function} $anchorScroll
+ *
+ * @description Ensure that the browser scrolls when the anchor is clicked
+ */
+.directive('backToTop', ['$anchorScroll', function($anchorScroll) {
+ return function link(scope, element) {
+ element.on('click', function(event) {
+ scope.$apply($anchorScroll);
+ });
+ };
+}])
+
+
+.directive('code', function() {
+ return {
+ restrict: 'E',
+ terminal: true,
+ compile: function(element) {
+ var linenums = element.hasClass('linenum') || element.parent()[0].nodeName === 'PRE';
+ var match = /lang-(\S)+/.exec(element.className);
+ var lang = match && match[1];
+ var html = element.html();
+ element.html(window.prettyPrintOne(html, lang, linenums));
+ }
+ };
+});
+
diff --git a/docs/app/src/docs.js b/docs/app/src/docs.js
new file mode 100644
index 00000000..5f9543d6
--- /dev/null
+++ b/docs/app/src/docs.js
@@ -0,0 +1,128 @@
+angular.module('docsApp', [
+ 'ngRoute',
+ 'ngCookies',
+ 'ngSanitize',
+ 'ngAnimate',
+ 'versionsData',
+ 'pagesData',
+ 'directives',
+ 'errors',
+ 'examples',
+ 'search',
+ 'tutorials',
+ 'versions',
+ 'bootstrap',
+ 'bootstrapPrettify',
+ 'ui.bootstrap.dropdown'
+])
+
+
+.config(function($locationProvider) {
+ $locationProvider.html5Mode(true).hashPrefix('!');
+})
+
+
+.controller('DocsController', function($scope, $rootScope, $location, $window, $cookies, NG_PAGES, NG_NAVIGATION, NG_VERSION) {
+
+ $scope.fold = function(url) {
+ if(url) {
+ $scope.docs_fold = '/notes/' + url;
+ if(/\/build/.test($window.location.href)) {
+ $scope.docs_fold = '/build/docs' + $scope.docs_fold;
+ }
+ window.scrollTo(0,0);
+ }
+ else {
+ $scope.docs_fold = null;
+ }
+ };
+ var OFFLINE_COOKIE_NAME = 'ng-offline',
+ INDEX_PATH = /^(\/|\/index[^\.]*.html)$/;
+
+
+ /**********************************
+ Publish methods
+ ***********************************/
+
+ $scope.navClass = function(navItem) {
+ return {
+ active: navItem.href && this.currentPage.path,
+ 'nav-index-section': navItem.type === 'section'
+ };
+ };
+
+ $scope.afterPartialLoaded = function() {
+ $window._gaq.push(['_trackPageview', $location.path()]);
+ };
+
+ /** stores a cookie that is used by apache to decide which manifest ot send */
+ $scope.enableOffline = function() {
+ //The cookie will be good for one year!
+ var date = new Date();
+ date.setTime(date.getTime()+(365*24*60*60*1000));
+ var expires = "; expires="+date.toGMTString();
+ var value = angular.version.full;
+ document.cookie = OFFLINE_COOKIE_NAME + "="+value+expires+"; path=" + $location.path;
+
+ //force the page to reload so server can serve new manifest file
+ window.location.reload(true);
+ };
+
+
+
+ /**********************************
+ Watches
+ ***********************************/
+
+
+ $scope.$watch(function docsPathWatch() {return $location.path(); }, function docsPathWatchAction(path) {
+ // Strip off leading slash
+ if ( path.charAt(0)==='/' ) {
+ path = path.substr(1);
+ }
+ var currentPage = $scope.currentPage = NG_PAGES[path];
+
+ if ( currentPage ) {
+ $scope.currentArea = currentPage && NG_NAVIGATION[currentPage.area];
+ var pathParts = currentPage.path.split('/');
+ var breadcrumb = $scope.breadcrumb = [];
+ var breadcrumbPath = '';
+ angular.forEach(pathParts, function(part) {
+ breadcrumbPath += part;
+ breadcrumb.push({ name: (NG_PAGES[breadcrumbPath]&&NG_PAGES[breadcrumbPath].name) || part, url: breadcrumbPath });
+ breadcrumbPath += '/';
+ });
+ } else {
+ $scope.currentArea = null;
+ $scope.breadcrumb = [];
+ }
+ });
+
+ /**********************************
+ Initialize
+ ***********************************/
+
+ $scope.versionNumber = angular.version.full;
+ $scope.version = angular.version.full + " " + angular.version.codeName;
+ $scope.subpage = false;
+ $scope.offlineEnabled = ($cookies[OFFLINE_COOKIE_NAME] == angular.version.full);
+ $scope.futurePartialTitle = null;
+ $scope.loading = 0;
+ $scope.URL = URL;
+ $scope.$cookies = $cookies;
+
+ $cookies.platformPreference = $cookies.platformPreference || 'gitUnix';
+
+ if (!$location.path() || INDEX_PATH.test($location.path())) {
+ $location.path('/api').replace();
+ }
+
+ // bind escape to hash reset callback
+ angular.element(window).on('keydown', function(e) {
+ if (e.keyCode === 27) {
+ $scope.$apply(function() {
+ $scope.subpage = false;
+ });
+ }
+ });
+});
diff --git a/docs/app/src/errors.js b/docs/app/src/errors.js
new file mode 100644
index 00000000..b91cfb81
--- /dev/null
+++ b/docs/app/src/errors.js
@@ -0,0 +1,62 @@
+angular.module('errors', ['ngSanitize'])
+
+.filter('errorLink', ['$sanitize', function ($sanitize) {
+ var LINKY_URL_REGEXP = /((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s\.\;\,\(\)\{\}<>]/g,
+ MAILTO_REGEXP = /^mailto:/,
+ STACK_TRACE_REGEXP = /:\d+:\d+$/;
+
+ var truncate = function (text, nchars) {
+ if (text.length > nchars) {
+ return text.substr(0, nchars - 3) + '...';
+ }
+ return text;
+ };
+
+ return function (text, target) {
+ var targetHtml = target ? ' target="' + target + '"' : '';
+
+ if (!text) return text;
+
+ return $sanitize(text.replace(LINKY_URL_REGEXP, function (url) {
+ if (STACK_TRACE_REGEXP.test(url)) {
+ return url;
+ }
+
+ // if we did not match ftp/http/mailto then assume mailto
+ if (!/^((ftp|https?):\/\/|mailto:)/.test(url)) url = 'mailto:' + url;
+
+ return '<a' + targetHtml + ' href="' + url +'">' +
+ truncate(url.replace(MAILTO_REGEXP, ''), 60) +
+ '</a>';
+ }));
+ };
+}])
+
+
+.directive('errorDisplay', ['$location', 'errorLinkFilter', function ($location, errorLinkFilter) {
+ var interpolate = function (formatString) {
+ var formatArgs = arguments;
+ return formatString.replace(/\{\d+\}/g, function (match) {
+ // Drop the braces and use the unary plus to convert to an integer.
+ // The index will be off by one because of the formatString.
+ var index = +match.slice(1, -1);
+ if (index + 1 >= formatArgs.length) {
+ return match;
+ }
+ return formatArgs[index+1];
+ });
+ };
+
+ return {
+ link: function (scope, element, attrs) {
+ var search = $location.search(),
+ formatArgs = [attrs.errorDisplay],
+ i;
+
+ for (i = 0; angular.isDefined(search['p'+i]); i++) {
+ formatArgs.push(search['p'+i]);
+ }
+ element.html(errorLinkFilter(interpolate.apply(null, formatArgs), '_blank'));
+ }
+ };
+}]); \ No newline at end of file
diff --git a/docs/app/src/examples.js b/docs/app/src/examples.js
new file mode 100644
index 00000000..44dd98f1
--- /dev/null
+++ b/docs/app/src/examples.js
@@ -0,0 +1,266 @@
+angular.module('examples', [])
+
+.directive('sourceEdit', function(getEmbeddedTemplate) {
+ return {
+ template: '<div class="btn-group pull-right">' +
+ '<a class="btn dropdown-toggle btn-primary" data-toggle="dropdown" href>' +
+ ' <i class="icon-pencil icon-white"></i> Edit<span class="caret"></span>' +
+ '</a>' +
+ '<ul class="dropdown-menu">' +
+ ' <li><a ng-click="plunkr($event)" href="">In Plunkr</a></li>' +
+ ' <li><a ng-click="fiddle($event)" href="">In JsFiddle</a></li>' +
+ '</ul>' +
+ '</div>',
+ scope: true,
+ controller: function($scope, $attrs, openJsFiddle, openPlunkr) {
+ var sources = {
+ module: $attrs.sourceEdit,
+ deps: read($attrs.sourceEditDeps),
+ html: read($attrs.sourceEditHtml),
+ css: read($attrs.sourceEditCss),
+ js: read($attrs.sourceEditJs),
+ json: read($attrs.sourceEditJson),
+ unit: read($attrs.sourceEditUnit),
+ scenario: read($attrs.sourceEditScenario)
+ };
+ $scope.fiddle = function(e) {
+ e.stopPropagation();
+ openJsFiddle(sources);
+ };
+ $scope.plunkr = function(e) {
+ e.stopPropagation();
+ openPlunkr(sources);
+ };
+ }
+ };
+
+ function read(text) {
+ var files = [];
+ angular.forEach(text ? text.split(' ') : [], function(refId) {
+ // refId is index.html-343, so we need to strip the unique ID when exporting the name
+ files.push({name: refId.replace(/-\d+$/, ''), content: getEmbeddedTemplate(refId)});
+ });
+ return files;
+ }
+})
+
+
+.factory('angularUrls', function($document) {
+ var urls = {};
+
+ angular.forEach($document.find('script'), function(script) {
+ var match = script.src.match(/^.*\/(angular[^\/]*\.js)$/);
+ if (match) {
+ urls[match[1].replace(/(\-\d.*)?(\.min)?\.js$/, '.js')] = match[0];
+ }
+ });
+
+ return urls;
+})
+
+
+.factory('formPostData', function($document) {
+ return function(url, fields) {
+ var form = angular.element('<form style="display: none;" method="post" action="' + url + '" target="_blank"></form>');
+ angular.forEach(fields, function(value, name) {
+ var input = angular.element('<input type="hidden" name="' + name + '">');
+ input.attr('value', value);
+ form.append(input);
+ });
+ $document.find('body').append(form);
+ form[0].submit();
+ form.remove();
+ };
+})
+
+
+.factory('prepareDefaultAppModule', function() {
+ return function(content) {
+ var deps = [];
+ angular.forEach(content.deps, function(file) {
+ if(file.name == 'angular-animate.js') {
+ deps.push('ngAnimate');
+ }
+ });
+
+ var moduleName = 'App';
+ return {
+ module : moduleName,
+ script : "angular.module('" + moduleName + "', [" +
+ (deps.length ? "'" + deps.join("','") + "'" : "") + "]);\n\n"
+ };
+ };
+})
+
+.factory('prepareEditorAssetTags', function(angularUrls) {
+ return function(content, options) {
+ options = options || {};
+ var includeLocalFiles = options.includeLocalFiles;
+ var html = makeScriptTag(angularUrls['angular.js']);
+
+ var allFiles = [].concat(content.js, content.css, content.html, content.json);
+ angular.forEach(content.deps, function(file) {
+ if (file.name !== 'angular.js') {
+ var isLocal = false;
+ for(var i=0;i<allFiles.length;i++) {
+ if(allFiles[i].name == file.name) {
+ isLocal = true;
+ break;
+ }
+ }
+ if(!(isLocal && !includeLocalFiles)) {
+ var assetUrl = angularUrls[file.name] || file.name;
+ html += makeScriptTag(assetUrl);
+ }
+ }
+ });
+
+ if(includeLocalFiles) {
+ angular.forEach(content.css, function(file, index) {
+ html += makeCssLinkTag(file.name);
+ });
+ }
+
+ return html;
+
+
+ function makeScriptTag(src) {
+ return '<script type="text/javascript" src="' + src + '"></script>\n';
+ }
+
+ function makeCssLinkTag(src) {
+ return '<link rel="stylesheet" type="text/css" href="' + src + '" />\n';
+ }
+ };
+})
+
+
+.factory('openPlunkr', function(templateMerge, formPostData, prepareEditorAssetTags, prepareDefaultAppModule) {
+ return function(content) {
+ var hasRouting = false;
+ angular.forEach(content.deps, function(file) {
+ hasRouting = hasRouting || file.name == 'angular-route.js';
+ });
+ var indexHtmlContent = '<!doctype html>\n' +
+ '<html ng-app="{{module}}">\n' +
+ ' <head>\n' +
+ '{{scriptDeps}}';
+
+ if(hasRouting) {
+ indexHtmlContent += '<script type="text/javascript">\n' +
+ '//this is here to make plunkr work with AngularJS routing\n' +
+ 'angular.element(document.getElementsByTagName(\'head\')).append(' +
+ 'angular.element(\'<base href="\' + window.location.pathname + \'" />\')' +
+ ');\n' +
+ '</script>\n';
+ }
+
+ indexHtmlContent += '</head>\n' +
+ ' <body>\n\n' +
+ '{{indexContents}}\n\n' +
+ ' </body>\n' +
+ '</html>\n';
+
+ indexProp = {
+ module: content.module,
+ scriptDeps: prepareEditorAssetTags(content, { includeLocalFiles : true }),
+ indexContents: content.html[0].content
+ };
+
+ var allFiles = [].concat(content.js, content.css, content.html, content.json);
+
+ if(!content.module) {
+ var moduleData = prepareDefaultAppModule(content);
+ indexProp.module = moduleData.module;
+
+ var found = false;
+ angular.forEach(content.js, function(file) {
+ if(file.name == 'script.js') {
+ file.content = moduleData.script + file.content;
+ found = true;
+ }
+ });
+ if(!found) {
+ indexProp.scriptDeps += '<script type="text/javascript" src="script.js"></script>\n';
+ allFiles.push({
+ name : 'script.js',
+ content : moduleData.script
+ });
+ }
+ }
+
+ var postData = {};
+
+ angular.forEach(allFiles, function(file, index) {
+ if (file.content && file.name != 'index.html') {
+ postData['files[' + file.name + ']'] = file.content;
+ }
+ });
+
+ postData['files[index.html]'] = templateMerge(indexHtmlContent, indexProp);
+ postData['tags[]'] = "angularjs";
+
+ postData.private = true;
+ postData.description = 'AngularJS Example Plunkr';
+
+ formPostData('http://plnkr.co/edit/?p=preview', postData);
+ };
+})
+
+.factory('openJsFiddle', function(templateMerge, formPostData, prepareEditorAssetTags, prepareDefaultAppModule) {
+ var HTML = '<div ng-app=\"{{module}}\">\n{{html:2}}</div>',
+ CSS = '</style> <!-- Ugly Hack to make remote files preload in jsFiddle --> \n' +
+ '{{head:0}}<style>{{css}}',
+ SCRIPT = '{{script}}',
+ SCRIPT_CACHE = '\n\n<!-- {{name}} -->\n<script type="text/ng-template" id="{{name}}">\n{{content:2}}</script>',
+ BASE_HREF_TAG = '<!-- Ugly Hack to make AngularJS routing work inside of jsFiddle -->\n' +
+ '<base href="/" />\n\n';
+
+ return function(content) {
+ var prop = {
+ module: content.module,
+ html: '',
+ css: '',
+ script: ''
+ };
+ if(!prop.module) {
+ var moduleData = prepareDefaultAppModule(content);
+ prop.script = moduleData.script;
+ prop.module = moduleData.module;
+ }
+
+ angular.forEach(content.html, function(file, index) {
+ if (index) {
+ prop.html += templateMerge(SCRIPT_CACHE, file);
+ } else {
+ prop.html += file.content;
+ }
+ });
+
+ prop.head = prepareEditorAssetTags(content, { includeLocalFiles : false });
+
+ angular.forEach(content.js, function(file, index) {
+ prop.script += file.content;
+ });
+
+ angular.forEach(content.css, function(file, index) {
+ prop.css += file.content;
+ });
+
+ var hasRouting = false;
+ angular.forEach(content.deps, function(file) {
+ hasRouting = hasRouting || file.name == 'angular-route.js';
+ });
+
+ var compiledHTML = templateMerge(HTML, prop);
+ if(hasRouting) {
+ compiledHTML = BASE_HREF_TAG + compiledHTML;
+ }
+ formPostData("http://jsfiddle.net/api/post/library/pure/", {
+ title: 'AngularJS Example',
+ html: compiledHTML,
+ js: templateMerge(SCRIPT, prop),
+ css: templateMerge(CSS, prop)
+ });
+ };
+}); \ No newline at end of file
diff --git a/docs/app/src/navigationService.js b/docs/app/src/navigationService.js
new file mode 100644
index 00000000..a05f6788
--- /dev/null
+++ b/docs/app/src/navigationService.js
@@ -0,0 +1,24 @@
+angular.module('docsApp.navigationService', [])
+
+.factory('navigationService', function($window) {
+ var service = {
+ currentPage: null,
+ currentVersion: null,
+ changePage: function(newPage) {
+
+ },
+ changeVersion: function(newVersion) {
+
+ //TODO =========
+ // var currentPagePath = '';
+
+ // // preserve URL path when switching between doc versions
+ // if (angular.isObject($rootScope.currentPage) && $rootScope.currentPage.section && $rootScope.currentPage.id) {
+ // currentPagePath = '/' + $rootScope.currentPage.section + '/' + $rootScope.currentPage.id;
+ // }
+
+ // $window.location = version.url + currentPagePath;
+
+ }
+ };
+}); \ No newline at end of file
diff --git a/docs/app/src/search.js b/docs/app/src/search.js
new file mode 100644
index 00000000..9ae18ff6
--- /dev/null
+++ b/docs/app/src/search.js
@@ -0,0 +1,149 @@
+angular.module('search', [])
+
+.controller('DocsSearchCtrl', ['$scope', '$location', 'docsSearch', function($scope, $location, docsSearch) {
+ function clearResults() {
+ $scope.results = [];
+ $scope.colClassName = null;
+ $scope.hasResults = false;
+ }
+
+ $scope.search = function(q) {
+ var MIN_SEARCH_LENGTH = 3;
+ if(q.length >= MIN_SEARCH_LENGTH) {
+ var results = docsSearch(q);
+ var totalAreas = 0;
+ for(var i in results) {
+ ++totalAreas;
+ }
+ if(totalAreas > 0) {
+ $scope.colClassName = 'cols-' + totalAreas;
+ }
+ $scope.hasResults = totalAreas > 0;
+ $scope.results = results;
+ }
+ else {
+ clearResults();
+ }
+ if(!$scope.$$phase) $scope.$apply();
+ };
+ $scope.submit = function() {
+ var result;
+ for(var i in $scope.results) {
+ result = $scope.results[i][0];
+ if(result) {
+ break;
+ }
+ }
+ if(result) {
+ $location.path(result.url);
+ $scope.hideResults();
+ }
+ };
+ $scope.hideResults = function() {
+ clearResults();
+ $scope.q = '';
+ };
+}])
+
+.factory('lunrSearch', function() {
+ return function(properties) {
+ if (window.RUNNING_IN_NG_TEST_RUNNER) return null;
+
+ var engine = lunr(properties);
+ return {
+ store : function(values) {
+ engine.add(values);
+ },
+ search : function(q) {
+ return engine.search(q);
+ }
+ };
+ };
+})
+
+.factory('docsSearch', ['$rootScope','lunrSearch', 'NG_PAGES',
+ function($rootScope, lunrSearch, NG_PAGES) {
+ if (window.RUNNING_IN_NG_TEST_RUNNER) {
+ return null;
+ }
+
+ var index = lunrSearch(function() {
+ this.ref('id');
+ this.field('title', {boost: 50});
+ this.field('keywords', { boost : 20 });
+ });
+
+ angular.forEach(NG_PAGES, function(page, key) {
+ if(page.searchTerms) {
+ index.store({
+ id : key,
+ title : page.searchTerms.titleWords,
+ keywords : page.searchTerms.keywords
+ });
+ };
+ });
+
+ return function(q) {
+ var results = {
+ api : [],
+ tutorial : [],
+ guide : [],
+ error : [],
+ misc : []
+ };
+ angular.forEach(index.search(q), function(result) {
+ var key = result.ref;
+ var item = NG_PAGES[key];
+ var area = item.area;
+ item.path = key;
+
+ var limit = area == 'api' ? 40 : 14;
+ if(results[area].length < limit) {
+ results[area].push(item);
+ }
+ });
+ return results;
+ };
+}])
+
+.directive('focused', function($timeout) {
+ return function(scope, element, attrs) {
+ element[0].focus();
+ element.on('focus', function() {
+ scope.$apply(attrs.focused + '=true');
+ });
+ element.on('blur', function() {
+ // have to use $timeout, so that we close the drop-down after the user clicks,
+ // otherwise when the user clicks we process the closing before we process the click.
+ $timeout(function() {
+ scope.$eval(attrs.focused + '=false');
+ });
+ });
+ scope.$eval(attrs.focused + '=true');
+ };
+})
+
+.directive('docsSearchInput', ['$document',function($document) {
+ return function(scope, element, attrs) {
+ var ESCAPE_KEY_KEYCODE = 27,
+ FORWARD_SLASH_KEYCODE = 191;
+ angular.element($document[0].body).bind('keydown', function(event) {
+ var input = element[0];
+ if(event.keyCode == FORWARD_SLASH_KEYCODE && document.activeElement != input) {
+ event.stopPropagation();
+ event.preventDefault();
+ input.focus();
+ }
+ });
+
+ element.bind('keydown', function(event) {
+ if(event.keyCode == ESCAPE_KEY_KEYCODE) {
+ event.stopPropagation();
+ event.preventDefault();
+ scope.$apply(function() {
+ scope.hideResults();
+ });
+ }
+ });
+ };
+}]);
diff --git a/docs/app/src/tutorials.js b/docs/app/src/tutorials.js
new file mode 100644
index 00000000..a978daed
--- /dev/null
+++ b/docs/app/src/tutorials.js
@@ -0,0 +1,58 @@
+angular.module('tutorials', [])
+
+.directive('docTutorialNav', function(templateMerge) {
+ var pages = [
+ '',
+ 'step_00', 'step_01', 'step_02', 'step_03', 'step_04',
+ 'step_05', 'step_06', 'step_07', 'step_08', 'step_09',
+ 'step_10', 'step_11', 'step_12', 'the_end'
+ ];
+ return {
+ compile: function(element, attrs) {
+ var seq = 1 * attrs.docTutorialNav,
+ props = {
+ seq: seq,
+ prev: pages[seq],
+ next: pages[2 + seq],
+ diffLo: seq ? (seq - 1): '0~1',
+ diffHi: seq
+ };
+
+ element.addClass('btn-group');
+ element.addClass('tutorial-nav');
+ element.append(templateMerge(
+ '<a href="tutorial/{{prev}}"><li class="btn btn-primary"><i class="icon-step-backward"></i> Previous</li></a>\n' +
+ '<a href="http://angular.github.com/angular-phonecat/step-{{seq}}/app"><li class="btn btn-primary"><i class="icon-play"></i> Live Demo</li></a>\n' +
+ '<a href="https://github.com/angular/angular-phonecat/compare/step-{{diffLo}}...step-{{diffHi}}"><li class="btn btn-primary"><i class="icon-search"></i> Code Diff</li></a>\n' +
+ '<a href="tutorial/{{next}}"><li class="btn btn-primary">Next <i class="icon-step-forward"></i></li></a>', props));
+ }
+ };
+})
+
+
+.directive('docTutorialReset', function() {
+ function tab(name, command, id, step) {
+ return '' +
+ ' <div class=\'tab-pane well\' title="' + name + '" value="' + id + '">\n' +
+ ' <ol>\n' +
+ ' <li><p>Reset the workspace to step ' + step + '.</p>' +
+ ' <pre>' + command + '</pre></li>\n' +
+ ' <li><p>Refresh your browser or check the app out on <a href="http://angular.github.com/angular-phonecat/step-' + step + '/app">Angular\'s server</a>.</p></li>\n' +
+ ' </ol>\n' +
+ ' </div>\n';
+ }
+
+ return {
+ compile: function(element, attrs) {
+ var step = attrs.docTutorialReset;
+ element.html(
+ '<div ng-hide="show">' +
+ '<p><a href="" ng-click="show=true;$event.stopPropagation()">Workspace Reset Instructions ➤</a></p>' +
+ '</div>\n' +
+ '<div class="tabbable" ng-show="show" ng-model="$cookies.platformPreference">\n' +
+ tab('Git on Mac/Linux', 'git checkout -f step-' + step, 'gitUnix', step) +
+ tab('Git on Windows', 'git checkout -f step-' + step, 'gitWin', step) +
+ '</div>\n');
+ }
+ };
+}); \ No newline at end of file
diff --git a/docs/app/src/versions.js b/docs/app/src/versions.js
new file mode 100644
index 00000000..701f41d3
--- /dev/null
+++ b/docs/app/src/versions.js
@@ -0,0 +1,15 @@
+angular.module('versions', [])
+
+.controller('DocsVersionsCtrl', ['$scope', '$location', '$window', 'NG_VERSIONS', function($scope, $location, $window, NG_VERSIONS) {
+ $scope.docs_versions = NG_VERSIONS;
+ $scope.docs_version = NG_VERSIONS[0];
+
+ $scope.jumpToDocsVersion = function(version) {
+ var currentPagePath = $location.path();
+
+ // TODO: We need to do some munging of the path for different versions of the API...
+
+
+ $window.location = version.docsUrl + currentPagePath;
+ };
+}]); \ No newline at end of file