aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/collect.js20
-rw-r--r--docs/directive.template2
-rw-r--r--docs/docs-scenario.js4
-rw-r--r--docs/formatter.template2
-rw-r--r--docs/index.html105
-rw-r--r--docs/overview.template2
-rw-r--r--docs/spec/collectSpec.js4
-rw-r--r--docs/validator.template2
-rw-r--r--docs/widget.template2
-rw-r--r--src/Angular.js191
-rw-r--r--src/Compiler.js51
-rw-r--r--src/directives.js18
-rw-r--r--src/markups.js51
-rw-r--r--src/parser.js2
-rw-r--r--src/widgets.js109
15 files changed, 519 insertions, 46 deletions
diff --git a/docs/collect.js b/docs/collect.js
index 08bb6be3..29d2a33f 100644
--- a/docs/collect.js
+++ b/docs/collect.js
@@ -7,8 +7,7 @@ var fs = require('fs'),
Showdown = require('showdown').Showdown;
var documentation = {
- section:{},
- all:[]
+ pages:[]
};
var SRC_DIR = "docs/";
@@ -24,9 +23,7 @@ var work = callback.chain(function () {
parseNgDoc(doc);
if (doc.ngdoc) {
delete doc.raw.text;
- var section = documentation.section;
- (section[doc.ngdoc] = section[doc.ngdoc] || []).push(doc);
- documentation.all.push(doc);
+ documentation.pages.push(doc);
console.log('Found:', doc.ngdoc + ':' + doc.shortName);
mergeTemplate(
doc.ngdoc + '.template',
@@ -38,6 +35,7 @@ var work = callback.chain(function () {
}).onError(function(err){
console.log('ERROR:', err.stack || err);
}).onDone(function(){
+ documentation.pages.sort(function(a,b){ return a.name == b.name ? 0:(a.name < b.name ? -1 : 1);});
mergeTemplate('docs-data.js', 'docs-data.js', {JSON:JSON.stringify(documentation)}, callback.chain());
mergeTemplate('docs-scenario.js', 'docs-scenario.js', documentation, callback.chain());
copy('docs-scenario.html', callback.chain());
@@ -128,12 +126,16 @@ function escapedHtmlTag(doc, name, value) {
function markdownTag(doc, name, value) {
doc[name] = markdown(value.replace(/^#/gm, '##')).
- replace(/\<pre\>/gmi, '<pre class="brush: xml; brush: js;" ng:non-bindable>');
+ replace(/\<pre\>/gmi, '<div ng:non-bindable><pre class="brush: js; html-script: true; toolbar: false;">').
+ replace(/\<\/pre\>/gmi, '</pre></div>');
}
function markdown(text) {
text = text.replace(/<angular\/>/gm, '<tt>&lt;angular/&gt;</tt>');
- return new Showdown.converter().makeHtml(text);
+ text = text.replace(/(angular\.[\w\._\-:]+)/gm, '<a href="#$1">$1</a>');
+ text = text.replace(/(`(ng:[\w\._\-]+)`)/gm, '<a href="#angular.directive.$2">$1</a>');
+ text = new Showdown.converter().makeHtml(text);
+ return text;
}
function markdownNoP(text) {
@@ -161,8 +163,8 @@ var TAG = {
element: valueTag,
name: function(doc, name, value) {
doc.name = value;
- var match = value.match(/^angular[\.\#](([^\.]+)\.(.*)|(.*))/);
- doc.shortName = match[3] || match[4];
+ doc.shortName = value.split(/\./).pop();
+ doc.depth = value.split(/\./).length - 1;
},
param: function(doc, name, value){
doc.param = doc.param || [];
diff --git a/docs/directive.template b/docs/directive.template
index 84b87ca7..a3a11056 100644
--- a/docs/directive.template
+++ b/docs/directive.template
@@ -35,3 +35,5 @@
<doc:scenario>{{{scenario}}}</doc:scenario>
</doc:example>
{{/example}}
+
+<script>SyntaxHighlighter.highlight();</script>
diff --git a/docs/docs-scenario.js b/docs/docs-scenario.js
index 08d0e91b..7383bd7f 100644
--- a/docs/docs-scenario.js
+++ b/docs/docs-scenario.js
@@ -1,4 +1,4 @@
-{{#all}}
+{{#pages}}
describe('{{name}}', function(){
beforeEach(function(){
browser().navigateTo('index.html#{{name}}');
@@ -6,4 +6,4 @@ describe('{{name}}', function(){
// {{raw.file}}:{{raw.line}}
{{{scenario}}}
});
-{{/all}}
+{{/pages}}
diff --git a/docs/formatter.template b/docs/formatter.template
index af95f729..b0871a2d 100644
--- a/docs/formatter.template
+++ b/docs/formatter.template
@@ -31,3 +31,5 @@ var modelValue = angular.formatter.{{shortName}}.parse(userInputString);
<doc:scenario>{{{scenario}}}</doc:scenario>
</doc:example>
{{/example}}
+
+<script>SyntaxHighlighter.highlight();</script>
diff --git a/docs/index.html b/docs/index.html
index b9dfebc0..2533caf3 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -26,18 +26,22 @@
};
this.getCurrentPartial = function(){
- if ($location.hashPath.match(/^angular\./)) {
- this.partialUrl = './' + $location.hashPath + '.html';
- }
- return this.partialUrl;
+ return './' + this.getTitle() + '.html';
}
this.getTitle = function(){
- if ($location.hashPath.match(/^angular\./)) {
- this.partialTitle = $location.hashPath;
+ var hashPath = $location.hashPath || 'angular';
+ if (hashPath.match(/^angular/)) {
+ this.partialTitle = hashPath;
}
return this.partialTitle;
}
+
+ this.getClass = function(page) {
+ return 'level-' + page.depth +
+ (page.name == this.getTitle() ? ' selected' : '');
+ };
+
}
</script>
<style type="text/css" media="screen">
@@ -47,12 +51,20 @@
margin: 0;
padding: 0;
}
-
- #sidebar {
- width: 15em;
- float: left;
+
+ a {
+ color: blue;
}
-
+
+ .nav-section {
+ margin-left: 1em;
+ margin-top: 0.5em;
+ }
+
+ .section-title {
+ float: right;
+ }
+
#header {
background-color: #F2C200;
margin-bottom: 1em;
@@ -95,11 +107,27 @@
#section h1 {
font-family: monospace;
margin-top: 0;
+ padding-bottom: 5px;
+ border-bottom: 1px solid #CCC;
}
- #sidebar h2 {
- font-size: 1.2em;
- margin: 5px 5px 5px 0.8em;
+ #sidebar {
+ width: 180px;
+ float: left;
+ padding-left: 1em;
+ }
+
+ #sidebar a {
+ text-decoration: none;
+ }
+
+ #sidebar a:hover {
+ text-decoration: underline;
+ }
+
+ #sidebar input {
+ width: 175px;
+ margin-bottom: 1em;
}
#sidebar ul {
@@ -110,18 +138,39 @@
}
#sidebar ul li {
- margin: 0;
- padding: 1px 1px 1px 1.5em;
}
+
+ #sidebar ul li a {
+ display: block;
+ padding: 2px 2px 2px 4px;
+ }
- .nav-section {
+ #sidebar ul li.selected a {
+ background-color: #DDD;
+ border-radius: 5px;
+ -moz-border-radius: 5px;
+ border: 1px solid #CCC;
+ padding: 1px 1px 1px 3px;
+ }
+
+ #sidebar ul li.level-0 {
+ margin-left: 0em;
+ font-weight: bold;
+ font-size: 1.2em;
+ }
+
+ #sidebar ul li.level-1 {
margin-left: 1em;
- margin-top: 0.5em;
+ margin-top: 5px;
+ font-size: 1.1em;
+ font-weight: bold;
}
- .section-title {
- float: right;
+ #sidebar ul li.level-2 {
+ margin-left: 2em;
+ font-family: monospace;
}
+
</style>
<title>&lt;angular/&gt;: {{getTitle()}}</title>
</head>
@@ -133,14 +182,14 @@
</h1>
</div>
<div id="sidebar" class="nav">
- <div ng:repeat="(name, type) in docs.section" class="nav-section">
- <h2>{{name}}</h2>
- <ul>
- <li ng:repeat="page in type.$orderBy('shortName')">
- <a href="{{getUrl(page)}}" ng:click="">{{page.shortName}}</a>
- </li>
- </ul>
- </div>
+ <div>
+ <input type="text" name="filterText" placeholder="search documentaiton"/>
+ <ul>
+ <li ng:repeat="page in docs.pages.$filter(filterText)" ng:class="getClass(page)">
+ <a href="{{getUrl(page)}}" ng:click="">{{page.shortName}}</a>
+ </li>
+ </ul>
+ </div>
</div>
<div id="section">
<ng:include src="getCurrentPartial()"></ng:include>
diff --git a/docs/overview.template b/docs/overview.template
index 7af05ff4..b05a9b1a 100644
--- a/docs/overview.template
+++ b/docs/overview.template
@@ -13,3 +13,5 @@
<doc:scenario>{{{scenario}}}</doc:scenario>
</doc:example>
{{/example}}
+
+<script>SyntaxHighlighter.highlight();</script>
diff --git a/docs/spec/collectSpec.js b/docs/spec/collectSpec.js
index 4df04b9f..b810f823 100644
--- a/docs/spec/collectSpec.js
+++ b/docs/spec/collectSpec.js
@@ -43,8 +43,8 @@ describe('collect', function(){
describe('@describe', function(){
it('should support pre blocks', function(){
- TAG.description(doc, 'description', '<pre class="brush: xml;" ng:non-bindable>abc</pre>');
- expect(doc.description).toEqual('<pre class="brush: xml;" ng:non-bindable>abc</pre>');
+ TAG.description(doc, 'description', '<pre>abc</pre>');
+ expect(doc.description).toEqual('<div ng:non-bindable><pre class="brush: js; html-script: true; toolbar: false;">abc</pre></div>');
});
describe('@example', function(){
diff --git a/docs/validator.template b/docs/validator.template
index 63d17072..f7b4e888 100644
--- a/docs/validator.template
+++ b/docs/validator.template
@@ -37,3 +37,5 @@ angular.validator.{{shortName}}({{paramFirst.name}}{{#paramRest}}{{^default}}, {
<doc:scenario>{{{scenario}}}</doc:scenario>
</doc:example>
{{/example}}
+
+<script>SyntaxHighlighter.highlight();</script>
diff --git a/docs/widget.template b/docs/widget.template
index 4a20629e..7ca3fcf4 100644
--- a/docs/widget.template
+++ b/docs/widget.template
@@ -37,3 +37,5 @@
<doc:scenario>{{{scenario}}}</doc:scenario>
</doc:example>
{{/example}}
+
+<script>SyntaxHighlighter.highlight();</script>
diff --git a/src/Angular.js b/src/Angular.js
index a1477e35..c40a7c67 100644
--- a/src/Angular.js
+++ b/src/Angular.js
@@ -1,3 +1,12 @@
+/**
+ * @ngdoc overview
+ * @name angular
+ * @namespace Namespace for angular.
+ * @description
+ * Hello world!
+ *
+ * @example
+ */
////////////////////////////////////
if (typeof document.getAttribute == $undefined)
@@ -91,6 +100,67 @@ var _undefined = undefined,
angular = window[$angular] || (window[$angular] = {}),
angularTextMarkup = extensionMap(angular, 'markup'),
angularAttrMarkup = extensionMap(angular, 'attrMarkup'),
+ /**
+ * @ngdoc overview
+ * @name angular.directive
+ * @namespace Namespace for all directives.
+ * @description
+ * A directive is an XML attribute that you can use in an existing HTML
+ * element type or in a DOM element type that you create using
+ * `angular.widget`, to modify that element's properties. You can use
+ * any number of directives per element.
+ *
+ * For example, you can add the ng:bind directive as an attribute of an
+ * HTML span element, as in `<span ng:bind="1+2"></span>`.
+ * How does this work? The compiler passes the attribute value `1+2`
+ * to the ng:bind extension, which in turn tells the Scope to watch
+ * that expression and report changes. On any change it sets the span
+ * text to the expression value.
+ *
+ * Here's how to define ng:bind:
+ * <pre>
+ angular.directive('ng:bind', function(expression, compiledElement) {
+ var compiler = this;
+ return function(linkElement) {
+ var currentScope = this;
+ currentScope.$watch(expression, function(value) {
+ linkElement.text(value);
+ });
+ };
+ });
+ * </pre>
+ *
+ * ## Directive vs. Attribute Widget
+ * Both attribute widgets and directives can compile a DOM element
+ * attribute. So why have two different ways to do the same thing?
+ * The answer is that order matters, but you have no control over
+ * the order in which attributes are read. To solve this we
+ * apply attribute widget before the directive.
+ *
+ * For example, consider this piece of HTML, which uses the
+ * directives `ng:repeat`, `ng:init`, and `ng:bind`:
+ * <pre>
+ <ul ng:init="people=['mike', 'mary']">
+ <li ng:repeat="person in people" ng:init="a=a+1" ng:bind="person"></li>
+ </ul>
+ * </pre>
+ *
+ * Notice that the order of execution matters here. We need to
+ * execute ng:repeat before we run the `ng:init` and `ng:bind`
+ * on the `<li/>;`. This is because we want to run the
+ * `ng:init="a=a+1` and `ng:bind="person"` once for each
+ * person in people. We could not have used directive to
+ * create this template because attributes are read in an
+ * unspecified order and there is no way of guaranteeing
+ * that the repeater attribute would execute first. Using
+ * the `ng:repeat` attribute directive ensures that we can
+ * transform the DOM element into a template.
+ *
+ * Widgets run before directives. Widgets are expected to
+ * manipulate the DOM whereas directives are not expected
+ * to manipulate the DOM, and they run last.
+ *
+ */
angularDirective = extensionMap(angular, 'directive'),
/**
@@ -332,7 +402,7 @@ var _undefined = undefined,
* The formatters are responsible for translating user readable text in an input widget to a
* data model stored in an application.
*
- * # Writting your own Fromatter
+ * # Writting your own Formatter
* Writing your own formatter is easy. Just register a pair of JavaScript functions with
* `angular.formatter`. One function for parsing user input text to the stored form,
* and one for formatting the stored data to user-visible text.
@@ -391,7 +461,7 @@ var _undefined = undefined,
*
*
* @scenario
- * it('should store reverse', function(){
+ * iit('should store reverse', function(){
* expect(element('.doc-example input:first').val()).toEqual('angular');
* expect(element('.doc-example input:last').val()).toEqual('RALUGNA');
*
@@ -399,7 +469,7 @@ var _undefined = undefined,
* $document.elements('.doc-example input:last').val('XYZ').trigger('change');
* done();
* });
- * expect(element('input:first').val()).toEqual('zyx');
+ * expect(element('.doc-example input:first').val()).toEqual('zyx');
* });
*/
angularFormatter = extensionMap(angular, 'formatter'),
@@ -767,6 +837,121 @@ function toKeyValue(obj) {
return parts.length ? parts.join('&') : '';
}
+/**
+ * @ngdoc directive
+ * @name angular.directive.ng:autobind
+ * @element script
+ *
+ * @description
+ * This section explains how to bootstrap your application to
+ * the <angular/> environment using either the
+ * `angular-x.x.x.js` or `angular-x.x.x.min.js` script.
+ *
+ * ## The bootstrap code
+ * Note that there are two versions of the bootstrap code that you can use:
+ *
+ * * `angular-x.x.x.js` - this file is unobfuscated, uncompressed, and thus
+ * human-readable. Note that despite the name of the file, there is
+ * no additional functionality built in to help you debug your
+ * application; it has the prefix debug because you can read
+ * the source code.
+ * * `angular-x.x.x.min.js` - this is a compressed and obfuscated version
+ * of `angular-x.x.x.js`. You might want to use this version if you
+ * want to load a smaller but functionally equivalent version of the
+ * code in your application. Note: this minified version was created
+ * using the Closure Compiler.
+ *
+ *
+ * ## Auto bind using: <tt>ng:autobind</tt>
+ * The simplest way to get an <angular/> application up and running is by
+ * inserting a script tag in your HTML file that bootstraps the
+ * `http://code.angularjs.org/angular-x.x.x.min.js` code and uses the
+ * special `ng:autobind` attribute, like in this snippet of HTML:
+ *
+ * <pre>
+ &lt;!doctype html&gt;
+ &lt;html xmlns:ng="http://angularjs.org"&gt;
+ &lt;head&gt;
+ &lt;script type="text/javascript" ng:autobind
+ src="http://code.angularjs.org/angular-0.9.3.min.js"&gt;&lt;/script&gt;
+ &lt;/head&gt;
+ &lt;body&gt;
+ Hello {{'world'}}!
+ &lt;/body&gt;
+ &lt;/html&gt;
+ * </pre>
+ *
+ * The `ng:autobind` attribute tells <angular/> to compile and manage
+ * the whole HTML document. The compilation occurs in the page's
+ * `onLoad` handler. Note that you don't need to explicitly add an
+ * `onLoad` event; auto bind mode takes care of all the magic for you.
+ *
+ * # Manual Bind
+ * Using autobind mode is a handy way to start using <angular/>, but
+ * advanced users who want more control over the initialization process
+ * might prefer to use manual bind mode instead.
+ *
+ * The best way to get started with manual bind mode is to look at the
+ * magic behind `ng:autobind` by writing out each step of the autobind
+ * process explicitly. Note that the following code is equivalent to
+ * the code in the previous section.
+ *
+ * <pre>
+ &lt;!doctype html&gt;
+ &lt;html xmlns:ng="http://angularjs.org"&gt;
+ &lt;head&gt;
+ &lt;script type="text/javascript" ng:autobind
+ src="http://code.angularjs.org/angular-0.9.3.min.js"&gt;&lt;/script&gt;
+ &lt;script type="text/javascript"&gt;
+ (function(window, previousOnLoad){
+ window.onload = function(){
+ try { (previousOnLoad||angular.noop)(); } catch(e) {}
+ angular.compile(window.document).$init();
+ };
+ })(window, window.onload);
+ &lt;/script&gt;
+ &lt;/head&gt;
+ &lt;body&gt;
+ Hello {{'World'}}!
+ &lt;/body&gt;
+ &lt;/html&gt;
+ * </pre>
+ *
+ * This is the sequence that your code should follow if you're writing
+ * your own manual binding code:
+ *
+ * * After the page is loaded, find the root of the HTML template,
+ * which is typically the root of the document.
+ * * Run the HTML compiler, which converts the templates into an
+ * executable, bi-directionally bound application.
+ *
+ * #XML Namespace
+ * *IMPORTANT:* When using <angular/> you must declare the ng namespace
+ * using the xmlsn tag. If you don't declare the namespace,
+ * Internet Explorer does not render widgets properly.
+ *
+ * <pre>
+ * &lt;html xmlns:ng="http://angularjs.org"&gt;
+ * </pre>
+ *
+ * # Create your own namespace
+ * If you want to define your own widgets, you must create your own
+ * namespace and use that namespace to form the fully qualified
+ * widget name. For example, you could map the alias `my` to your
+ * domain and create a widget called my:widget. To create your own
+ * namespace, simply add another xmlsn tag to your page, create an
+ * alias, and set it to your unique domain:
+ *
+ * <pre>
+ * &lt;html xmlns:ng="http://angularjs.org" xmlns:my="http://mydomain.com"&gt;
+ * </pre>
+ *
+ * # Global Object
+ * The <angular/> script creates a single global variable `angular`
+ * in the global namespace. All APIs are bound to fields of this
+ * global object.
+ *
+ */
function angularInit(config){
if (config.autobind) {
// TODO default to the source of angular.js
diff --git a/src/Compiler.js b/src/Compiler.js
index b0210247..680ead11 100644
--- a/src/Compiler.js
+++ b/src/Compiler.js
@@ -109,6 +109,57 @@ Compiler.prototype = {
};
},
+
+ /**
+ * @ngdoc directive
+ * @name angular.directive.ng:eval-order
+ *
+ * @description
+ * Normally the view is updated from top to bottom. This usually is
+ * not a problem, but under some circumstances the values for data
+ * is not available until after the full view is computed. If such
+ * values are needed before they are computed the order of
+ * evaluation can be change using ng:eval-order
+ *
+ * @element ANY
+ * @param {integer|string=} [priority=0] priority integer, or FIRST, LAST constant
+ *
+ * @exampleDescription
+ * try changing the invoice and see that the Total will lag in evaluation
+ * @example
+ <div>TOTAL: without ng:eval-order {{ items.$sum('total') | currency }}</div>
+ <div ng:eval-order='LAST'>TOTAL: with ng:eval-order {{ items.$sum('total') | currency }}</div>
+ <table ng:init="items=[{qty:1, cost:9.99, desc:'gadget'}]">
+ <tr>
+ <td>QTY</td>
+ <td>Description</td>
+ <td>Cost</td>
+ <td>Total</td>
+ <td></td>
+ </tr>
+ <tr ng:repeat="item in items">
+ <td><input name="item.qty"/></td>
+ <td><input name="item.desc"/></td>
+ <td><input name="item.cost"/></td>
+ <td>{{item.total = item.qty * item.cost | currency}}</td>
+ <td><a href="" ng:click="items.$remove(item)">X</a></td>
+ </tr>
+ <tr>
+ <td colspan="3"><a href="" ng:click="items.$add()">add</a></td>
+ <td>{{ items.$sum('total') | currency }}</td>
+ </tr>
+ </table>
+ *
+ * @scenario
+ it('should check ng:format', function(){
+ expect(using('.doc-example-live div:first').binding("items.$sum('total')")).toBe('$9.99');
+ expect(using('.doc-example-live div:last').binding("items.$sum('total')")).toBe('$9.99');
+ input('item.qty').enter('2');
+ expect(using('.doc-example-live div:first').binding("items.$sum('total')")).toBe('$9.99');
+ expect(using('.doc-example-live div:last').binding("items.$sum('total')")).toBe('$19.98');
+ });
+ */
+
templatize: function(element, elementIndex, priority){
var self = this,
widget,
diff --git a/src/directives.js b/src/directives.js
index 50901cbe..e359d6cc 100644
--- a/src/directives.js
+++ b/src/directives.js
@@ -155,7 +155,7 @@ angularDirective("ng:eval", function(expression){
* HTML element with the value of the given expression and kept it up to
* date when the expression's value changes. Usually you just write
* {{expression}} and let <angular/> compile it into
- * <span ng:bind="expression"></span> at bootstrap time.
+ * `<span ng:bind="expression"></span>` at bootstrap time.
*
* @element ANY
* @param {expression} expression to eval.
@@ -649,6 +649,7 @@ angularDirective("ng:submit", function(expression, element) {
expect(using('.doc-example-live').binding('counter')).toBe('3');
});
*/
+//TODO: delete me, since having watch in UI is logic in UI. (leftover form getangular)
angularDirective("ng:watch", function(expression, element){
return function(element){
var self = this;
@@ -862,15 +863,28 @@ angularDirective("ng:hide", function(expression, element){
* @name angular.directive.ng:style
*
* @description
+ * The ng:style allows you to set CSS style on an HTML element conditionally.
*
* @element ANY
- * @param {expression} expression to eval.
+ * @param {expression} expression which evals to an object whes key's are
+ * CSS style names and values are coresponding values for those
+ * CSS keys.
*
* @exampleDescription
* @example
+ <input type="button" value="set" ng:click="myStyle={color:'red'}">
+ <input type="button" value="clear" ng:click="myStyle={}">
+ <br/>
+ <span ng:style="myStyle">Sample Text</span>
+ <pre>myStyle={{myStyle}}</pre>
*
* @scenario
it('should check ng:style', function(){
+ expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)');
+ element('.doc-example-live :button[value=set]').click();
+ expect(element('.doc-example-live span').css('color')).toBe('red');
+ element('.doc-example-live :button[value=clear]').click();
+ expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)');
});
*/
angularDirective("ng:style", function(expression, element){
diff --git a/src/markups.js b/src/markups.js
index 159d7b12..57a209b7 100644
--- a/src/markups.js
+++ b/src/markups.js
@@ -68,6 +68,57 @@ angularTextMarkup('OPTION', function(text, textNode, parentElement){
}
});
+/**
+ * @ngdoc directive
+ * @name angular.directive.ng:href
+ *
+ * @description
+ * Using <angular/> markup like {{hash}} in an href attribute makes
+ * the page open to a wrong URL, ff the user clicks that link before
+ * angular has a chance to replace the {{hash}} with actual URL, the
+ * link will be broken and will most likely return a 404 error.
+ * The `ng:href` solves this problem by placing the `href` in the
+ * `ng:` namespace.
+ *
+ * The buggy way to write it:
+ * <pre>
+ * <a href="http://www.gravatar.com/avatar/{{hash}}"/>
+ * </pre>
+ *
+ * The correct way to write it:
+ * <pre>
+ * <a ng:href="http://www.gravatar.com/avatar/{{hash}}"/>
+ * </pre>
+ *
+ * @element ANY
+ * @param {template} template any string which can contain `{{}}` markup.
+ */
+
+/**
+ * @ngdoc directive
+ * @name angular.directive.ng:src
+ *
+ * @description
+ * Using <angular/> markup like `{{hash}}` in a `src` attribute doesn't
+ * work right: The browser will fetch from the URL with the literal
+ * text `{{hash}}` until <angular/> replaces the expression inside
+ * `{{hash}}`. The `ng:src` attribute solves this problem by placing
+ * the `src` attribute in the `ng:` namespace.
+ *
+ * The buggy way to write it:
+ * <pre>
+ * <img src="http://www.gravatar.com/avatar/{{hash}}"/>
+ * </pre>
+ *
+ * The correct way to write it:
+ * <pre>
+ * <img ng:src="http://www.gravatar.com/avatar/{{hash}}"/>
+ * </pre>
+ *
+ * @element ANY
+ * @param {template} template any string which can contain `{{}}` markup.
+ */
+
var NG_BIND_ATTR = 'ng:bind-attr';
var SPECIAL_ATTRS = {'ng:src': 'src', 'ng:href': 'href'};
angularAttrMarkup('{{}}', function(value, name, element){
diff --git a/src/parser.js b/src/parser.js
index 97d5740d..85b9c651 100644
--- a/src/parser.js
+++ b/src/parser.js
@@ -244,6 +244,7 @@ function parser(text, json){
statements: statements,
validator: validator,
filter: filter,
+ //TODO: delete me, since having watch in UI is logic in UI. (leftover form getangular)
watch: watch
};
@@ -624,6 +625,7 @@ function parser(text, json){
};
}
+ //TODO: delete me, since having watch in UI is logic in UI. (leftover form getangular)
function watch () {
var decl = [];
while(hasTokens()) {
diff --git a/src/widgets.js b/src/widgets.js
index e639e32b..0ebdd1d5 100644
--- a/src/widgets.js
+++ b/src/widgets.js
@@ -165,6 +165,82 @@ function compileValidator(expr) {
return parser(expr).validator()();
}
+/**
+ * @ngdoc directive
+ * @name angular.directive.ng:validate
+ *
+ * @description
+ * This directive validates the user input. If the input does not
+ * pass validation, this sets an `ng-validation-error` CSS class and
+ * an `ng:error` attribute on the input element. Visit validators to
+ * find out more.
+ *
+ * @element INPUT
+ * @css ng-validation-error
+ * @param {function} validation call this function to validate input
+ * falsy return means validation passed, To return error, simply
+ * return the error string.
+ *
+ * @exampleDescription
+ * @example
+ I don't validate: <input type="text" name="value"><br/>
+ I cannot be blank: <input type="text" name="value" ng:required><br/>
+ I need an integer or nothing: <input type="text" name="value" ng:validate="integer"><br/>
+ I must have an integer: <input type="text" name="value" ng:required ng:validate="integer"><br/>
+ *
+ * @scenario
+ it('should check ng:validate', function(){
+ expect(element('.doc-example-live :input:last').attr('className')).toMatch(/ng-validation-error/);
+ input('value').enter('123');
+ expect(element('.doc-example-live :input:last').attr('className')).not().toMatch(/ng-validation-error/);
+ });
+ */
+/**
+ * @ngdoc directive
+ * @name angular.directive.ng:required
+ *
+ * @description
+ * This directive requires the user input to be present.
+ *
+ * @element INPUT
+ * @css ng-validation-error
+ *
+ * @exampleDescription
+ * @example
+ I cannot be blank: <input type="text" name="value" ng:required><br/>
+ *
+ * @scenario
+ it('should check ng:required', function(){
+ expect(element('.doc-example-live :input').attr('className')).toMatch(/ng-validation-error/);
+ input('value').enter('123');
+ expect(element('.doc-example-live :input').attr('className')).not().toMatch(/ng-validation-error/);
+ });
+ */
+/**
+ * @ngdoc directive
+ * @name angular.directive.ng:format
+ *
+ * @description
+ * The `ng:format` directive formats stored data to user-readable
+ * text and parses the text back to the stored form. You might
+ * find this useful for example if you collect user input in a
+ * text field but need to store the data in the model as a list.
+ *
+ * @element INPUT
+ *
+ * @exampleDescription
+ * @example
+ Enter a comma separated list of items:
+ <input type="text" name="list" ng:format="list" value="table, chairs, plate">
+ <pre>list={{list}}</pre>
+ *
+ * @scenario
+ it('should check ng:format', function(){
+ expect(binding('list')).toBe('list=["table","chairs","plate"]');
+ input('list').enter(',,, a ,,,');
+ expect(binding('list')).toBe('list=["a"]');
+ });
+ */
function valueAccessor(scope, element) {
var validatorName = element.attr('ng:validate') || NOOP,
validator = compileValidator(validatorName),
@@ -320,6 +396,39 @@ function radioInit(model, view, element) {
view.set(modelValue);
}
+/**
+ * @ngdoc directive
+ * @name angular.directive.ng:change
+ *
+ * @description
+ * The directive executes an expression whenever the input widget changes.
+ *
+ * @element INPUT
+ * @param {expression} expression to execute.
+ *
+ * @exampleDescription
+ * @example
+ <div ng:init="checkboxCount=0; textCount=0"></div>
+ <input type="text" name="text" ng:change="textCount = 1 + textCount">
+ changeCount {{textCount}}<br/>
+ <input type="checkbox" name="checkbox" ng:change="checkboxCount = 1 + checkboxCount">
+ changeCount {{checkboxCount}}<br/>
+ *
+ * @scenario
+ it('should check ng:change', function(){
+ expect(binding('textCount')).toBe('0');
+ expect(binding('checkboxCount')).toBe('0');
+
+ using('.doc-example-live').input('text').enter('abc');
+ expect(binding('textCount')).toBe('1');
+ expect(binding('checkboxCount')).toBe('0');
+
+
+ using('.doc-example-live').input('checkbox').check();
+ expect(binding('textCount')).toBe('1');
+ expect(binding('checkboxCount')).toBe('1');
+ });
+ */
function inputWidget(events, modelAccessor, viewAccessor, initFn) {
return function(element) {
var scope = this,