');
+ var addField = function (name, value) {
+ var input = angular.element('');
+ input.attr('value', value);
+ form.append(input);
+ };
+
+ var indexContent = function (content, version) {
+ return '\n' +
+ '\n' +
+ ' \n' +
+ ' \n' +
+ ' \n' +
+ ' \n' +
+ ' \n' +
+ ' \n' +
+ ' \n\n' +
+ content + '\n' +
+ ' \n' +
+ '\n';
+ };
+
+ var scriptContent = function(content) {
+ return "angular.module('plunker', ['placeholders']);" + "\n" + content;
+ };
+
+ addField('description', 'http://joshdmiller.github.com/angular-placeholders/');
+ addField('files[index.html]', indexContent(content.markup, version));
+ addField('files[example.js]', scriptContent(content.javascript));
+
+ $document.find('body').append(form);
+ form[0].submit();
+ form.remove();
+ };
+ })
+
+ .controller('PlunkerCtrl', function ($scope, plunkGenerator) {
+
+ $scope.content = {};
+
+ $scope.edit = function (version, module, plunker) {
+ plunkGenerator(version, module, $scope.content);
+ };
+ })
+
+ .directive('plunkerContent', function () {
+ return {
+ link:function (scope, element, attrs) {
+ scope.$parent.content[attrs.plunkerContent] = element.text();
+ }
+ };
+ });
diff --git a/dist/assets/rainbow-generic.js b/dist/assets/rainbow-generic.js
new file mode 100644
index 0000000..bfc5477
--- /dev/null
+++ b/dist/assets/rainbow-generic.js
@@ -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
+ }
+]);
diff --git a/dist/assets/rainbow-html.js b/dist/assets/rainbow-html.js
new file mode 100644
index 0000000..f221888
--- /dev/null
+++ b/dist/assets/rainbow-html.js
@@ -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': /<\?=?(?!xml)(php)?([\s\S]*?)(\?>)/gm
+ },
+ {
+ 'name': 'source.css.embedded',
+ 'matches': {
+ 0: {
+ 'language': 'css'
+ }
+ },
+ 'pattern': /<style(.*?)>([\s\S]*?)<\/style>/gm
+ },
+ {
+ 'name': 'source.js.embedded',
+ 'matches': {
+ 0: {
+ 'language': 'javascript'
+ }
+ },
+ 'pattern': /<script(?! src)(.*?)>([\s\S]*?)<\/script>/gm
+ },
+ {
+ 'name': 'comment.html',
+ 'pattern': /<\!--[\S\s]*?-->/g
+ },
+ {
+ 'matches': {
+ 1: 'support.tag.open',
+ 2: 'support.tag.cclose'
+ },
+ 'pattern': /(<)|(\/?\??>)/g
+ },
+ {
+ 'name': 'support.tag',
+ 'matches': {
+ 1: 'support.tag',
+ 2: 'support.tag.special',
+ 3: 'support.tag-name'
+ },
+ 'pattern': /(<\??)(\/|\!?)(\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|>)(?![\s\S]*<)/g
+ }
+], true);
diff --git a/dist/assets/rainbow-javascript.js b/dist/assets/rainbow-javascript.js
new file mode 100644
index 0000000..83b0d46
--- /dev/null
+++ b/dist/assets/rainbow-javascript.js
@@ -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': /(<\/?)(script.*?)(>)/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.cclose',
+ 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
+ }
+]);
diff --git a/dist/assets/rainbow.css b/dist/assets/rainbow.css
new file mode 100644
index 0000000..088f065
--- /dev/null
+++ b/dist/assets/rainbow.css
@@ -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;
+}
diff --git a/dist/assets/rainbow.js b/dist/assets/rainbow.js
new file mode 100644
index 0000000..ed8894d
--- /dev/null
+++ b/dist/assets/rainbow.js
@@ -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:
+ // with a bunch of 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:
+ // or the HTML5 style:
+ 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, '>').replace(/&(?![\w\#]+;)/g, '&');
+ }
+
+ /**
+ * 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 '' + code + '';
+ }
+
+ /**
+ * 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 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;
diff --git a/dist/assets/ui-bootstrap-tpls-0.1.0-SNAPSHOT.min.js b/dist/assets/ui-bootstrap-tpls-0.1.0-SNAPSHOT.min.js
new file mode 100644
index 0000000..3d093d6
--- /dev/null
+++ b/dist/assets/ui-bootstrap-tpls-0.1.0-SNAPSHOT.min.js
@@ -0,0 +1 @@
+angular.module("ui.bootstrap",["ui.bootstrap.tpls","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.carousel","ui.bootstrap.collapse","ui.bootstrap.dialog","ui.bootstrap.dropdownToggle","ui.bootstrap.modal","ui.bootstrap.pagination","ui.bootstrap.tabs","ui.bootstrap.tooltip","ui.bootstrap.transition"]),angular.module("ui.bootstrap.accordion",["ui.bootstrap.collapse"]),angular.module("ui.bootstrap.accordion").controller("AccordionController",["$scope","$attrs",function(e,t){this.groups=[],this.closeOthers=function(n){(angular.isUndefined(t.closeOthers)||e.$eval(t.closeOthers))&&angular.forEach(this.groups,function(e){e!==n&&(e.isOpen=!1)})},this.addGroup=function(e){var t=this;this.groups.push(e),e.$on("$destroy",function(n){t.removeGroup(e)})},this.removeGroup=function(e){var t=this.groups.indexOf(e);t!==-1&&this.groups.splice(this.groups.indexOf(e),1)}}]),angular.module("ui.bootstrap.accordion").directive("accordion",function(){return{restrict:"E",controller:"AccordionController",transclude:!0,replace:!1,templateUrl:"template/accordion/accordion.html"}}),angular.module("ui.bootstrap.accordion").directive("accordionGroup",["$parse","$transition","$timeout",function(e,t,n){return{require:"^accordion",restrict:"E",transclude:!0,replace:!0,templateUrl:"template/accordion/accordion-group.html",scope:{heading:"@"},link:function(t,n,r,i){var s,o;i.addGroup(t),t.isOpen=!1,r.isOpen&&(s=e(r.isOpen),o=s.assign,t.$watch(function(){return s(t.$parent)},function(n){t.isOpen=n}),t.isOpen=s?s(t.$parent):!1),t.$watch("isOpen",function(e){e&&i.closeOthers(t),o&&o(t.$parent,e)})}}}]),angular.module("ui.bootstrap.alert",[]).directive("alert",function(){return{restrict:"E",templateUrl:"template/alert/alert.html",transclude:!0,scope:{type:"=",close:"&"},link:function(e,t,n){e.type=e.type||"info",e.dismiss=function(){e.close()}}}}),angular.module("ui.bootstrap.carousel",["ui.bootstrap.transition"]).controller("CarouselController",["$scope","$timeout","$transition","$q",function(e,t,n,r){function f(){function n(){a?(e.next(),f()):e.pause()}u&&t.cancel(u);var r=+e.interval;!isNaN(r)&&r>=0&&(u=t(n,r))}var i=this,s=i.slides=[],o=-1,u,a;i.currentSlide=null,i.select=function(r,u){function l(){i.currentSlide&&angular.isString(u)&&!e.noTransition&&r.$element?(r.$element.addClass(u),r.$element[0].offsetWidth=r.$element[0].offsetWidth,angular.forEach(s,function(e){angular.extend(e,{direction:"",entering:!1,leaving:!1,active:!1})}),angular.extend(r,{direction:u,active:!0,entering:!0}),angular.extend(i.currentSlide||{},{direction:u,leaving:!0}),e.$currentTransition=n(r.$element,{}),function(t,n){e.$currentTransition.then(function(){c(t,n)},function(){c(t,n)})}(r,i.currentSlide)):c(r,i.currentSlide),i.currentSlide=r,o=a,f()}function c(t,n){angular.extend(t,{direction:"",active:!0,leaving:!1,entering:!1}),angular.extend(n||{},{direction:"",active:!1,leaving:!1,entering:!1}),e.$currentTransition=null}var a=s.indexOf(r);u===undefined&&(u=a>o?"next":"prev"),r&&r!==i.currentSlide&&(e.$currentTransition?(e.$currentTransition.cancel(),t(l)):l())},i.indexOfSlide=function(e){return s.indexOf(e)},e.next=function(){var e=(o+1)%s.length;return i.select(s[e],"next")},e.prev=function(){var e=o-1<0?s.length-1:o-1;return i.select(s[e],"prev")},e.$watch("interval",f),e.play=function(){a||(a=!0,f())},e.pause=function(){a=!1,u&&t.cancel(u)},i.addSlide=function(t,n){t.$element=n,s.push(t),s.length===1||t.active?(i.select(s[s.length-1]),s.length==1&&e.play()):t.active=!1},i.removeSlide=function(e){var t=s.indexOf(e);s.splice(t,1),s.length>0&&e.active&&(t>=s.length?i.select(s[t-1]):i.select(s[t]))}}]).directive("carousel",[function(){return{restrict:"EA",transclude:!0,replace:!0,controller:"CarouselController",require:"carousel",templateUrl:"template/carousel/carousel.html",scope:{interval:"=",noTransition:"="}}}]).directive("slide",[function(){return{require:"^carousel",restrict:"EA",transclude:!0,replace:!0,templateUrl:"template/carousel/slide.html",scope:{active:"="},link:function(e,t,n,r){r.addSlide(e,t),e.$on("$destroy",function(){r.removeSlide(e)}),e.$watch("active",function(t){t&&r.select(e)})}}}]),angular.module("ui.bootstrap.collapse",["ui.bootstrap.transition"]).directive("collapse",["$transition",function(e){var t=function(e,t,n){t.removeClass("collapse"),t.css({height:n});var r=t[0].offsetWidth;t.addClass("collapse")};return{link:function(n,r,i){var s;n.$watch(i.collapse,function(e){e?f():a()});var o,u=function(t){return o&&o.cancel(),o=e(r,t),o.then(function(){o=undefined},function(){o=undefined}),o},a=function(){u({height:r[0].scrollHeight+"px"}).then(function(){s||t(n,r,"auto")}),s=!1},f=function(){s=!0,t(n,r,r[0].scrollHeight+"px"),u({height:"0"})}}}}]);var dialogModule=angular.module("ui.bootstrap.dialog",["ui.bootstrap.transition"]);dialogModule.controller("MessageBoxController",["$scope","dialog","model",function(e,t,n){e.title=n.title,e.message=n.message,e.buttons=n.buttons,e.close=function(e){t.close(e)}}]),dialogModule.provider("$dialog",function(){var e={backdrop:!0,modalClass:"modal",backdropClass:"modal-backdrop",transitionClass:"fade",triggerClass:"in",resolve:{},backdropFade:!1,modalFade:!1,keyboard:!0,backdropClick:!0},t={};this.options=function(e){t=e},this.$get=["$http","$document","$compile","$rootScope","$controller","$templateCache","$q","$transition",function(n,r,i,s,o,u,a,f){function c(e){var t=angular.element("
");return t.addClass(e),t}function h(n){var r=this,i=this.options=angular.extend({},e,t,n);this.backdropEl=c(i.backdropClass),i.backdropFade&&(this.backdropEl.addClass(i.transitionClass),this.backdropEl.removeClass(i.triggerClass)),this.modalEl=c(i.modalClass),i.modalFade&&(this.modalEl.addClass(i.transitionClass),this.modalEl.removeClass(i.triggerClass)),this.handledEscapeKey=function(e){e.which===27&&(r.close(),e.preventDefault(),r.$scope.$apply())},this.handleBackDropClick=function(e){r.close(),e.preventDefault(),r.$scope.$apply()}}var l=r.find("body");return h.prototype.isOpen=function(){return this._open},h.prototype.open=function(e,t){var n=this,r=this.options;e&&(r.templateUrl=e),t&&(r.controller=t);if(!r.template&&!r.templateUrl)throw new Error("Dialog.open expected template or templateUrl, neither found. Use options or open method to specify them.");return this._loadResolves().then(function(e){var t=e.$scope=n.$scope=s.$new();n.modalEl.html(e.$template);if(n.options.controller){var r=o(n.options.controller,e);n.modalEl.contents().data("ngControllerController",r)}i(n.modalEl.contents())(t),n._addElementsToDom(),setTimeout(function(){n.options.modalFade&&n.modalEl.addClass(n.options.triggerClass),n.options.backdropFade&&n.backdropEl.addClass(n.options.triggerClass)}),n._bindEvents()}),this.deferred=a.defer(),this.deferred.promise},h.prototype.close=function(e){function i(e){e.removeClass(t.options.triggerClass)}function s(){t._open&&t._onCloseComplete(e)}var t=this,n=this._getFadingElements();if(n.length>0){for(var r=n.length-1;r>=0;r--)f(n[r],i).then(s);return}this._onCloseComplete(e)},h.prototype._getFadingElements=function(){var e=[];return this.options.modalFade&&e.push(this.modalEl),this.options.backdropFade&&e.push(this.backdropEl),e},h.prototype._bindEvents=function(){this.options.keyboard&&l.bind("keydown",this.handledEscapeKey),this.options.backdrop&&this.options.backdropClick&&this.backdropEl.bind("click",this.handleBackDropClick)},h.prototype._unbindEvents=function(){this.options.keyboard&&l.unbind("keydown",this.handledEscapeKey),this.options.backdrop&&this.options.backdropClick&&this.backdropEl.unbind("click",this.handleBackDropClick)},h.prototype._onCloseComplete=function(e){this._removeElementsFromDom(),this._unbindEvents(),this.deferred.resolve(e)},h.prototype._addElementsToDom=function(){l.append(this.modalEl),this.options.backdrop&&l.append(this.backdropEl),this._open=!0},h.prototype._removeElementsFromDom=function(){this.modalEl.remove(),this.options.backdrop&&this.backdropEl.remove(),this._open=!1},h.prototype._loadResolves=function(){var e=[],t=[],r,i=this;return this.options.template?r=a.when(this.options.template):this.options.templateUrl&&(r=n.get(this.options.templateUrl,{cache:u}).then(function(e){return e.data})),angular.forEach(this.options.resolve||[],function(n,r){t.push(r),e.push(n)}),t.push("$template"),e.push(r),a.all(e).then(function(e){var n={};return angular.forEach(e,function(e,r){n[t[r]]=e}),n.dialog=i,n})},{dialog:function(e){return new h(e)},messageBox:function(e,t,n){return new h({templateUrl:"template/dialog/message.html",controller:"MessageBoxController",resolve:{model:{title:e,message:t,buttons:n}}})}}}]}),angular.module("ui.bootstrap.dropdownToggle",[]).directive("dropdownToggle",["$document","$location","$window",function(e,t,n){var r=null,i;return{restrict:"CA",link:function(n,s,o){n.$watch(function(){return t.path()},function(){i&&i()}),s.parent().bind("click",function(e){i&&i()}),s.bind("click",function(t){t.preventDefault(),t.stopPropagation();var n=!1;r&&(n=r===s,i()),n||(s.parent().addClass("open"),r=s,i=function(t){t&&(t.preventDefault(),t.stopPropagation()),e.unbind("click",i),s.parent().removeClass("open"),i=null,r=null},e.bind("click",i))})}}}]),angular.module("ui.bootstrap.modal",[]).directive("modal",["$parse",function(e){var t,n=angular.element(document.getElementsByTagName("body")[0]),r={backdrop:!0,escape:!0};return{restrict:"EA",link:function(i,s,o){function l(e){i.$apply(function(){model.assign(i,e)})}function c(e){e.which===27&&f()}function h(){f()}function p(){u.escape&&n.unbind("keyup",c),u.backdrop&&(t.css("display","none").removeClass("in"),t.unbind("click",h)),s.css("display","none").removeClass("in"),n.removeClass("modal-open")}function d(){u.escape&&n.bind("keyup",c),u.backdrop&&(t.css("display","block").addClass("in"),t.bind("click",h)),s.css("display","block").addClass("in"),n.addClass("modal-open")}var u=angular.extend(r,i.$eval(o.uiOptions||o.bsOptions||o.options)),a=o.modal||o.show,f;o.close?f=function(){i.$apply(o.close)}:f=function(){i.$apply(function(){e(a).assign(i,!1)})},s.addClass("modal"),u.backdrop&&!t&&(t=angular.element(''),t.css("display","none"),n.append(t)),i.$watch(a,function(e,t){e?d():p()})}}}]),angular.module("ui.bootstrap.pagination",[]).directive("pagination",function(){return{restrict:"E",scope:{numPages:"=",currentPage:"=",maxSize:"=",onSelectPage:"&"},templateUrl:"template/pagination/pagination.html",replace:!0,link:function(e){e.$watch("numPages + currentPage + maxSize",function(){e.pages=[],angular.isDefined(e.maxSize)&&e.maxSize>e.numPages&&(e.maxSize=e.numPages);var t=e.maxSize?e.maxSize:e.numPages,n=e.currentPage-Math.floor(t/2);n<1&&(n=1),n+t-1>e.numPages&&(n-=n+t-1-e.numPages);for(var r=0;re.numPages&&e.selectPage(e.numPages)}),e.noPrevious=function(){return e.currentPage===1},e.noNext=function(){return e.currentPage===e.numPages},e.isActive=function(t){return e.currentPage===t},e.selectPage=function(t){e.isActive(t)||(e.currentPage=t,e.onSelectPage({page:t}))},e.selectPrevious=function(){e.noPrevious()||e.selectPage(e.currentPage-1)},e.selectNext=function(){e.noNext()||e.selectPage(e.currentPage+1)}}}}),angular.module("ui.bootstrap.tabs",[]).controller("TabsController",["$scope","$element",function(e,t){var n=e.panes=[];e.select=function(t){angular.forEach(n,function(e){e.selected=!1}),t.selected=!0},this.addPane=function(r){n.length||e.select(r),n.push(r)},this.removePane=function(r){var i=n.indexOf(r);n.splice(i,1),r.selected&&n.length>0&&e.select(n[i
The phImg directive creates client-side placeholder images in any
+size. The directive creates a PNG image using the HTML5 canvas library and
+uses the generated client-side URL as either (a) the src attribute, if the
+element is an img, or (b) the css-background for all other types of
+elements.
+
+
The directive takes a single eponymous attribute that specifies the dimensions
+of the image to create; the expected format is "100x100".
var ImageDemoCtrl = function ( $scope ) {
+ $scope.imageDimension = '550x300';
+ $scope.imageLabel = 'labels are awesome!';
+};
+
+
+
+
+
+
+
+
+
+
Txt
+
+
+
+
+
Here's a few sentences
+
# Sentences:
+
+
+
And a few paragraphs
+
# Paragraphs:
+
+
+
And combining both:
+
# Paragraphs:
+
+
+
+
+
+
The phTxt directive dynamically inserts "Lorem ipsum"-style text
+into an element. It can work as either an element or an attribute.
+
+
phTxt accepts a single value that can express the number of desired paragraphs
+and/or sentences. The format is #p#s, where the numbers represent the number
+of requested paragraphs and sentences, respectively. Order is irrelevant.
+
+
If both are provided, the element will contain the requested number of
+paragraphs, each with the requested number of sentences.
+
+
If just the number of paragraphs is provided, the number of sentences will be
+random for each paragraph. If just the number of sentences is provided, no
+paragraphs will be generated - just the specified number of sentences.
+
+
If neither are provided, the default behavior is a random number of paragraphs,
+each with a random number of sentences.