feat(grunt): Add demo grunt task

This commit is contained in:
Pete Bacon Darwin
2012-12-21 09:40:20 +00:00
parent 7c552caeb0
commit 243ffc0687
22 changed files with 7305 additions and 21 deletions
+1
View File
@@ -8,6 +8,7 @@ lib-cov
*.gz
*.swp
*.swo
.DS_Store
pids
logs
+2 -1
View File
@@ -5,6 +5,7 @@
before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- npm install --quiet -g grunt@0.3.x testacular@0.2.x
- npm install --quiet -g grunt-cli testacular
- npm install
script: grunt
+54 -13
View File
@@ -1,3 +1,6 @@
var fs = require('fs');
var markdown = require('node-markdown').Markdown;
module.exports = function(grunt) {
// Project configuration.
@@ -7,7 +10,7 @@ module.exports = function(grunt) {
banner: 'angular.module("ui.bootstrap", [<%= modules %>]);'
},
lint: {
files: ['grunt.js', 'src/**/*.js']
files: ['grunt.js','src/**/*.js']
},
watch: {
files: '<config:lint.files>',
@@ -16,7 +19,7 @@ module.exports = function(grunt) {
concat: {
dist: {
src: ['<banner>', 'src/*/*.js'],
dest: 'dist/angular-ui-bootstrap.js'
dest: 'dist/ui-bootstrap.js'
}
},
html2js: {
@@ -39,20 +42,58 @@ module.exports = function(grunt) {
//register before and after test tasks so we've don't have to change cli options on the goole's CI server
grunt.registerTask('before-test', 'lint html2js');
grunt.registerTask('after-test', 'find-modules concat');
grunt.registerTask('demo', 'before-test after-test build-demo');
// Default task.
grunt.registerTask('default', 'before-test test after-test');
grunt.registerTask('default', 'before-test test after-test demo');
//Common ui.bootstrap module containing all modules
grunt.registerTask('find-modules', 'Generate ui.bootstrap module depending on all existing directives', function() {
var modules = [];
grunt.file.expandDirs('src/*').forEach(function(dir) {
var moduleName = dir.split("/")[1];
modules.push('"ui.bootstrap.' + moduleName + '"');
var modules = grunt.file.expandDirs('src/*').map(function(dir) {
return '"ui.bootstrap.' + dir.split("/")[1] + '"';
});
grunt.config('modules', modules);
});
grunt.registerTask('build-demo', 'Create grunt demo.html from every module\'s files', function() {
this.requires('find-modules concat html2js');
var modules = grunt.file.expandDirs('src/*').map(function(dir) {
var moduleName = dir.split("/")[1];
if (fs.existsSync(dir + "docs")) {
return {
name: moduleName,
js: grunt.file.expand(dir + "docs/*.js").map(grunt.file.read).join(''),
html: grunt.file.expand(dir + "docs/*.html").map(grunt.file.read).join(''),
description: grunt.file.expand(dir + "docs/*.md").map(grunt.file.read).map(markdown).join('')
};
}
return {
name: moduleName,
js: moduleName,
html: moduleName,
description: moduleName
};
});
var templateFiles = grunt.file.expand("template/**/*.html.js");
grunt.file.write(
'dist/demo.html',
grunt.template.process(grunt.file.read('misc/demo-template.html'), {
modules: modules,
templateModules: templateFiles.map(function(fileName) {
return "'"+fileName.substr(0, fileName.length - 3)+"'";
}),
templates: templateFiles.map(grunt.file.read).join('')
})
);
grunt.file.expand('misc/demo-assets/*').forEach(function(path) {
grunt.file.copy(path, 'dist/assets/' + path.replace('misc/demo-assets/',''));
});
});
//Html templates to $templateCache for tests
grunt.registerMultiTask('html2js', 'Generate js versions of html template', function() {
//Put templates on ng's run function so they are global
@@ -60,14 +101,14 @@ module.exports = function(grunt) {
' $templateCache.put("<%= file %>",\n "<%= content %>");\n' +
'});\n';
var files = grunt._watch_changed_files || grunt.file.expand(this.data);
function escapeContent(content) {
return content.replace(/"/g, '\\"').replace(/\n/g, '" +\n "').replace(/\r/g, '');
}
files.forEach(function(file) {
grunt.file.write(file + ".js", grunt.template.process(TPL, {
file: file,
content: escapeContent(grunt.file.read(file))
file: file,
content: escapeContent(grunt.file.read(file))
}));
});
});
@@ -78,8 +119,8 @@ module.exports = function(grunt) {
var args = [command].concat(options);
var done = grunt.task.current.async();
var child = grunt.utils.spawn({
cmd: testacularCmd,
args: args
cmd: testacularCmd,
args: args
}, function(err, result, code) {
if (code) {
done(false);
@@ -116,4 +157,4 @@ module.exports = function(grunt) {
var options = ['--no-single-run', '--auto-watch'].concat(this.args);
runTestacular('start', options);
});
};
};
+6039
View File
File diff suppressed because it is too large Load Diff
+59
View File
@@ -0,0 +1,59 @@
/**
* Generic language patterns
*
* @author Craig Campbell
* @version 1.0.9
*/
Rainbow.extend([
{
'matches': {
1: {
'name': 'keyword.operator',
'pattern': /\=/g
},
2: {
'name': 'string',
'matches': {
'name': 'constant.character.escape',
'pattern': /\\('|"){1}/g
}
}
},
'pattern': /(\(|\s|\[|\=|:)(('|")([^\\\1]|\\.)*?(\3))/gm
},
{
'name': 'comment',
'pattern': /\/\*[\s\S]*?\*\/|(\/\/|\#)[\s\S]*?$/gm
},
{
'name': 'constant.numeric',
'pattern': /\b(\d+(\.\d+)?(e(\+|\-)?\d+)?(f|d)?|0x[\da-f]+)\b/gi
},
{
'matches': {
1: 'keyword'
},
'pattern': /\b(and|array|as|bool(ean)?|c(atch|har|lass|onst)|d(ef|elete|o(uble)?)|e(cho|lse(if)?|xit|xtends|xcept)|f(inally|loat|or(each)?|unction)|global|if|import|int(eger)?|long|new|object|or|pr(int|ivate|otected)|public|return|self|st(ring|ruct|atic)|switch|th(en|is|row)|try|(un)?signed|var|void|while)(?=\(|\b)/gi
},
{
'name': 'constant.language',
'pattern': /true|false|null/g
},
{
'name': 'keyword.operator',
'pattern': /\+|\!|\-|&(gt|lt|amp);|\||\*|\=/g
},
{
'matches': {
1: 'function.call'
},
'pattern': /(\w+?)(?=\()/g
},
{
'matches': {
1: 'storage.function',
2: 'entity.name.function'
},
'pattern': /(function)\s(.*?)(?=\()/g
}
]);
+83
View File
@@ -0,0 +1,83 @@
/**
* HTML patterns
*
* @author Craig Campbell
* @version 1.0.7
*/
Rainbow.extend('html', [
{
'name': 'source.php.embedded',
'matches': {
2: {
'language': 'php'
}
},
'pattern': /&lt;\?=?(?!xml)(php)?([\s\S]*?)(\?&gt;)/gm
},
{
'name': 'source.css.embedded',
'matches': {
0: {
'language': 'css'
}
},
'pattern': /&lt;style(.*?)&gt;([\s\S]*?)&lt;\/style&gt;/gm
},
{
'name': 'source.js.embedded',
'matches': {
0: {
'language': 'javascript'
}
},
'pattern': /&lt;script(?! src)(.*?)&gt;([\s\S]*?)&lt;\/script&gt;/gm
},
{
'name': 'comment.html',
'pattern': /&lt;\!--[\S\s]*?--&gt;/g
},
{
'matches': {
1: 'support.tag.open',
2: 'support.tag.close'
},
'pattern': /(&lt;)|(\/?\??&gt;)/g
},
{
'name': 'support.tag',
'matches': {
1: 'support.tag',
2: 'support.tag.special',
3: 'support.tag-name'
},
'pattern': /(&lt;\??)(\/|\!?)(\w+)/g
},
{
'matches': {
1: 'support.attribute'
},
'pattern': /([a-z-]+)(?=\=)/gi
},
{
'matches': {
1: 'support.operator',
2: 'string.quote',
3: 'string.value',
4: 'string.quote'
},
'pattern': /(=)('|")(.*?)(\2)/g
},
{
'matches': {
1: 'support.operator',
2: 'support.value'
},
'pattern': /(=)([a-zA-Z\-0-9]*)\b/g
},
{
'matches': {
1: 'support.attribute'
},
'pattern': /\s(\w+)(?=\s|&gt;)(?![\s\S]*&lt;)/g
}
], true);
+110
View File
@@ -0,0 +1,110 @@
/**
* Javascript patterns
*
* @author Craig Campbell
* @version 1.0.7
*/
Rainbow.extend('javascript', [
/**
* matches $. or $(
*/
{
'name': 'selector',
'pattern': /(\s|^)\$(?=\.|\()/g
},
{
'name': 'support',
'pattern': /\b(window|document)\b/g
},
{
'matches': {
1: 'support.property'
},
'pattern': /\.(length|node(Name|Value))\b/g
},
{
'matches': {
1: 'support.function'
},
'pattern': /(setTimeout|setInterval)(?=\()/g
},
{
'matches': {
1: 'support.method'
},
'pattern': /\.(getAttribute|push|getElementById|getElementsByClassName|log|setTimeout|setInterval)(?=\()/g
},
{
'matches': {
1: 'support.tag.script',
2: [
{
'name': 'string',
'pattern': /('|")(.*?)(\1)/g
},
{
'name': 'entity.tag.script',
'pattern': /(\w+)/g
}
],
3: 'support.tag.script'
},
'pattern': /(&lt;\/?)(script.*?)(&gt;)/g
},
/**
* matches any escaped characters inside of a js regex pattern
*
* @see https://github.com/ccampbell/rainbow/issues/22
*
* this was causing single line comments to fail so it now makes sure
* the opening / is not directly followed by a *
*
* @todo check that there is valid regex in match group 1
*/
{
'name': 'string.regexp',
'matches': {
1: 'string.regexp.open',
2: {
'name': 'constant.regexp.escape',
'pattern': /\\(.){1}/g
},
3: 'string.regexp.close',
4: 'string.regexp.modifier'
},
'pattern': /(\/)(?!\*)(.+)(\/)([igm]{0,3})/g
},
/**
* matches runtime function declarations
*/
{
'matches': {
1: 'storage',
3: 'entity.function'
},
'pattern': /(var)?(\s|^)(.*)(?=\s?=\s?function\()/g
},
/**
* matches constructor call
*/
{
'matches': {
1: 'keyword',
2: 'entity.function'
},
'pattern': /(new)\s+(.*)(?=\()/g
},
/**
* matches any function call in the style functionName: function()
*/
{
'name': 'entity.function',
'pattern': /(\w+)(?=:\s{0,}function)/g
}
]);
+88
View File
@@ -0,0 +1,88 @@
/**
* GitHub theme
*
* @author Craig Campbell
* @version 1.0.4
*/
pre {
border: 1px solid #ccc;
word-wrap: break-word;
padding: 6px 10px;
line-height: 19px;
margin-bottom: 20px;
}
code {
border: 1px solid #eaeaea;
margin: 0px 2px;
padding: 0px 5px;
font-size: 12px;
}
pre code {
border: 0px;
padding: 0px;
margin: 0px;
-moz-border-radius: 0px;
-webkit-border-radius: 0px;
border-radius: 0px;
}
pre, code {
font-family: Consolas, 'Liberation Mono', Courier, monospace;
color: #333;
background: #f8f8f8;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
}
pre, pre code {
font-size: 13px;
}
pre .comment {
color: #998;
}
pre .support {
color: #0086B3;
}
pre .tag, pre .tag-name {
color: navy;
}
pre .keyword, pre .css-property, pre .vendor-prefix, pre .sass, pre .class, pre .id, pre .css-value, pre .entity.function, pre .storage.function {
font-weight: bold;
}
pre .css-property, pre .css-value, pre .vendor-prefix, pre .support.namespace {
color: #333;
}
pre .constant.numeric, pre .keyword.unit, pre .hex-color {
font-weight: normal;
color: #099;
}
pre .entity.class {
color: #458;
}
pre .entity.id, pre .entity.function {
color: #900;
}
pre .attribute, pre .variable {
color: teal;
}
pre .string, pre .support.value {
font-weight: normal;
color: #d14;
}
pre .regexp {
color: #009926;
}
+773
View File
@@ -0,0 +1,773 @@
/**
* Copyright 2012 Craig Campbell
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Rainbow is a simple code syntax highlighter
*
* @preserve @version 1.1.8
* @url rainbowco.de
*/
window['Rainbow'] = (function() {
/**
* array of replacements to process at the end
*
* @type {Object}
*/
var replacements = {},
/**
* an array of start and end positions of blocks to be replaced
*
* @type {Object}
*/
replacement_positions = {},
/**
* an array of the language patterns specified for each language
*
* @type {Object}
*/
language_patterns = {},
/**
* an array of languages and whether they should bypass the default patterns
*
* @type {Object}
*/
bypass_defaults = {},
/**
* processing level
*
* replacements are stored at this level so if there is a sub block of code
* (for example php inside of html) it runs at a different level
*
* @type {number}
*/
CURRENT_LEVEL = 0,
/**
* constant used to refer to the default language
*
* @type {number}
*/
DEFAULT_LANGUAGE = 0,
/**
* used as counters so we can selectively call setTimeout
* after processing a certain number of matches/replacements
*
* @type {number}
*/
match_counter = 0,
/**
* @type {number}
*/
replacement_counter = 0,
/**
* @type {null|string}
*/
global_class,
/**
* @type {null|Function}
*/
onHighlight;
/**
* cross browser get attribute for an element
*
* @see http://stackoverflow.com/questions/3755227/cross-browser-javascript-getattribute-method
*
* @param {Node} el
* @param {string} attr attribute you are trying to get
* @returns {string|number}
*/
function _attr(el, attr, attrs, i) {
var result = (el.getAttribute && el.getAttribute(attr)) || 0;
if (!result) {
attrs = el.attributes;
for (i = 0; i < attrs.length; ++i) {
if (attrs[i].nodeName === attr) {
return attrs[i].nodeValue;
}
}
}
return result;
}
/**
* adds a class to a given code block
*
* @param {Element} el
* @param {string} class_name class name to add
* @returns void
*/
function _addClass(el, class_name) {
el.className += el.className ? ' ' + class_name : class_name;
}
/**
* checks if a block has a given class
*
* @param {Element} el
* @param {string} class_name class name to check for
* @returns {boolean}
*/
function _hasClass(el, class_name) {
return (' ' + el.className + ' ').indexOf(' ' + class_name + ' ') > -1;
}
/**
* gets the language for this block of code
*
* @param {Element} block
* @returns {string|null}
*/
function _getLanguageForBlock(block) {
// if this doesn't have a language but the parent does then use that
// this means if for example you have: <pre data-language="php">
// with a bunch of <code> blocks inside then you do not have
// to specify the language for each block
var language = _attr(block, 'data-language') || _attr(block.parentNode, 'data-language');
// this adds support for specifying language via a css class
// you can use the Google Code Prettify style: <pre class="lang-php">
// or the HTML5 style: <pre><code class="language-php">
if (!language) {
var pattern = /\blang(?:uage)?-(\w+)/,
match = block.className.match(pattern) || block.parentNode.className.match(pattern);
if (match) {
language = match[1];
}
}
return language;
}
/**
* makes sure html entities are always used for tags
*
* @param {string} code
* @returns {string}
*/
function _htmlEntities(code) {
return code.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/&(?![\w\#]+;)/g, '&amp;');
}
/**
* determines if a new match intersects with an existing one
*
* @param {number} start1 start position of existing match
* @param {number} end1 end position of existing match
* @param {number} start2 start position of new match
* @param {number} end2 end position of new match
* @returns {boolean}
*/
function _intersects(start1, end1, start2, end2) {
if (start2 >= start1 && start2 < end1) {
return true;
}
return end2 > start1 && end2 < end1;
}
/**
* determines if two different matches have complete overlap with each other
*
* @param {number} start1 start position of existing match
* @param {number} end1 end position of existing match
* @param {number} start2 start position of new match
* @param {number} end2 end position of new match
* @returns {boolean}
*/
function _hasCompleteOverlap(start1, end1, start2, end2) {
// if the starting and end positions are exactly the same
// then the first one should stay and this one should be ignored
if (start2 == start1 && end2 == end1) {
return false;
}
return start2 <= start1 && end2 >= end1;
}
/**
* determines if the match passed in falls inside of an existing match
* this prevents a regex pattern from matching inside of a bigger pattern
*
* @param {number} start - start position of new match
* @param {number} end - end position of new match
* @returns {boolean}
*/
function _matchIsInsideOtherMatch(start, end) {
for (var key in replacement_positions[CURRENT_LEVEL]) {
key = parseInt(key, 10);
// if this block completely overlaps with another block
// then we should remove the other block and return false
if (_hasCompleteOverlap(key, replacement_positions[CURRENT_LEVEL][key], start, end)) {
delete replacement_positions[CURRENT_LEVEL][key];
delete replacements[CURRENT_LEVEL][key];
}
if (_intersects(key, replacement_positions[CURRENT_LEVEL][key], start, end)) {
return true;
}
}
return false;
}
/**
* takes a string of code and wraps it in a span tag based on the name
*
* @param {string} name name of the pattern (ie keyword.regex)
* @param {string} code block of code to wrap
* @returns {string}
*/
function _wrapCodeInSpan(name, code) {
return '<span class="' + name.replace(/\./g, ' ') + (global_class ? ' ' + global_class : '') + '">' + code + '</span>';
}
/**
* finds out the position of group match for a regular expression
*
* @see http://stackoverflow.com/questions/1985594/how-to-find-index-of-groups-in-match
*
* @param {Object} match
* @param {number} group_number
* @returns {number}
*/
function _indexOfGroup(match, group_number) {
var index = 0,
i;
for (i = 1; i < group_number; ++i) {
if (match[i]) {
index += match[i].length;
}
}
return index;
}
/**
* matches a regex pattern against a block of code
* finds all matches that should be processed and stores the positions
* of where they should be replaced within the string
*
* this is where pretty much all the work is done but it should not
* be called directly
*
* @param {RegExp} pattern
* @param {string} code
* @returns void
*/
function _processPattern(regex, pattern, code, callback)
{
var match = regex.exec(code);
if (!match) {
return callback();
}
++match_counter;
// treat match 0 the same way as name
if (!pattern['name'] && typeof pattern['matches'][0] == 'string') {
pattern['name'] = pattern['matches'][0];
delete pattern['matches'][0];
}
var replacement = match[0],
start_pos = match.index,
end_pos = match[0].length + start_pos,
/**
* callback to process the next match of this pattern
*/
processNext = function() {
var nextCall = function() {
_processPattern(regex, pattern, code, callback);
};
// every 100 items we process let's call set timeout
// to let the ui breathe a little
return match_counter % 100 > 0 ? nextCall() : setTimeout(nextCall, 0);
};
// if this is not a child match and it falls inside of another
// match that already happened we should skip it and continue processing
if (_matchIsInsideOtherMatch(start_pos, end_pos)) {
return processNext();
}
/**
* callback for when a match was successfully processed
*
* @param {string} replacement
* @returns void
*/
var onMatchSuccess = function(replacement) {
// if this match has a name then wrap it in a span tag
if (pattern['name']) {
replacement = _wrapCodeInSpan(pattern['name'], replacement);
}
// console.log('LEVEL', CURRENT_LEVEL, 'replace', match[0], 'with', replacement, 'at position', start_pos, 'to', end_pos);
// store what needs to be replaced with what at this position
if (!replacements[CURRENT_LEVEL]) {
replacements[CURRENT_LEVEL] = {};
replacement_positions[CURRENT_LEVEL] = {};
}
replacements[CURRENT_LEVEL][start_pos] = {
'replace': match[0],
'with': replacement
};
// store the range of this match so we can use it for comparisons
// with other matches later
replacement_positions[CURRENT_LEVEL][start_pos] = end_pos;
// process the next match
processNext();
},
// if this pattern has sub matches for different groups in the regex
// then we should process them one at a time by rerunning them through
// this function to generate the new replacement
//
// we run through them backwards because the match position of earlier
// matches will not change depending on what gets replaced in later
// matches
group_keys = keys(pattern['matches']),
/**
* callback for processing a sub group
*
* @param {number} i
* @param {Array} group_keys
* @param {Function} callback
*/
processGroup = function(i, group_keys, callback) {
if (i >= group_keys.length) {
return callback(replacement);
}
var processNextGroup = function() {
processGroup(++i, group_keys, callback);
},
block = match[group_keys[i]];
// if there is no match here then move on
if (!block) {
return processNextGroup();
}
var group = pattern['matches'][group_keys[i]],
language = group['language'],
/**
* process group is what group we should use to actually process
* this match group
*
* for example if the subgroup pattern looks like this
* 2: {
* 'name': 'keyword',
* 'pattern': /true/g
* }
*
* then we use that as is, but if it looks like this
*
* 2: {
* 'name': 'keyword',
* 'matches': {
* 'name': 'special',
* 'pattern': /whatever/g
* }
* }
*
* we treat the 'matches' part as the pattern and keep
* the name around to wrap it with later
*/
process_group = group['name'] && group['matches'] ? group['matches'] : group,
/**
* takes the code block matched at this group, replaces it
* with the highlighted block, and optionally wraps it with
* a span with a name
*
* @param {string} block
* @param {string} replace_block
* @param {string|null} match_name
*/
_replaceAndContinue = function(block, replace_block, match_name) {
replacement = _replaceAtPosition(_indexOfGroup(match, group_keys[i]), block, match_name ? _wrapCodeInSpan(match_name, replace_block) : replace_block, replacement);
processNextGroup();
};
// if this is a sublanguage go and process the block using that language
if (language) {
return _highlightBlockForLanguage(block, language, function(code) {
_replaceAndContinue(block, code);
});
}
// if this is a string then this match is directly mapped to selector
// so all we have to do is wrap it in a span and continue
if (typeof group === 'string') {
return _replaceAndContinue(block, block, group);
}
// the process group can be a single pattern or an array of patterns
// _processCodeWithPatterns always expects an array so we convert it here
_processCodeWithPatterns(block, process_group.length ? process_group : [process_group], function(code) {
_replaceAndContinue(block, code, group['matches'] ? group['name'] : 0);
});
};
processGroup(0, group_keys, onMatchSuccess);
}
/**
* should a language bypass the default patterns?
*
* if you call Rainbow.extend() and pass true as the third argument
* it will bypass the defaults
*/
function _bypassDefaultPatterns(language)
{
return bypass_defaults[language];
}
/**
* returns a list of regex patterns for this language
*
* @param {string} language
* @returns {Array}
*/
function _getPatternsForLanguage(language) {
var patterns = language_patterns[language] || [],
default_patterns = language_patterns[DEFAULT_LANGUAGE] || [];
return _bypassDefaultPatterns(language) ? patterns : patterns.concat(default_patterns);
}
/**
* substring replace call to replace part of a string at a certain position
*
* @param {number} position the position where the replacement should happen
* @param {string} replace the text we want to replace
* @param {string} replace_with the text we want to replace it with
* @param {string} code the code we are doing the replacing in
* @returns {string}
*/
function _replaceAtPosition(position, replace, replace_with, code) {
var sub_string = code.substr(position);
return code.substr(0, position) + sub_string.replace(replace, replace_with);
}
/**
* sorts an object by index descending
*
* @param {Object} object
* @return {Array}
*/
function keys(object) {
var locations = [],
replacement,
pos;
for(var location in object) {
if (object.hasOwnProperty(location)) {
locations.push(location);
}
}
// numeric descending
return locations.sort(function(a, b) {
return b - a;
});
}
/**
* processes a block of code using specified patterns
*
* @param {string} code
* @param {Array} patterns
* @returns void
*/
function _processCodeWithPatterns(code, patterns, callback)
{
// we have to increase the level here so that the
// replacements will not conflict with each other when
// processing sub blocks of code
++CURRENT_LEVEL;
// patterns are processed one at a time through this function
function _workOnPatterns(patterns, i)
{
// still have patterns to process, keep going
if (i < patterns.length) {
return _processPattern(patterns[i]['pattern'], patterns[i], code, function() {
_workOnPatterns(patterns, ++i);
});
}
// we are done processing the patterns
// process the replacements and update the DOM
_processReplacements(code, function(code) {
// when we are done processing replacements
// we are done at this level so we can go back down
delete replacements[CURRENT_LEVEL];
delete replacement_positions[CURRENT_LEVEL];
--CURRENT_LEVEL;
callback(code);
});
}
_workOnPatterns(patterns, 0);
}
/**
* process replacements in the string of code to actually update the markup
*
* @param {string} code the code to process replacements in
* @param {Function} onComplete what to do when we are done processing
* @returns void
*/
function _processReplacements(code, onComplete) {
/**
* processes a single replacement
*
* @param {string} code
* @param {Array} positions
* @param {number} i
* @param {Function} onComplete
* @returns void
*/
function _processReplacement(code, positions, i, onComplete) {
if (i < positions.length) {
++replacement_counter;
var pos = positions[i],
replacement = replacements[CURRENT_LEVEL][pos];
code = _replaceAtPosition(pos, replacement['replace'], replacement['with'], code);
// process next function
var next = function() {
_processReplacement(code, positions, ++i, onComplete);
};
// use a timeout every 250 to not freeze up the UI
return replacement_counter % 250 > 0 ? next() : setTimeout(next, 0);
}
onComplete(code);
}
var string_positions = keys(replacements[CURRENT_LEVEL]);
_processReplacement(code, string_positions, 0, onComplete);
}
/**
* takes a string of code and highlights it according to the language specified
*
* @param {string} code
* @param {string} language
* @param {Function} onComplete
* @returns void
*/
function _highlightBlockForLanguage(code, language, onComplete) {
var patterns = _getPatternsForLanguage(language);
_processCodeWithPatterns(_htmlEntities(code), patterns, onComplete);
}
/**
* highlight an individual code block
*
* @param {Array} code_blocks
* @param {number} i
* @returns void
*/
function _highlightCodeBlock(code_blocks, i, onComplete) {
if (i < code_blocks.length) {
var block = code_blocks[i],
language = _getLanguageForBlock(block);
if (!_hasClass(block, 'rainbow') && language) {
language = language.toLowerCase();
_addClass(block, 'rainbow');
return _highlightBlockForLanguage(block.innerHTML, language, function(code) {
block.innerHTML = code;
// reset the replacement arrays
replacements = {};
replacement_positions = {};
// if you have a listener attached tell it that this block is now highlighted
if (onHighlight) {
onHighlight(block, language);
}
// process the next block
setTimeout(function() {
_highlightCodeBlock(code_blocks, ++i, onComplete);
}, 0);
});
}
return _highlightCodeBlock(code_blocks, ++i, onComplete);
}
if (onComplete) {
onComplete();
}
}
/**
* start highlighting all the code blocks
*
* @returns void
*/
function _highlight(node, onComplete) {
// the first argument can be an Event or a DOM Element
// I was originally checking instanceof Event but that makes it break
// when using mootools
//
// @see https://github.com/ccampbell/rainbow/issues/32
//
node = node && typeof node.getElementsByTagName == 'function' ? node : document;
var pre_blocks = node.getElementsByTagName('pre'),
code_blocks = node.getElementsByTagName('code'),
i,
final_blocks = [];
// @see http://stackoverflow.com/questions/2735067/how-to-convert-a-dom-node-list-to-an-array-in-javascript
// we are going to process all <code> blocks
for (i = 0; i < code_blocks.length; ++i) {
final_blocks.push(code_blocks[i]);
}
// loop through the pre blocks to see which ones we should add
for (i = 0; i < pre_blocks.length; ++i) {
// if the pre block has no code blocks then process it directly
if (!pre_blocks[i].getElementsByTagName('code').length) {
final_blocks.push(pre_blocks[i]);
}
}
_highlightCodeBlock(final_blocks, 0, onComplete);
}
/**
* public methods
*/
return {
/**
* extends the language pattern matches
*
* @param {*} language name of language
* @param {*} patterns array of patterns to add on
* @param {boolean|null} bypass if true this will bypass the default language patterns
*/
extend: function(language, patterns, bypass) {
// if there is only one argument then we assume that we want to
// extend the default language rules
if (arguments.length == 1) {
patterns = language;
language = DEFAULT_LANGUAGE;
}
bypass_defaults[language] = bypass;
language_patterns[language] = patterns.concat(language_patterns[language] || []);
},
/**
* call back to let you do stuff in your app after a piece of code has been highlighted
*
* @param {Function} callback
*/
onHighlight: function(callback) {
onHighlight = callback;
},
/**
* method to set a global class that will be applied to all spans
*
* @param {string} class_name
*/
addClass: function(class_name) {
global_class = class_name;
},
/**
* starts the magic rainbow
*
* @returns void
*/
color: function() {
// if you want to straight up highlight a string you can pass the string of code,
// the language, and a callback function
if (typeof arguments[0] == 'string') {
return _highlightBlockForLanguage(arguments[0], arguments[1], arguments[2]);
}
// if you pass a callback function then we rerun the color function
// on all the code and call the callback function on complete
if (typeof arguments[0] == 'function') {
return _highlight(0, arguments[0]);
}
// otherwise we use whatever node you passed in with an optional
// callback function as the second parameter
_highlight(arguments[0], arguments[1]);
}
};
}) ();
/**
* adds event listener to start highlighting
*/
(function() {
if (window.addEventListener) {
return window.addEventListener('load', Rainbow.color, false);
}
window.attachEvent('onload', Rainbow.color);
}) ();
// When using Google closure compiler in advanced mode some methods
// get renamed. This keeps a public reference to these methods so they can
// still be referenced from outside this library.
Rainbow["onHighlight"] = Rainbow.onHighlight;
Rainbow["addClass"] = Rainbow.addClass;
+66
View File
@@ -0,0 +1,66 @@
<!DOCTYPE html>
<html lang="en" ng-app="bootstrapDemoApp" id="top">
<head>
<title>Bootstrap for AngularUI</title>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.min.js"></script>
<script src="ui-bootstrap.js"></script>
<script>
angular.module('bootstrapDemoApp', ['ui.bootstrap', <%= templateModules %>]);
</script>
<script>
<%= templates %>
</script>
<link rel="stylesheet" href="assets/bootstrap.css" />
<link rel="stylesheet" href="assets/rainbow.css" />
</head>
<body>
<header class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="demo.html">ui-bootstrap</a>
<div class="nav-collapse">
<ul class="nav">
<% modules.forEach(function(module) { %>
<li><a href="#<%= module.name %>"><%= module.name %></a></li>
<% }); %>
</ul>
</div>
</div>
</div>
</header>
<div style="margin: 32px;"></div>
<div role="main">
<div class="container">
<% modules.forEach(function(module) { %>
<section id="<%= module.name %>">
<div class="page-header">
<h1><%= module.name %></h1>
</div>
<div class="row">
<div class="span6">
<%= module.html %>
</div>
<div class="span6">
<%= module.description %>
</div>
</div>
<div class="row">
<div class="span6">
<pre ng-non-bindable><code data-language="html"><%- module.html %></code></pre>
</div>
<div class="span6">
<pre ng-non-bindable><code data-language="javascript"><%- module.js %></code></pre>
</div>
</div>
</section>
<script><%= module.js %></script>
<% }); %>
</div>
</div>
<script src="assets/rainbow.js"></script>
<script src="assets/rainbow-generic.js"></script>
<script src="assets/rainbow-javascript.js"></script>
<script src="assets/rainbow-html.js"></script>
</body>
</html>
+2 -2
View File
@@ -2,9 +2,9 @@
"author": "https://github.com/angular-ui/bootstrap/graphs/contributors",
"name": "angular-ui-bootstrap",
"version": "0.1.0",
"dependencies": {
},
"dependencies": {},
"devDependencies": {
"node-markdown": "*",
"grunt": "~0.3.17"
}
}
+3
View File
@@ -0,0 +1,3 @@
Accordion is an angular-version of bootstrap's accordion.
**This** has *markdown* in it.
+10
View File
@@ -0,0 +1,10 @@
<li class="dropdown" ng-controller="DropdownCtrl">
<a class="dropdown-toggle">
Click me for a dropdown, yo!
</a>
<ul class="dropdown-menu">
<li ng-repeat="choice in items">
<a>{{choice}}</a>
</li>
</ul>
</li>
+7
View File
@@ -0,0 +1,7 @@
function DropdownCtrl($scope) {
$scope.items = [
"The first choice!",
"And another choice for you.",
"but wait! A third!"
];
}
+2
View File
@@ -0,0 +1,2 @@
DropdownToggle is a simple directive which will toggle a dropdown link on click. Simply put it on the `<a>` tag of the toggler-element, and it will find the nearest dropdown menu and toggle it when the `<a dropdown-toggle>` is clicked.
+6 -5
View File
@@ -6,10 +6,10 @@ basePath = '.';
files = [
JASMINE,
JASMINE_ADAPTER,
'test/lib/jquery-1.8.2.min.js',
'test/lib/angular-1.0.2.js',
'test/lib/angular-1.0.2-mocks.js',
'test/lib/helpers.js',
'misc/test-lib/jquery-1.8.2.min.js',
'misc/test-lib/angular-1.0.2.js',
'misc/test-lib/angular-1.0.2-mocks.js',
'misc/test-lib/helpers.js',
'src/**/*.js',
'template/**/*.js'
];
@@ -25,7 +25,7 @@ exclude = [
// - Opera
// - Safari
// - PhantomJS
browsers = ['Chrome'];
browsers = [];
// test results reporter to use
// possible values: dots || progress
@@ -50,3 +50,4 @@ autoWatch = true;
// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun = false;