mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-17 15:40:22 +00:00
1407 lines
43 KiB
JavaScript
1407 lines
43 KiB
JavaScript
/**
|
|
* All parsing/transformation code goes here. All code here should be sync to ease testing.
|
|
*/
|
|
var DOM = require('./dom.js').DOM;
|
|
var htmlEscape = require('./dom.js').htmlEscape;
|
|
var Example = require('./example.js').Example;
|
|
var NEW_LINE = /\n\r?/;
|
|
var globalID = 0;
|
|
var fs = require('fs');
|
|
var fspath = require('path');
|
|
var shell = require('shelljs');
|
|
var gruntUtil = require('../../lib/grunt/utils.js');
|
|
var errorsJson;
|
|
var marked = require('marked');
|
|
marked.setOptions({
|
|
gfm: true,
|
|
tables: true
|
|
});
|
|
|
|
var lookupMinerrMsg = function (doc) {
|
|
var code, namespace;
|
|
|
|
if (errorsJson === undefined) {
|
|
errorsJson = require('../../build/errors.json').errors;
|
|
}
|
|
|
|
namespace = doc.getMinerrNamespace();
|
|
code = doc.getMinerrCode();
|
|
if (namespace === undefined) {
|
|
return errorsJson[code];
|
|
}
|
|
return errorsJson[namespace][code];
|
|
};
|
|
|
|
exports.trim = trim;
|
|
exports.metadata = metadata;
|
|
exports.scenarios = scenarios;
|
|
exports.merge = merge;
|
|
exports.checkBrokenLinks = checkBrokenLinks;
|
|
exports.Doc = Doc;
|
|
|
|
exports.ngVersions = function() {
|
|
var versions = [], regex = /^v([1-9]\d*(?:\.\d+\S+)+)$/; //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]);
|
|
}
|
|
});
|
|
|
|
//match the future version of AngularJS that is set in the package.json file
|
|
return expandVersions(sortVersionsNatrually(versions), exports.ngCurrentVersion().full);
|
|
|
|
function expandVersions(versions, latestVersion) {
|
|
var RC_VERSION = /rc\d/;
|
|
//copy the array to avoid changing the versions param data
|
|
//the latest version is not on the git tags list, but
|
|
//docs.angularjs.org will always point to master as of 1.2
|
|
versions = versions.concat([latestVersion]);
|
|
|
|
var firstUnstable, expanded = [];
|
|
for(var i=versions.length-1;i>=0;i--) {
|
|
var version = versions[i],
|
|
split = version.split('.'),
|
|
isMaster = version == latestVersion,
|
|
isStable = split[1] % 2 === 0 && !RC_VERSION.test(version);
|
|
|
|
var title = 'AngularJS - v' + version;
|
|
|
|
var docsPath = version < '1.0.2' ? 'docs-' + version : 'docs';
|
|
|
|
var url = isMaster ?
|
|
'http://docs.angularjs.org' :
|
|
'http://code.angularjs.org/' + version + '/' + docsPath;
|
|
|
|
expanded.push({
|
|
version : version,
|
|
stable : isStable,
|
|
title : title,
|
|
group : (isStable ? 'Stable' : 'Unstable'),
|
|
url : url
|
|
});
|
|
};
|
|
|
|
return expanded;
|
|
};
|
|
|
|
function sortVersionsNatrually(versions) {
|
|
var versionMap = {},
|
|
NON_RC_RELEASE_NUMBER = 999;
|
|
for(var i = versions.length - 1; i >= 0; i--) {
|
|
var version = versions[i];
|
|
var split = version.split(/\.|rc/);
|
|
var baseVersion = split[0] + '.' + split[1] + '.' + split[2];
|
|
|
|
//create a map of RC versions for each version
|
|
//this way each RC version can be sorted in "natural" order
|
|
versionMap[baseVersion] = versionMap[baseVersion] || [];
|
|
|
|
//NON_RC_RELEASE_NUMBER is used to signal the non-RC version for the release and
|
|
//it will always appear at the top of the list since the number is so high!
|
|
versionMap[baseVersion].push(
|
|
version == baseVersion ? NON_RC_RELEASE_NUMBER : parseInt(version.match(/rc\.?(\d+)/)[1]));
|
|
};
|
|
|
|
//flatten the map so that the RC versions occur in a natural sorted order
|
|
//and the official non-RC version shows up at the top of the list of sorted
|
|
//RC versions!
|
|
var angularVersions = [];
|
|
sortedKeys(versionMap).forEach(function(key) {
|
|
var versions = versionMap[key];
|
|
|
|
//basic numerical sort
|
|
versions.sort(function(a,b) {
|
|
return a - b;
|
|
});
|
|
|
|
versions.forEach(function(v) {
|
|
angularVersions.push(v == NON_RC_RELEASE_NUMBER ? key : key + 'rc' + v);
|
|
});
|
|
});
|
|
|
|
return angularVersions;
|
|
};
|
|
|
|
function sortedKeys(obj) {
|
|
var keys = [];
|
|
for(var key in obj) {
|
|
keys.push(key);
|
|
};
|
|
keys.sort(true);
|
|
return keys;
|
|
};
|
|
};
|
|
|
|
exports.ngCurrentVersion = function() {
|
|
return gruntUtil.getVersion();
|
|
};
|
|
|
|
var BOOLEAN_ATTR = {};
|
|
['multiple', 'selected', 'checked', 'disabled', 'readOnly', 'required'].forEach(function(value) {
|
|
BOOLEAN_ATTR[value] = true;
|
|
});
|
|
|
|
//////////////////////////////////////////////////////////
|
|
function Doc(text, file, line) {
|
|
if (typeof text == 'object') {
|
|
for ( var key in text) {
|
|
this[key] = text[key];
|
|
}
|
|
} else {
|
|
this.text = text;
|
|
this.file = file;
|
|
this.line = line;
|
|
}
|
|
this.scenarios = this.scenarios || [];
|
|
this.requires = this.requires || [];
|
|
this.param = this.param || [];
|
|
this.properties = this.properties || [];
|
|
this.methods = this.methods || [];
|
|
this.events = this.events || [];
|
|
this.links = this.links || [];
|
|
this.anchors = this.anchors || [];
|
|
}
|
|
Doc.METADATA_IGNORE = (function() {
|
|
var words = fs.readFileSync(__dirname + '/ignore.words', 'utf8');
|
|
return words.toString().split(/[,\s\n\r]+/gm);
|
|
})();
|
|
|
|
|
|
Doc.prototype = {
|
|
keywords: function keywords() {
|
|
var keywords = {};
|
|
var words = [];
|
|
Doc.METADATA_IGNORE.forEach(function(ignore){ keywords[ignore] = true; });
|
|
|
|
function extractWords(text) {
|
|
var tokens = text.toLowerCase().split(/[\.\s,`'"#]+/mg);
|
|
tokens.forEach(function(key){
|
|
var match = key.match(/^((ng:|[\$_a-z])[\w\-_]+)/);
|
|
if (match){
|
|
key = match[1];
|
|
if (!keywords[key]) {
|
|
keywords[key] = true;
|
|
words.push(key);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
extractWords(this.text);
|
|
this.properties.forEach(function(prop) {
|
|
extractWords(prop.text || prop.description || '');
|
|
});
|
|
this.methods.forEach(function(method) {
|
|
extractWords(method.text || method.description || '');
|
|
});
|
|
if (this.ngdoc === 'error') {
|
|
words.push(this.getMinerrNamespace());
|
|
words.push(this.getMinerrCode());
|
|
}
|
|
words.sort();
|
|
return words.join(' ');
|
|
},
|
|
|
|
shortDescription : function() {
|
|
if (!this.description) return this.description;
|
|
var text = this.description.split("\n")[0];
|
|
text = text.replace(/<.+?\/?>/g, '');
|
|
text = text.replace(/{/g,'{');
|
|
text = text.replace(/}/g,'}');
|
|
return text;
|
|
},
|
|
|
|
getMinerrNamespace: function () {
|
|
if (this.ngdoc !== 'error') {
|
|
throw new Error('Tried to get the minErr namespace, but @ngdoc ' +
|
|
this.ngdoc + ' was supplied. It should be @ngdoc error');
|
|
}
|
|
return this.name.split(':')[0];
|
|
},
|
|
|
|
getMinerrCode: function () {
|
|
if (this.ngdoc !== 'error') {
|
|
throw new Error('Tried to get the minErr error code, but @ngdoc ' +
|
|
this.ngdoc + ' was supplied. It should be @ngdoc error');
|
|
}
|
|
return this.name.split(':')[1];
|
|
},
|
|
|
|
/**
|
|
* Converts relative urls (without section) into absolute
|
|
* Absolute url means url with section
|
|
*
|
|
* @example
|
|
* - if the link is inside any api doc:
|
|
* angular.widget -> api/angular.widget
|
|
*
|
|
* - if the link is inside any guid doc:
|
|
* intro -> guide/intro
|
|
*
|
|
* @param {string} url Absolute or relative url
|
|
* @returns {string} Absolute url
|
|
*/
|
|
convertUrlToAbsolute: function(url) {
|
|
var hashIdx = url.indexOf('#');
|
|
|
|
// Lowercase hash parts of the links,
|
|
// so that we can keep correct API names even when the urls are lowercased.
|
|
if (hashIdx !== -1) {
|
|
url = url.substr(0, hashIdx) + url.substr(hashIdx).toLowerCase();
|
|
}
|
|
|
|
if (url.substr(-1) == '/') return url + 'index';
|
|
if (url.match(/\//)) return url;
|
|
return this.section + '/' + url;
|
|
},
|
|
|
|
markdown: function(text) {
|
|
if (!text) return text;
|
|
|
|
var self = this,
|
|
IS_URL = /^(https?:\/\/|ftps?:\/\/|mailto:|\.|\/)/,
|
|
IS_ANGULAR = /^(api\/)?(angular|ng|AUTO)\./,
|
|
IS_HASH = /^#/,
|
|
parts = trim(text).split(/(<pre.*?>[\s\S]*?<\/pre>|<doc:example(\S*).*?>[\s\S]*?<\/doc:example>|<example[^>]*>[\s\S]*?<\/example>)/),
|
|
seq = 0,
|
|
placeholderMap = {};
|
|
|
|
function placeholder(text) {
|
|
var id = 'REPLACEME' + (seq++);
|
|
placeholderMap[id] = text;
|
|
return id;
|
|
}
|
|
|
|
function extractInlineDocCode(text, tag) {
|
|
if(tag == 'all') {
|
|
//use a greedy operator to match the last </docs> tag
|
|
regex = /\/\/<docs.*?>([.\s\S]+)\/\/<\/docs>/im;
|
|
}
|
|
else {
|
|
//use a non-greedy operator to match the next </docs> tag
|
|
regex = new RegExp("\/\/<docs\\s*tag=\"" + tag + "\".*?>([.\\s\\S]+?)\/\/<\/docs>","im");
|
|
}
|
|
var matches = regex.exec(text.toString());
|
|
return matches && matches.length > 1 ? matches[1] : "";
|
|
}
|
|
|
|
parts.forEach(function(text, i) {
|
|
parts[i] = (text || '').
|
|
replace(/<example(?:\s+module="([^"]*)")?(?:\s+deps="([^"]*)")?(\s+animations="true")?>([\s\S]*?)<\/example>/gmi,
|
|
function(_, module, deps, animations, content) {
|
|
|
|
var example = new Example(self.scenarios);
|
|
if(animations) {
|
|
example.enableAnimations();
|
|
example.addDeps('angular-animate.js');
|
|
}
|
|
|
|
example.setModule(module);
|
|
example.addDeps(deps);
|
|
content.replace(/<file\s+name="([^"]*)"\s*>([\s\S]*?)<\/file>/gmi, function(_, name, content) {
|
|
example.addSource(name, content);
|
|
});
|
|
content.replace(/<file\s+src="([^"]+)"(?:\s+tag="([^"]+)")?(?:\s+name="([^"]+)")?\s*\/?>/gmi, function(_, file, tag, name) {
|
|
if(fs.existsSync(file)) {
|
|
var content = fs.readFileSync(file, 'utf8');
|
|
if(content && content.length > 0) {
|
|
if(tag && tag.length > 0) {
|
|
content = extractInlineDocCode(content, tag);
|
|
}
|
|
name = name && name.length > 0 ? name : fspath.basename(file);
|
|
example.addSource(name, content);
|
|
}
|
|
}
|
|
return '';
|
|
})
|
|
return placeholder(example.toHtml());
|
|
}).
|
|
replace(/(?:\*\s+)?<file.+?src="([^"]+)"(?:\s+tag="([^"]+)")?\s*\/?>/i, function(_, file, tag) {
|
|
if(fs.existsSync(file)) {
|
|
var content = fs.readFileSync(file, 'utf8');
|
|
if(tag && tag.length > 0) {
|
|
content = extractInlineDocCode(content, tag);
|
|
}
|
|
return content;
|
|
}
|
|
}).
|
|
replace(/^<doc:example(\s+[^>]*)?>([\s\S]*)<\/doc:example>/mi, function(_, attrs, content) {
|
|
var html, script, scenario,
|
|
example = new Example(self.scenarios);
|
|
|
|
example.setModule((attrs||'module=""').match(/^\s*module=["'](.*)["']\s*$/)[1]);
|
|
content.
|
|
replace(/<doc:source(\s+[^>]*)?>([\s\S]*)<\/doc:source>/mi, function(_, attrs, content) {
|
|
example.addSource('index.html', content.
|
|
replace(/<script>([\s\S]*)<\/script>/mi, function(_, script) {
|
|
example.addSource('script.js', script);
|
|
return '';
|
|
}).
|
|
replace(/<style>([\s\S]*)<\/style>/mi, function(_, style) {
|
|
example.addSource('style.css', style);
|
|
return '';
|
|
})
|
|
);
|
|
}).
|
|
replace(/(<doc:scenario>)([\s\S]*)(<\/doc:scenario>)/mi, function(_, before, content){
|
|
example.addSource('scenario.js', content);
|
|
});
|
|
|
|
return placeholder(example.toHtml());
|
|
}).
|
|
replace(/^<pre(.*?)>([\s\S]*?)<\/pre>/mi, function(_, attrs, content){
|
|
return placeholder(
|
|
'<pre'+attrs+' class="prettyprint linenums">' +
|
|
content.replace(/</g, '<').replace(/>/g, '>') +
|
|
'</pre>');
|
|
}).
|
|
replace(/<div([^>]*)><\/div>/, '<div$1>\n<\/div>').
|
|
replace(/{@link\s+([^\s}]+)\s*([^}]*?)\s*}/g, function(_all, url, title){
|
|
var isFullUrl = url.match(IS_URL),
|
|
isAngular = url.match(IS_ANGULAR),
|
|
isHash = url.match(IS_HASH),
|
|
absUrl = isHash
|
|
? url
|
|
: (isFullUrl ? url : self.convertUrlToAbsolute(url));
|
|
|
|
if (!isFullUrl) self.links.push(absUrl);
|
|
|
|
return '<a href="' + absUrl + '">' +
|
|
(isAngular ? '<code>' : '') +
|
|
(title || url).replace(/^#/g, '').replace(/\n/g, ' ') +
|
|
(isAngular ? '</code>' : '') +
|
|
'</a>';
|
|
}).
|
|
replace(/{@type\s+(\S+)(?:\s+(\S+))?}/g, function(_, type, url) {
|
|
url = url || '#';
|
|
return '<a href="' + url + '" class="' + self.prepare_type_hint_class_name(type) + '">' + type + '</a>';
|
|
}).
|
|
replace(/{@installModule\s+(\S+)?}/g, function(_, module) {
|
|
return explainModuleInstallation(module);
|
|
});
|
|
});
|
|
text = parts.join('');
|
|
|
|
function prepareClassName(text) {
|
|
return text.toLowerCase().replace(/[_\W]+/g, '-');
|
|
};
|
|
|
|
var pageClassName, suffix = '-page';
|
|
if(this.name) {
|
|
var split = this.name.match(/^\s*(.+?)\s*:\s*(.+)/);
|
|
if(split && split.length > 1) {
|
|
var before = prepareClassName(split[1]);
|
|
var after = prepareClassName(split[2]);
|
|
pageClassName = before + suffix + ' ' + before + '-' + after + suffix;
|
|
}
|
|
}
|
|
pageClassName = pageClassName || prepareClassName(this.name || 'docs') + suffix;
|
|
|
|
text = '<div class="' + pageClassName + '">' +
|
|
marked(text) +
|
|
'</div>';
|
|
text = text.replace(/(?:<p>)?(REPLACEME\d+)(?:<\/p>)?/g, function(_, id) {
|
|
return placeholderMap[id];
|
|
});
|
|
|
|
//!annotate CONTENT
|
|
//!annotate="REGEX" CONTENT
|
|
//!annotate="REGEX" TITLE|CONTENT
|
|
text = text.replace(/\n?\/\/!annotate\s*(?:=\s*['"](.+?)['"])?\s+(.+?)\n\s*(.+?\n)/img,
|
|
function(_, pattern, content, line) {
|
|
var pattern = new RegExp(pattern || '.+');
|
|
var title, text, split = content.split(/\|/);
|
|
if(split.length > 1) {
|
|
text = split[1];
|
|
title = split[0];
|
|
}
|
|
else {
|
|
title = 'Info';
|
|
text = content;
|
|
}
|
|
return "\n" + line.replace(pattern, function(match) {
|
|
return '<div class="nocode nocode-content" data-popover ' +
|
|
'data-content="' + text + '" ' +
|
|
'data-title="' + title + '">' +
|
|
match +
|
|
'</div>';
|
|
});
|
|
}
|
|
);
|
|
|
|
//!details /path/to/local/docs/file.html
|
|
//!details="REGEX" /path/to/local/docs/file.html
|
|
text = text.replace(/\/\/!details\s*(?:=\s*['"](.+?)['"])?\s+(.+?)\n\s*(.+?\n)/img,
|
|
function(_, pattern, url, line) {
|
|
url = '/notes/' + url;
|
|
var pattern = new RegExp(pattern || '.+');
|
|
return line.replace(pattern, function(match) {
|
|
return '<div class="nocode nocode-content" data-foldout data-url="' + url + '">' + match + '</div>';
|
|
});
|
|
}
|
|
);
|
|
|
|
return text;
|
|
},
|
|
|
|
parse: function() {
|
|
var atName;
|
|
var atText;
|
|
var match;
|
|
var self = this;
|
|
self.text.split(NEW_LINE).forEach(function(line){
|
|
if ((match = line.match(/^\s*@(\w+)(\s+(.*))?/))) {
|
|
// we found @name ...
|
|
// if we have existing name
|
|
flush();
|
|
atName = match[1];
|
|
atText = [];
|
|
if(match[3]) atText.push(match[3]);
|
|
} else {
|
|
if (atName) {
|
|
atText.push(line);
|
|
}
|
|
}
|
|
});
|
|
flush();
|
|
this.shortName = this.name.split(/[\.:#]/).pop().trim();
|
|
this.id = this.id || // if we have an id just use it
|
|
(this.ngdoc === 'error' ? this.name : '') ||
|
|
(((this.file||'').match(/.*(\/|\\)([^(\/|\\)]*)\.ngdoc/)||{})[2]) || // try to extract it from file name
|
|
this.name; // default to name
|
|
this.moduleName = parseModuleName(this.id);
|
|
this.description = this.markdown(this.description);
|
|
this.example = this.markdown(this.example);
|
|
this['this'] = this.markdown(this['this']);
|
|
return this;
|
|
|
|
function parseModuleName(id) {
|
|
var module = id.split('.')[0];
|
|
if(module == 'angular') {
|
|
module = 'ng';
|
|
}
|
|
return module;
|
|
}
|
|
|
|
function flush() {
|
|
if (atName) {
|
|
var text = trim(atText.join('\n')), match;
|
|
if (atName == 'param') {
|
|
match = text.match(/^\{([^}]+)\}\s+(([^\s=]+)|\[(\S+)=([^\]]+)\])\s+(.*)/);
|
|
// 1 1 23 3 4 4 5 5 2 6 6
|
|
if (!match) {
|
|
throw new Error("Not a valid 'param' format: " + text + ' (found in: ' + self.file + ':' + self.line + ')');
|
|
}
|
|
|
|
var optional = (match[1].slice(-1) === '=');
|
|
var param = {
|
|
name: match[4] || match[3],
|
|
description:self.markdown(text.replace(match[0], match[6])),
|
|
type: optional ? match[1].substring(0, match[1].length-1) : match[1],
|
|
optional: optional,
|
|
default: match[5]
|
|
};
|
|
self.param.push(param);
|
|
} else if (atName == 'returns' || atName == 'return') {
|
|
match = text.match(/^\{([^}]+)\}\s+(.*)/);
|
|
if (!match) {
|
|
throw new Error("Not a valid 'returns' format: " + text + ' (found in: ' + self.file + ':' + self.line + ')');
|
|
}
|
|
self.returns = {
|
|
type: match[1],
|
|
description: self.markdown(text.replace(match[0], match[2]))
|
|
};
|
|
} else if(atName == 'requires') {
|
|
match = text.match(/^([^\s]*)\s*([\S\s]*)/);
|
|
self.requires.push({
|
|
name: match[1],
|
|
text: self.markdown(match[2])
|
|
});
|
|
} else if(atName == 'property') {
|
|
match = text.match(/^\{(\S+)\}\s+(\S+)(\s+(.*))?/);
|
|
if (!match) {
|
|
throw new Error("Not a valid 'property' format: " + text + ' (found in: ' + self.file + ':' + self.line + ')');
|
|
}
|
|
var property = new Doc({
|
|
type: match[1],
|
|
name: match[2],
|
|
shortName: match[2],
|
|
description: self.markdown(text.replace(match[0], match[4]))
|
|
});
|
|
self.properties.push(property);
|
|
} else if(atName == 'eventType') {
|
|
match = text.match(/^([^\s]*)\s+on\s+([\S\s]*)/);
|
|
self.type = match[1];
|
|
self.target = match[2];
|
|
} else {
|
|
self[atName] = text;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
html: function() {
|
|
var dom = new DOM(),
|
|
self = this,
|
|
minerrMsg;
|
|
|
|
var gitTagFromFullVersion = function(version) {
|
|
var match = version.match(/-(\w{7})/);
|
|
|
|
if (match) {
|
|
// git sha
|
|
return match[1];
|
|
}
|
|
|
|
// git tag
|
|
return 'v' + version;
|
|
};
|
|
|
|
if (this.section === 'api') {
|
|
dom.tag('a', {
|
|
href: 'http://github.com/angular/angular.js/tree/' +
|
|
gitTagFromFullVersion(gruntUtil.getVersion().full) + '/' + self.file + '#L' + self.line,
|
|
class: 'view-source btn btn-action' }, function(dom) {
|
|
dom.tag('i', {class:'icon-zoom-in'}, ' ');
|
|
dom.text(' View source');
|
|
});
|
|
}
|
|
dom.tag('a', {
|
|
href: 'http://github.com/angular/angular.js/edit/master/' + self.file,
|
|
class: 'improve-docs btn btn-primary' }, function(dom) {
|
|
dom.tag('i', {class:'icon-edit'}, ' ');
|
|
dom.text(' Improve this doc');
|
|
});
|
|
dom.h(title(this), function() {
|
|
notice('deprecated', 'Deprecated API', self.deprecated);
|
|
if (self.ngdoc === 'error') {
|
|
minerrMsg = lookupMinerrMsg(self);
|
|
dom.tag('pre', {
|
|
class:'minerr-errmsg',
|
|
'error-display': minerrMsg.replace(/"/g, '"')
|
|
}, minerrMsg);
|
|
}
|
|
if (self.ngdoc != 'overview') {
|
|
dom.h('Description', self.description, dom.html);
|
|
}
|
|
dom.h('Dependencies', self.requires, function(require){
|
|
dom.tag('code', function() {
|
|
dom.tag('a', {href: 'api/ng.' + require.name}, require.name);
|
|
});
|
|
dom.html(require.text);
|
|
});
|
|
|
|
(self['html_usage_' + self.ngdoc] || function() {
|
|
throw new Error("Don't know how to format @ngdoc: " + self.ngdoc);
|
|
}).call(self, dom);
|
|
|
|
dom.h('Example', self.example, dom.html);
|
|
});
|
|
|
|
self.anchors = dom.anchors;
|
|
|
|
return dom.toString();
|
|
|
|
//////////////////////////
|
|
|
|
function notice(name, legend, msg){
|
|
if (self[name] === undefined) return;
|
|
dom.tag('fieldset', {'class':name}, function(dom){
|
|
dom.tag('legend', legend);
|
|
dom.text(msg);
|
|
});
|
|
}
|
|
|
|
},
|
|
|
|
prepare_type_hint_class_name : function(type) {
|
|
var typeClass = type.toLowerCase().match(/^[-\w]+/) || [];
|
|
typeClass = typeClass[0] ? typeClass[0] : 'object';
|
|
return 'label type-hint type-hint-' + typeClass;
|
|
},
|
|
|
|
html_usage_parameters: function(dom) {
|
|
var self = this;
|
|
var params = this.param ? this.param : [];
|
|
if(this.animations) {
|
|
dom.h('Animations', this.animations, function(animations){
|
|
dom.html('<ul>');
|
|
var animations = animations.split("\n");
|
|
animations.forEach(function(ani) {
|
|
dom.html('<li>');
|
|
dom.text(ani);
|
|
dom.html('</li>');
|
|
});
|
|
dom.html('</ul>');
|
|
});
|
|
dom.html('<a href="api/ngAnimate.$animate">Click here</a> to learn more about the steps involved in the animation.');
|
|
}
|
|
if(params.length > 0) {
|
|
dom.html('<h2>Parameters</h2>');
|
|
dom.html('<table class="variables-matrix table table-bordered table-striped">');
|
|
dom.html('<thead>');
|
|
dom.html('<tr>');
|
|
dom.html('<th>Param</th>');
|
|
dom.html('<th>Type</th>');
|
|
dom.html('<th>Details</th>');
|
|
dom.html('</tr>');
|
|
dom.html('</thead>');
|
|
dom.html('<tbody>');
|
|
for(var i=0;i<params.length;i++) {
|
|
param = params[i];
|
|
var name = param.name;
|
|
var types = param.type;
|
|
if(types[0]=='(') {
|
|
types = types.substr(1);
|
|
}
|
|
|
|
var limit = types.length - 1;
|
|
if(types.charAt(limit) == ')' && types.charAt(limit-1) != '(') {
|
|
types = types.substr(0,limit);
|
|
}
|
|
types = types.split(/\|(?![\(\)\w\|\s]+>)/);
|
|
if (param.optional) {
|
|
name += ' <div><em>(optional)</em></div>';
|
|
}
|
|
dom.html('<tr>');
|
|
dom.html('<td>' + name + '</td>');
|
|
dom.html('<td>');
|
|
for(var j=0;j<types.length;j++) {
|
|
var type = types[j];
|
|
dom.html('<a href="" class="' + self.prepare_type_hint_class_name(type) + '">');
|
|
dom.text(type);
|
|
dom.html('</a>');
|
|
}
|
|
|
|
dom.html('</td>');
|
|
var description = '<td>';
|
|
description += param.description;
|
|
if (param.default) {
|
|
description += ' <p><em>(default: ' + param.default + ')</em></p>';
|
|
}
|
|
description += '</td>';
|
|
dom.html(description);
|
|
dom.html('</tr>');
|
|
};
|
|
dom.html('</tbody>');
|
|
dom.html('</table>');
|
|
}
|
|
},
|
|
|
|
html_usage_returns: function(dom) {
|
|
var self = this;
|
|
if (self.returns) {
|
|
dom.html('<h2>Returns</h2>');
|
|
dom.html('<table class="variables-matrix">');
|
|
dom.html('<tr>');
|
|
dom.html('<td>');
|
|
dom.html('<a href="" class="' + self.prepare_type_hint_class_name(self.returns.type) + '">');
|
|
dom.text(self.returns.type);
|
|
dom.html('</a>');
|
|
dom.html('</td>');
|
|
dom.html('<td>');
|
|
dom.html(self.returns.description);
|
|
dom.html('</td>');
|
|
dom.html('</tr>');
|
|
dom.html('</table>');
|
|
}
|
|
},
|
|
|
|
html_usage_this: function(dom) {
|
|
var self = this;
|
|
if (self['this']) {
|
|
dom.h(function(dom){
|
|
dom.html("Method's <code>this</code>");
|
|
}, function(dom){
|
|
dom.html(self['this']);
|
|
});
|
|
}
|
|
},
|
|
|
|
html_usage_function: function(dom){
|
|
var self = this;
|
|
var name = self.name.match(/^angular(\.mock)?\.(\w+)$/) ? self.name : self.name.split(/\./).pop()
|
|
|
|
dom.h('Usage', function() {
|
|
dom.code(function() {
|
|
dom.text(name);
|
|
dom.text('(');
|
|
self.parameters(dom, ', ');
|
|
dom.text(');');
|
|
});
|
|
|
|
self.html_usage_parameters(dom);
|
|
self.html_usage_this(dom);
|
|
self.html_usage_returns(dom);
|
|
});
|
|
this.method_properties_events(dom);
|
|
},
|
|
|
|
html_usage_property: function(dom){
|
|
var self = this;
|
|
dom.h('Usage', function() {
|
|
dom.code(function() {
|
|
dom.text(self.name);
|
|
});
|
|
|
|
self.html_usage_returns(dom);
|
|
});
|
|
},
|
|
|
|
html_usage_directive: function(dom){
|
|
var self = this;
|
|
dom.h('Usage', function() {
|
|
var restrict = self.restrict || 'A';
|
|
|
|
if (restrict.match(/E/)) {
|
|
dom.html('<p>');
|
|
dom.text('This directive can be used as custom element, but be aware of ');
|
|
dom.tag('a', {href:'guide/ie'}, 'IE restrictions');
|
|
dom.text('.');
|
|
dom.html('</p>');
|
|
}
|
|
|
|
if (self.usage) {
|
|
dom.tag('pre', function() {
|
|
dom.tag('code', function() {
|
|
dom.text(self.usage);
|
|
});
|
|
});
|
|
} else {
|
|
if (restrict.match(/E/)) {
|
|
dom.text('as element:');
|
|
dom.code(function() {
|
|
dom.text('<');
|
|
dom.text(dashCase(self.shortName));
|
|
renderParams('\n ', '="', '"');
|
|
dom.text('>\n</');
|
|
dom.text(dashCase(self.shortName));
|
|
dom.text('>');
|
|
});
|
|
}
|
|
if (restrict.match(/A/)) {
|
|
var element = self.element || 'ANY';
|
|
dom.text('as attribute');
|
|
dom.code(function() {
|
|
dom.text('<' + element + ' ');
|
|
dom.text(dashCase(self.shortName));
|
|
renderParams('\n ', '="', '"', true);
|
|
dom.text('>\n ...\n');
|
|
dom.text('</' + element + '>');
|
|
});
|
|
}
|
|
if (restrict.match(/C/)) {
|
|
dom.text('as class');
|
|
var element = self.element || 'ANY';
|
|
dom.code(function() {
|
|
dom.text('<' + element + ' class="');
|
|
dom.text(dashCase(self.shortName));
|
|
renderParams(' ', ': ', ';', true);
|
|
dom.text('">\n ...\n');
|
|
dom.text('</' + element + '>');
|
|
});
|
|
}
|
|
}
|
|
self.html_usage_directiveInfo(dom);
|
|
self.html_usage_parameters(dom);
|
|
});
|
|
|
|
self.method_properties_events(dom);
|
|
|
|
function renderParams(prefix, infix, suffix, skipSelf) {
|
|
(self.param||[]).forEach(function(param) {
|
|
var skip = skipSelf && (param.name == self.shortName || param.name.indexOf(self.shortName + '|') == 0);
|
|
if (!skip) {
|
|
dom.text(prefix);
|
|
dom.text(param.optional ? '[' : '');
|
|
var parts = param.name.split('|');
|
|
dom.text(dashCase(parts[skipSelf ? 0 : 1] || parts[0]));
|
|
}
|
|
if (BOOLEAN_ATTR[param.name]) {
|
|
dom.text(param.optional ? ']' : '');
|
|
} else {
|
|
dom.text(BOOLEAN_ATTR[param.name] ? '' : infix );
|
|
dom.text(('{' + param.type + '}').replace(/^\{\'(.*)\'\}$/, '$1'));
|
|
dom.text(suffix);
|
|
dom.text(param.optional && !skip ? ']' : '');
|
|
}
|
|
});
|
|
}
|
|
|
|
},
|
|
|
|
html_usage_filter: function(dom){
|
|
var self = this;
|
|
dom.h('Usage', function() {
|
|
dom.h('In HTML Template Binding', function() {
|
|
dom.tag('code', function() {
|
|
if (self.usage) {
|
|
dom.text(self.usage);
|
|
} else {
|
|
dom.text('{{ ');
|
|
dom.text(self.shortName);
|
|
dom.text('_expression | ');
|
|
dom.text(self.shortName);
|
|
self.parameters(dom, ':', true);
|
|
dom.text(' }}');
|
|
}
|
|
});
|
|
});
|
|
|
|
dom.h('In JavaScript', function() {
|
|
dom.tag('code', function() {
|
|
dom.text('$filter(\'');
|
|
dom.text(self.shortName);
|
|
dom.text('\')(');
|
|
self.parameters(dom, ', ');
|
|
dom.text(')');
|
|
});
|
|
});
|
|
|
|
self.html_usage_parameters(dom);
|
|
self.html_usage_this(dom);
|
|
self.html_usage_returns(dom);
|
|
});
|
|
},
|
|
|
|
html_usage_inputType: function(dom){
|
|
var self = this;
|
|
dom.h('Usage', function() {
|
|
dom.code(function() {
|
|
dom.text('<input type="' + self.shortName + '"');
|
|
(self.param||[]).forEach(function(param){
|
|
dom.text('\n ');
|
|
dom.text(param.optional ? ' [' : ' ');
|
|
dom.text(dashCase(param.name));
|
|
dom.text(BOOLEAN_ATTR[param.name] ? '' : '="{' + param.type + '}"');
|
|
dom.text(param.optional ? ']' : '');
|
|
});
|
|
dom.text('>');
|
|
});
|
|
self.html_usage_parameters(dom);
|
|
});
|
|
},
|
|
|
|
html_usage_directiveInfo: function(dom) {
|
|
var self = this;
|
|
var list = [];
|
|
|
|
|
|
if (self.scope !== undefined) {
|
|
list.push('This directive creates new scope.');
|
|
}
|
|
if (self.priority !== undefined) {
|
|
list.push('This directive executes at priority level ' + self.priority + '.');
|
|
}
|
|
|
|
if (list.length) {
|
|
dom.h('Directive info', function() {
|
|
dom.ul(list);
|
|
});
|
|
}
|
|
},
|
|
|
|
html_usage_overview: function(dom){
|
|
dom.html(this.description);
|
|
},
|
|
|
|
html_usage_error: function (dom) {
|
|
dom.html();
|
|
},
|
|
|
|
html_usage_interface: function(dom){
|
|
var self = this;
|
|
|
|
if (this.param.length) {
|
|
dom.h('Usage', function() {
|
|
dom.code(function() {
|
|
dom.text(self.name.split('.').pop());
|
|
dom.text('(');
|
|
self.parameters(dom, ', ');
|
|
dom.text(');');
|
|
});
|
|
|
|
self.html_usage_parameters(dom);
|
|
self.html_usage_this(dom);
|
|
self.html_usage_returns(dom);
|
|
});
|
|
}
|
|
this.method_properties_events(dom);
|
|
},
|
|
|
|
html_usage_service: function(dom) {
|
|
this.html_usage_interface(dom)
|
|
},
|
|
|
|
html_usage_object: function(dom) {
|
|
this.html_usage_interface(dom)
|
|
},
|
|
|
|
method_properties_events: function(dom) {
|
|
var self = this;
|
|
if (self.methods.length) {
|
|
dom.div({class:'member method'}, function(){
|
|
dom.h('Methods', self.methods, function(method){
|
|
var signature = (method.param || []).map(property('name'));
|
|
dom.h(method.shortName + '(' + signature.join(', ') + ')', method, function() {
|
|
dom.html(method.description);
|
|
method.html_usage_parameters(dom);
|
|
self.html_usage_this(dom);
|
|
method.html_usage_returns(dom);
|
|
|
|
dom.h('Example', method.example, dom.html);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
if (self.properties.length) {
|
|
dom.div({class:'member property'}, function(){
|
|
dom.h('Properties', self.properties, function(property){
|
|
dom.h(property.shortName, function() {
|
|
dom.html(property.description);
|
|
if (!property.html_usage_returns) {
|
|
console.log(property);
|
|
}
|
|
property.html_usage_returns(dom);
|
|
dom.h('Example', property.example, dom.html);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
if (self.events.length) {
|
|
dom.div({class:'member event'}, function(){
|
|
dom.h('Events', self.events, function(event){
|
|
dom.h(event.shortName, event, function() {
|
|
dom.html(event.description);
|
|
if (event.type == 'listen') {
|
|
dom.tag('div', {class:'inline'}, function() {
|
|
dom.h('Listen on:', event.target);
|
|
});
|
|
} else {
|
|
dom.tag('div', {class:'inline'}, function() {
|
|
dom.h('Type:', event.type);
|
|
});
|
|
dom.tag('div', {class:'inline'}, function() {
|
|
dom.h('Target:', event.target);
|
|
});
|
|
}
|
|
event.html_usage_parameters(dom);
|
|
self.html_usage_this(dom);
|
|
|
|
dom.h('Example', event.example, dom.html);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
},
|
|
|
|
parameters: function(dom, separator, skipFirst, prefix) {
|
|
var sep = prefix ? separator : '';
|
|
(this.param||[]).forEach(function(param, i){
|
|
if (!(skipFirst && i==0)) {
|
|
if (param.optional) {
|
|
dom.text('[' + sep + param.name + ']');
|
|
} else {
|
|
dom.text(sep + param.name);
|
|
}
|
|
}
|
|
sep = separator;
|
|
});
|
|
}
|
|
|
|
};
|
|
//////////////////////////////////////////////////////////
|
|
|
|
|
|
//////////////////////////////////////////////////////////
|
|
var GLOBALS = /^angular\.([^\.]+)$/,
|
|
MODULE = /^((?:(?!^angular\.)[^\.])+)$/,
|
|
MODULE_MOCK = /^angular\.mock\.([^\.]+)$/,
|
|
MODULE_DIRECTIVE = /^((?:(?!^angular\.)[^\.])+)\.directive:([^\.]+)$/,
|
|
MODULE_DIRECTIVE_INPUT = /^((?:(?!^angular\.)[^\.])+)\.directive:input\.([^\.]+)$/,
|
|
MODULE_FILTER = /^((?:(?!^angular\.)[^\.])+)\.filter:([^\.]+)$/,
|
|
MODULE_SERVICE = /^((?:(?!^angular\.)[^\.])+)\.([^\.]+?)(Provider)?$/,
|
|
MODULE_TYPE = /^((?:(?!^angular\.)[^\.])+)\..+\.([A-Z][^\.]+)$/;
|
|
|
|
|
|
function title(doc) {
|
|
if (!doc.name) return doc.name;
|
|
var match,
|
|
text = doc.name;
|
|
|
|
var makeTitle = function (name, type, componentType, component) {
|
|
// Makes title markup.
|
|
// makeTitle('Foo', 'directive', 'module', 'ng') ->
|
|
// Foo is a directive in module ng
|
|
return function () {
|
|
this.tag('code', name);
|
|
this.tag('div', function () {
|
|
this.tag('span', {class: 'hint'}, function () {
|
|
if (type && component) {
|
|
this.text(type + ' in ' + componentType + ' ');
|
|
this.tag('code', component);
|
|
}
|
|
});
|
|
});
|
|
};
|
|
};
|
|
|
|
if (doc.ngdoc === 'error') {
|
|
return makeTitle(doc.fullName, 'error', 'component', doc.getMinerrNamespace());
|
|
} else if (text == 'angular.Module') {
|
|
return makeTitle('Module', 'Type', 'module', 'ng');
|
|
} else if (match = text.match(GLOBALS)) {
|
|
return makeTitle('angular.' + match[1], 'API', 'module', 'ng');
|
|
} else if (match = text.match(MODULE)) {
|
|
return makeTitle('', '', 'module', match[1]);
|
|
} else if (match = text.match(MODULE_MOCK)) {
|
|
return makeTitle('angular.mock.' + match[1], 'API', 'module', 'ng');
|
|
} else if (match = text.match(MODULE_DIRECTIVE)) {
|
|
return makeTitle(match[2], 'directive', 'module', match[1]);
|
|
} else if (match = text.match(MODULE_DIRECTIVE_INPUT)) {
|
|
return makeTitle('input [' + match[2] + ']', 'directive', 'module', match[1]);
|
|
} else if (match = text.match(MODULE_FILTER)) {
|
|
return makeTitle(match[2], 'filter', 'module', match[1]);
|
|
} else if (match = text.match(MODULE_SERVICE)) {
|
|
return makeTitle(match[2] + (match[3] || ''), 'service', 'module', match[1]);
|
|
} else if (match = text.match(MODULE_TYPE)) {
|
|
return makeTitle(match[2], 'type', 'module', match[1]);
|
|
}
|
|
return text;
|
|
}
|
|
|
|
|
|
function scenarios(docs){
|
|
var specs = [];
|
|
|
|
specs.push('describe("angular+jqlite", function() {');
|
|
appendSpecs('index-nocache.html#!/');
|
|
specs.push('});');
|
|
|
|
specs.push('');
|
|
specs.push('');
|
|
|
|
specs.push('describe("angular+jquery", function() {');
|
|
appendSpecs('index-jq-nocache.html#!/');
|
|
specs.push('});');
|
|
|
|
return specs.join('\n');
|
|
|
|
function appendSpecs(urlPrefix) {
|
|
docs.forEach(function(doc){
|
|
specs.push(' describe("' + doc.section + '/' + doc.id + '", function() {');
|
|
specs.push(' beforeEach(function() {');
|
|
specs.push(' browser().navigateTo("' + urlPrefix + doc.section + '/' + doc.id + '");');
|
|
specs.push(' });');
|
|
specs.push(' ');
|
|
doc.scenarios.forEach(function(scenario){
|
|
specs.push(indentCode(trim(scenario), 4));
|
|
specs.push('');
|
|
});
|
|
specs.push('});');
|
|
specs.push('');
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////
|
|
function metadata(docs){
|
|
var pages = [];
|
|
docs.forEach(function(doc){
|
|
var path = (doc.name || '').split(/(\:\s*)/);
|
|
for ( var i = 1; i < path.length; i++) {
|
|
path.splice(i, 1);
|
|
}
|
|
var shortName = path.pop().trim();
|
|
|
|
if (path.pop() == 'input') {
|
|
shortName = 'input [' + shortName + ']';
|
|
}
|
|
|
|
pages.push({
|
|
section: doc.section,
|
|
id: doc.id,
|
|
name: title(doc),
|
|
shortName: shortName,
|
|
type: doc.ngdoc,
|
|
moduleName: doc.moduleName,
|
|
shortDescription: doc.shortDescription(),
|
|
keywords: doc.keywords()
|
|
});
|
|
});
|
|
pages.sort(sidebarSort);
|
|
return pages;
|
|
}
|
|
|
|
var KEYWORD_PRIORITY = {
|
|
'.index': 1,
|
|
'.overview': 1,
|
|
'.bootstrap': 2,
|
|
'.mvc': 3,
|
|
'.scopes': 4,
|
|
'.compiler': 5,
|
|
'.templates': 6,
|
|
'.services': 7,
|
|
'.di': 8,
|
|
'.unit-testing': 9,
|
|
'.dev_guide': 9,
|
|
'.dev_guide.overview': 1,
|
|
'.dev_guide.bootstrap': 2,
|
|
'.dev_guide.bootstrap.auto_bootstrap': 1,
|
|
'.dev_guide.bootstrap.manual_bootstrap': 2,
|
|
'.dev_guide.mvc': 3,
|
|
'.dev_guide.mvc.understanding_model': 1,
|
|
'.dev_guide.mvc.understanding_controller': 2,
|
|
'.dev_guide.mvc.understanding_view': 3,
|
|
'.dev_guide.scopes': 4,
|
|
'.dev_guide.scopes.understanding_scopes': 1,
|
|
'.dev_guide.scopes.internals': 2,
|
|
'.dev_guide.compiler': 5,
|
|
'.dev_guide.templates': 6,
|
|
'.dev_guide.services': 7,
|
|
'.dev_guide.di': 8,
|
|
'.dev_guide.unit-testing': 9
|
|
};
|
|
|
|
var GUIDE_PRIORITY = [
|
|
'introduction',
|
|
'overview',
|
|
'concepts',
|
|
'dev_guide.mvc',
|
|
|
|
'dev_guide.mvc.understanding_controller',
|
|
'dev_guide.mvc.understanding_model',
|
|
'dev_guide.mvc.understanding_view',
|
|
|
|
'dev_guide.services.understanding_services',
|
|
'dev_guide.services.managing_dependencies',
|
|
'dev_guide.services.creating_services',
|
|
'dev_guide.services.injecting_controllers',
|
|
'dev_guide.services.testing_services',
|
|
'dev_guide.services.$location',
|
|
'dev_guide.services',
|
|
|
|
'databinding',
|
|
'dev_guide.templates.css-styling',
|
|
'dev_guide.templates.filters.creating_filters',
|
|
'dev_guide.templates.filters',
|
|
'dev_guide.templates.filters.using_filters',
|
|
'dev_guide.templates',
|
|
|
|
'di',
|
|
'providers',
|
|
'module',
|
|
'scope',
|
|
'expression',
|
|
'bootstrap',
|
|
'directive',
|
|
'compiler',
|
|
|
|
'forms',
|
|
'animations',
|
|
|
|
'dev_guide.e2e-testing',
|
|
'dev_guide.unit-testing',
|
|
|
|
'i18n',
|
|
'ie',
|
|
'migration',
|
|
];
|
|
|
|
function sidebarSort(a, b){
|
|
priorityA = GUIDE_PRIORITY.indexOf(a.id);
|
|
priorityB = GUIDE_PRIORITY.indexOf(b.id);
|
|
|
|
if (priorityA > -1 || priorityB > -1) {
|
|
return priorityA < priorityB ? -1 : (priorityA > priorityB ? 1 : 0);
|
|
}
|
|
|
|
function mangleName(doc) {
|
|
var path = doc.id.split(/\./);
|
|
var mangled = [];
|
|
var partialName = '';
|
|
path.forEach(function(name){
|
|
partialName += '.' + name;
|
|
mangled.push(KEYWORD_PRIORITY[partialName] || 5);
|
|
mangled.push(name);
|
|
});
|
|
return (doc.section + '/' + mangled.join('.')).toLowerCase();
|
|
}
|
|
var nameA = mangleName(a);
|
|
var nameB = mangleName(b);
|
|
return nameA < nameB ? -1 : (nameA > nameB ? 1 : 0);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////
|
|
function trim(text) {
|
|
var MAX_INDENT = 9999;
|
|
var empty = RegExp.prototype.test.bind(/^\s*$/);
|
|
var lines = text.split('\n');
|
|
var minIndent = MAX_INDENT;
|
|
var indentRegExp;
|
|
var ignoreLine = (lines[0][0] != ' ' && lines.length > 1);
|
|
// ignore first line if it has no indentation and there is more than one line
|
|
|
|
lines.forEach(function(line){
|
|
if (ignoreLine) {
|
|
ignoreLine = false;
|
|
return;
|
|
}
|
|
|
|
var indent = line.match(/^\s*/)[0].length;
|
|
if (indent > 0 || minIndent == MAX_INDENT) {
|
|
minIndent = Math.min(minIndent, indent);
|
|
}
|
|
});
|
|
|
|
indentRegExp = new RegExp('^\\s{0,' + minIndent + '}');
|
|
|
|
for ( var i = 0; i < lines.length; i++) {
|
|
lines[i] = lines[i].replace(indentRegExp, '');
|
|
}
|
|
|
|
// remove leading lines
|
|
while (empty(lines[0])) {
|
|
lines.shift();
|
|
}
|
|
|
|
// remove trailing
|
|
while (empty(lines[lines.length - 1])) {
|
|
lines.pop();
|
|
}
|
|
return lines.join('\n');
|
|
}
|
|
|
|
function indentCode(text, spaceCount) {
|
|
var lines = text.split('\n'),
|
|
indent = '',
|
|
fixedLines = [];
|
|
|
|
while(spaceCount--) indent += ' ';
|
|
|
|
lines.forEach(function(line) {
|
|
fixedLines.push(indent + line);
|
|
});
|
|
|
|
return fixedLines.join('\n');
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////
|
|
function merge(docs){
|
|
var byFullId = {};
|
|
|
|
docs.forEach(function(doc) {
|
|
byFullId[doc.section + '/' + doc.id] = doc;
|
|
});
|
|
|
|
for(var i = 0; i < docs.length;) {
|
|
if (findParent(docs[i], 'method') || findParent(docs[i], 'property') || findParent(docs[i], 'event')) {
|
|
docs.splice(i, 1);
|
|
} else {
|
|
i++;
|
|
}
|
|
}
|
|
|
|
function findParent(doc, name) {
|
|
var parentName = doc[name + 'Of'];
|
|
if (!parentName) return false;
|
|
|
|
var parent = byFullId['api/' + parentName];
|
|
if (!parent)
|
|
throw new Error("No parent named '" + parentName + "' for '" +
|
|
doc.name + "' in @" + name + "Of.");
|
|
|
|
var listName = (name + 's').replace(/ys$/, 'ies');
|
|
var list = parent[listName] = (parent[listName] || []);
|
|
list.push(doc);
|
|
list.sort(orderByName);
|
|
return true;
|
|
}
|
|
|
|
function orderByName(a, b){
|
|
return a.name < b.name ? -1 : (a.name > b.name ? 1 : 0);
|
|
}
|
|
}
|
|
//////////////////////////////////////////////////////////
|
|
|
|
|
|
function checkBrokenLinks(docs) {
|
|
var byFullId = Object.create(null);
|
|
|
|
docs.forEach(function(doc) {
|
|
byFullId[doc.section + '/' + doc.id] = doc;
|
|
if (doc.section === 'api') {
|
|
doc.anchors.push('directive', 'service', 'filter', 'function');
|
|
}
|
|
});
|
|
|
|
docs.forEach(function(doc) {
|
|
doc.links.forEach(function(link) {
|
|
// convert #id to path#id
|
|
if (link[0] == '#') {
|
|
link = doc.section + '/' + doc.id.split('#').shift() + link;
|
|
}
|
|
|
|
var parts = link.split('#');
|
|
var pageLink = parts[0];
|
|
var anchorLink = parts[1];
|
|
var linkedPage = byFullId[pageLink];
|
|
|
|
if (!linkedPage) {
|
|
console.log('WARNING: ' + doc.section + '/' + doc.id + ' (defined in ' + doc.file + ') points to a non existing page "' + link + '"!');
|
|
} else if (anchorLink && linkedPage.anchors.indexOf(anchorLink) === -1) {
|
|
console.log('WARNING: ' + doc.section + '/' + doc.id + ' (defined in ' + doc.file + ') points to a non existing anchor "' + link + '"!');
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
function property(name) {
|
|
return function(value){
|
|
return value[name];
|
|
};
|
|
}
|
|
|
|
|
|
var DASH_CASE_REGEXP = /[A-Z]/g;
|
|
function dashCase(name){
|
|
return name.replace(DASH_CASE_REGEXP, function(letter, pos) {
|
|
return (pos ? '-' : '') + letter.toLowerCase();
|
|
});
|
|
}
|
|
//////////////////////////////////////////////////////////
|
|
|
|
function explainModuleInstallation(moduleName){
|
|
var ngMod = ngModule(moduleName),
|
|
modulePackage = 'angular-' + moduleName,
|
|
modulePackageFile = modulePackage + '.js';
|
|
|
|
return '<h1>Installation</h1>' +
|
|
'<p>First include <code>' + modulePackageFile +'</code> in your HTML:</p><pre><code>' +
|
|
' <script src="angular.js">\n' +
|
|
' <script src="' + modulePackageFile + '"></pre></code>' +
|
|
|
|
'<p>You can download this file from the following places:</p>' +
|
|
'<ul>' +
|
|
'<li>[Google CDN](https://developers.google.com/speed/libraries/devguide#angularjs)<br>' +
|
|
'e.g. <code>"//ajax.googleapis.com/ajax/libs/angularjs/X.Y.Z/' + modulePackageFile + '"</code></li>' +
|
|
'<li>[Bower](http://bower.io)<br>' +
|
|
'e.g. <code>bower install ' + modulePackage + '@X.Y.Z</code></li>' +
|
|
'<li><a href="http://code.angularjs.org/">code.angularjs.org</a><br>' +
|
|
'e.g. <code>"//code.angularjs.org/X.Y.Z/' + modulePackageFile + '"</code></li>' +
|
|
'</ul>' +
|
|
'<p>where X.Y.Z is the AngularJS version you are running.</p>' +
|
|
'<p>Then load the module in your application by adding it as a dependent module:</p><pre><code>' +
|
|
' angular.module(\'app\', [\'' + ngMod + '\']);</pre></code>' +
|
|
|
|
'<p>With that you\'re ready to get started!</p>';
|
|
}
|
|
|
|
function ngModule(moduleName) {
|
|
return 'ng' + moduleName[0].toUpperCase() + moduleName.substr(1);
|
|
}
|