var docsApp = { controller: {}, directive: {}, serviceFactory: {}, filter: {} }; docsApp.controller.DocsVersionsCtrl = ['$scope', '$rootScope', '$window', 'NG_VERSIONS', 'NG_VERSION', function($scope, $rootScope, $window, NG_VERSIONS, NG_VERSION) { $scope.docs_versions = NG_VERSIONS; $scope.docs_version = NG_VERSIONS[0]; $scope.jumpToDocsVersion = function(version) { 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; }; }]; docsApp.controller.DocsNavigationCtrl = ['$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 totalSections = 0; for(var i in results) { ++totalSections; } if(totalSections > 0) { $scope.colClassName = 'cols-' + totalSections; } $scope.hasResults = totalSections > 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 = ''; }; }]; docsApp.serviceFactory.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); } }; }; }; docsApp.serviceFactory.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('description', { boost : 20 }); }); angular.forEach(NG_PAGES, function(page, i) { var title = page.shortName; if(title.charAt(0) == 'n' && title.charAt(1) == 'g') { title = title + ' ' + title.charAt(2).toLowerCase() + title.substr(3); } index.store({ id: i, title: title, description: page.keywords }); }); return function(q) { var results = {}; angular.forEach(index.search(q), function(result) { var item = NG_PAGES[result.ref]; var section = item.section; results[section] = results[section] || []; if(results[section].length < 15) { results[section].push(item); } }); return results; }; }]; docsApp.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'); }; }; docsApp.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) { if(event.keyCode == FORWARD_SLASH_KEYCODE && document.activeElement) { var activeElement = document.activeElement; var activeTagName = activeElement.nodeName.toLowerCase(); var hasInputFocus = activeTagName == 'input' || activeTagName == 'select' || activeTagName == 'option' || activeTagName == 'textarea' || activeElement.hasAttribute('contenteditable'); if(!hasInputFocus) { event.stopPropagation(); event.preventDefault(); var input = element[0]; input.focus(); } } }); element.bind('keydown', function(event) { if(event.keyCode == ESCAPE_KEY_KEYCODE) { event.stopPropagation(); event.preventDefault(); scope.$apply(function() { scope.hideResults(); }); } }); }; }]; docsApp.directive.code = function() { return { restrict:'E', terminal: true }; }; docsApp.directive.sourceEdit = function(getEmbeddedTemplate) { return { template: '
' + '' + ' Edit' + '' + '' + '
', 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; } }; docsApp.directive.docModuleComponents = function() { return { template: '
' + '

Module Components

' + '
' + '

{{ section.title }}

' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '
NameDescription
{{ component.shortName }}{{ component.shortDescription }}
' + '
' + '
', scope : { module : '@docModuleComponents' }, controller : ['$scope', '$anchorScroll', '$timeout', 'sections', function($scope, $anchorScroll, $timeout, sections) { var validTypes = ['property','function','directive','service','object','filter']; var components = {}; angular.forEach(sections.api, function(item) { if(item.moduleName == $scope.module) { var type = item.type; if(type == 'object') type = 'service'; if(validTypes.indexOf(type) >= 0) { components[type] = components[type] || { title : type, type : type, components : [] }; components[type].components.push(item); } } }); $scope.components = components; $timeout($anchorScroll, 0, false); }] }; }; docsApp.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( '
  • Previous
  • \n' + '
  • Live Demo
  • \n' + '
  • Code Diff
  • \n' + '
  • Next
  • ', props)); } }; }; docsApp.directive.docTutorialReset = function() { function tab(name, command, id, step) { return '' + '
    \n' + '
      \n' + '
    1. Reset the workspace to step ' + step + '.

      ' + '
      ' + command + '
    2. \n' + '
    3. Refresh your browser or check the app out on Angular\'s server.

    4. \n' + '
    \n' + '
    \n'; } return { compile: function(element, attrs) { var step = attrs.docTutorialReset; element.html( '
    ' + '

    Workspace Reset Instructions ➤

    ' + '
    \n' + '
    \n' + tab('Git on Mac/Linux', 'git checkout -f step-' + step, 'gitUnix', step) + tab('Git on Windows', 'git checkout -f step-' + step, 'gitWin', step) + '
    \n'); } }; }; docsApp.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 '' + truncate(url.replace(MAILTO_REGEXP, ''), 60) + ''; })); }; }]; docsApp.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')); } }; }]; /** * backToTop Directive * @param {Function} $anchorScroll * * @description Ensure that the browser scrolls when the anchor is clicked */ docsApp.directive.backToTop = ['$anchorScroll', function($anchorScroll) { return function link(scope, element) { element.on('click', function(event) { scope.$apply($anchorScroll); }); }; }]; docsApp.serviceFactory.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; }; docsApp.serviceFactory.formPostData = function($document) { return function(url, fields) { var form = angular.element('
    '); angular.forEach(fields, function(value, name) { var input = angular.element(''); input.attr('value', value); form.append(input); }); $document.find('body').append(form); form[0].submit(); form.remove(); }; }; docsApp.serviceFactory.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" }; }; }; docsApp.serviceFactory.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\n'; }; function makeCssLinkTag(src) { return '\n'; }; }; }; docsApp.serviceFactory.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 = '\n' + '\n' + ' \n' + '{{scriptDeps}}'; if(hasRouting) { indexHtmlContent += '\n'; } indexHtmlContent += '\n' + ' \n\n' + '{{indexContents}}\n\n' + ' \n' + '\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 += '\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); }; }; docsApp.serviceFactory.openJsFiddle = function(templateMerge, formPostData, prepareEditorAssetTags, prepareDefaultAppModule) { var HTML = '
    \n{{html:2}}
    ', CSS = ' \n' + '{{head:0}}