diff options
Diffstat (limited to 'docs/app/src')
| -rw-r--r-- | docs/app/src/directives.js | 35 | ||||
| -rw-r--r-- | docs/app/src/docs.js | 128 | ||||
| -rw-r--r-- | docs/app/src/errors.js | 62 | ||||
| -rw-r--r-- | docs/app/src/examples.js | 266 | ||||
| -rw-r--r-- | docs/app/src/navigationService.js | 24 | ||||
| -rw-r--r-- | docs/app/src/search.js | 149 | ||||
| -rw-r--r-- | docs/app/src/tutorials.js | 58 | ||||
| -rw-r--r-- | docs/app/src/versions.js | 15 |
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 |
