var docsApp = {
controller: {},
directive: {},
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', '$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) {
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) {
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;
if(section == 'cookbook') {
section = 'tutorial';
}
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 = function() {
return function(scope, element, attrs) {
var ESCAPE_KEY_KEYCODE = 27;
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: '
',
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.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', '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' +
' Reset the workspace to step ' + step + '.
' +
' ' + command + '
\n' +
' Refresh your browser or check the app out on Angular\'s server.
\n' +
'
\n' +
'
\n';
}
return {
compile: function(element, attrs) {
var step = attrs.docTutorialReset;
element.html(
'\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.directive.errorDisplay = ['$location', function ($location) {
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; search['p'+i]; i++) {
formatArgs.push(search['p'+i]);
}
element.text(interpolate.apply(null, formatArgs));
}
};
}];
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.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) {
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 postData = {};
var allFiles = [].concat(content.js, content.css, content.html, content.json);
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) {
var HTML = '\n{{html:2}}
',
CSS = ' \n' +
'{{head:0}}