'use strict'; /** * @workInProgress * @ngdoc overview * @name angular.directive * @description * * Custom attributes for DOM elements. Directives modify the behavior of the element they are * specified in, but are not intended to add elements to the DOM as are * {@link angular.widget widgets}. * * Following is the list of built-in angular directives: * * * {@link angular.directive.ng:bind ng:bind} - Creates a data-binding between HTML text value and * data model. * * {@link angular.directive.ng:bind-attr ng:bind-attr} - Creates a data-binding as in `ng:bind`, * but uses JSON key / value pairs. * * {@link angular.directive.ng:bind-template ng:bind-template} - Replaces text value of an element * with a specified template. * * {@link angular.directive.ng:change ng:change} - Executes an expression when the value of an * input widget changes. * * {@link angular.directive.ng:class ng:class} - Conditionally set CSS class on an element. * * {@link angular.directive.ng:class-even ng:class-even} - Like `ng:class`, but works in * conjunction with {@link angular.widget.@ng:repeat} to affect even rows in a collection. * * {@link angular.directive.ng:class-odd ng:class-odd} - Like `ng:class`, but works with {@link * angular.widget.@ng:repeat} to affect odd rows. * * {@link angular.directive.ng:click ng:click} - Executes custom behavior when element is clicked. * * {@link angular.directive.ng:controller ng:controller} - Creates a scope object linked to the * DOM element and assigns behavior to the scope. * * {@link angular.directive.ng:eval ng:eval} - Executes a binding but blocks output. * * {@link angular.directive.ng:eval-order ng:eval-order} - Change evaluation order when updating * the view. * * {@link angular.directive.ng:hide ng:hide} - Conditionally hides a portion of HTML. * * {@link angular.directive.ng:href ng:href} - Places an href in the angular namespace. * * {@link angular.directive.ng:init} - Initialization tasks run before a template is executed. * * {@link angular.directive.ng:show ng:show} - Conditionally displays a portion of HTML. * * {@link angular.directive.ng:src ng:src} - Places a `src` attribute into the angular namespace. * * {@link angular.directive.ng:style ng:style} - Conditionally set CSS styles on an element. * * {@link angular.directive.ng:submit} - Binds angular expressions to `onSubmit` events. * * For more information about how angular directives work, and how to create your own directives, * see {@link guide/dev_guide.compiler.directives Understanding Angular Directives} in the angular * Developer Guide. */ /** * @workInProgress * @ngdoc directive * @name angular.directive.ng:init * * @description * The `ng:init` attribute specifies initialization tasks to be executed * before the template enters execution mode during bootstrap. * * @element ANY * @param {expression} expression {@link guide/dev_guide.expressions Expression} to eval. * * @example
{{greeting}} {{person}}!
it('should check greeting', function(){ expect(binding('greeting')).toBe('Hello'); expect(binding('person')).toBe('World'); });
*/ angularDirective("ng:init", function(expression){ return function(element){ this.$tryEval(expression, element); }; }); /** * @workInProgress * @ngdoc directive * @name angular.directive.ng:controller * * @description * The `ng:controller` directive assigns behavior to a scope. This is a key aspect of how angular * supports the principles behind the Model-View-Controller design pattern. * * MVC components in angular: * * * Model — The Model is data in scope properties; scopes are attached to the DOM. * * View — The template (HTML with data bindings) is rendered into the View. * * Controller — The `ng:controller` directive specifies a Controller class; the class has * methods that typically express the business logic behind the application. * * Note that an alternative way to define controllers is via the `{@link angular.service.$route}` * service. * * @element ANY * @param {expression} expression Name of a globally accessible constructor function or an * {@link guide/dev_guide.expressions expression} that on the current scope evaluates to a * constructor function. * * @example * Here is a simple form for editing user contact information. Adding, removing, clearing, and * greeting are methods declared on the controller (see source tab). These methods can * easily be called from the angular markup. Notice that the scope becomes the `this` for the * controller's instance. This allows for easy access to the view data from the controller. Also * notice that any changes to the data are automatically reflected in the View without the need * for a manual update.
Name: [ greet ]
Contact:
it('should check controller', function(){ expect(element('.doc-example-live div>:input').val()).toBe('John Smith'); expect(element('.doc-example-live li[ng\\:repeat-index="0"] input').val()).toBe('408 555 1212'); expect(element('.doc-example-live li[ng\\:repeat-index="1"] input').val()).toBe('john.smith@example.org'); element('.doc-example-live li:first a:contains("clear")').click(); expect(element('.doc-example-live li:first input').val()).toBe(''); element('.doc-example-live li:last a:contains("add")').click(); expect(element('.doc-example-live li[ng\\:repeat-index="2"] input').val()).toBe('yourname@example.org'); });
*/ angularDirective("ng:controller", function(expression){ this.scope(true); return function(element){ var controller = getter(window, expression, true) || getter(this, expression, true); if (!controller) throw "Can not find '"+expression+"' controller."; if (!isFunction(controller)) throw "Reference '"+expression+"' is not a class."; this.$become(controller); }; }); /** * @workInProgress * @ngdoc directive * @name angular.directive.ng:eval * * @description * The `ng:eval` allows you to execute a binding which has side effects * without displaying the result to the user. * * @element ANY * @param {expression} expression {@link guide/dev_guide.expressions Expression} to eval. * * @example * Notice that `{{` `obj.multiplied = obj.a * obj.b` `}}` has a side effect of assigning * a value to `obj.multiplied` and displaying the result to the user. Sometimes, * however, it is desirable to execute a side effect without showing the value to * the user. In such a case `ng:eval` allows you to execute code without updating * the display. * = {{obj.multiplied = obj.a * obj.b}}
obj.divide = {{obj.divide}}
obj.updateCount = {{obj.updateCount}}
it('should check eval', function(){ expect(binding('obj.divide')).toBe('3'); expect(binding('obj.updateCount')).toBe('2'); input('obj.a').enter('12'); expect(binding('obj.divide')).toBe('6'); expect(binding('obj.updateCount')).toBe('3'); });
*/ angularDirective("ng:eval", function(expression){ return function(element){ this.$onEval(expression, element); }; }); /** * @workInProgress * @ngdoc directive * @name angular.directive.ng:bind * * @description * The `ng:bind` attribute asks angular to replace the text content of this * HTML element with the value of the given expression, and to keep the text * content up to date when the expression's value changes. Usually you would * just write `{{ expression }}` and let angular compile it into * `` at bootstrap time. * * @element ANY * @param {expression} expression {@link guide/dev_guide.expressions Expression} to eval. * * @example * You can try it right here: enter text in the text box and watch the greeting change. Enter name:
Hello !
it('should check ng:bind', function(){ expect(using('.doc-example-live').binding('name')).toBe('Whirled'); using('.doc-example-live').input('name').enter('world'); expect(using('.doc-example-live').binding('name')).toBe('world'); });
*/ angularDirective("ng:bind", function(expression, element){ element.addClass('ng-binding'); return function(element) { var lastValue = noop, lastError = noop; this.$onEval(function() { var error, value, html, isHtml, isDomElement, oldElement = this.hasOwnProperty($$element) ? this.$element : undefined; this.$element = element; value = this.$tryEval(expression, function(e){ error = formatError(e); }); this.$element = oldElement; // If we are HTML then save the raw HTML data so that we don't // recompute sanitization since it is expensive. // TODO: turn this into a more generic way to compute this if (isHtml = (value instanceof HTML)) value = (html = value).html; if (lastValue === value && lastError == error) return; isDomElement = isElement(value); if (!isHtml && !isDomElement && isObject(value)) { value = toJson(value, true); } if (value != lastValue || error != lastError) { lastValue = value; lastError = error; elementError(element, NG_EXCEPTION, error); if (error) value = error; if (isHtml) { element.html(html.get()); } else if (isDomElement) { element.html(''); element.append(value); } else { element.text(value == undefined ? '' : value); } } }, element); }; }); var bindTemplateCache = {}; function compileBindTemplate(template){ var fn = bindTemplateCache[template]; if (!fn) { var bindings = []; forEach(parseBindings(template), function(text){ var exp = binding(text); bindings.push(exp ? function(element){ var error, value = this.$tryEval(exp, function(e){ error = toJson(e); }); elementError(element, NG_EXCEPTION, error); return error ? error : value; } : function() { return text; }); }); bindTemplateCache[template] = fn = function(element, prettyPrintJson){ var parts = [], self = this, oldElement = this.hasOwnProperty($$element) ? self.$element : undefined; self.$element = element; for ( var i = 0; i < bindings.length; i++) { var value = bindings[i].call(self, element); if (isElement(value)) value = ''; else if (isObject(value)) value = toJson(value, prettyPrintJson); parts.push(value); } self.$element = oldElement; return parts.join(''); }; } return fn; } /** * @workInProgress * @ngdoc directive * @name angular.directive.ng:bind-template * * @description * The `ng:bind-template` attribute specifies that the element * text should be replaced with the template in ng:bind-template. * Unlike ng:bind the ng:bind-template can contain multiple `{{` `}}` * expressions. (This is required since some HTML elements * can not have SPAN elements such as TITLE, or OPTION to name a few.) * * @element ANY * @param {string} template of form * {{ expression }} to eval. * * @example * Try it here: enter text in text box and watch the greeting change. Salutation:
Name:

     
it('should check ng:bind', function(){ expect(using('.doc-example-live').binding('{{salutation}} {{name}}')). toBe('Hello World!'); using('.doc-example-live').input('salutation').enter('Greetings'); using('.doc-example-live').input('name').enter('user'); expect(using('.doc-example-live').binding('{{salutation}} {{name}}')). toBe('Greetings user!'); });
*/ angularDirective("ng:bind-template", function(expression, element){ element.addClass('ng-binding'); var templateFn = compileBindTemplate(expression); return function(element) { var lastValue; this.$onEval(function() { var value = templateFn.call(this, element, true); if (value != lastValue) { element.text(value); lastValue = value; } }, element); }; }); var REMOVE_ATTRIBUTES = { 'disabled':'disabled', 'readonly':'readOnly', 'checked':'checked', 'selected':'selected', 'multiple':'multiple' }; /** * @workInProgress * @ngdoc directive * @name angular.directive.ng:bind-attr * * @description * The `ng:bind-attr` attribute specifies that * {@link guide/dev_guide.templates.databinding databindings} should be created between element * attributes and given expressions. Unlike `ng:bind` the `ng:bind-attr` contains a JSON key value * pairs representing which attributes need to be mapped to which * {@link guide/dev_guide.expressions expressions}. * * You don't usually write the `ng:bind-attr` in the HTML since embedding * {{expression}} into the attribute directly as the attribute value is * preferred. The attributes get translated into `` at * compile time. * * This HTML snippet is preferred way of working with `ng:bind-attr` *
 *   Google
 * 
* * The above gets translated to bellow during bootstrap time. *
 *   Google
 * 
* * @element ANY * @param {string} attribute_json a JSON key-value pairs representing * the attributes to replace. Each key matches the attribute * which needs to be replaced. Each value is a text template of * the attribute with embedded * {{expression}}s. Any number of * key-value pairs can be specified. * * @example * Try it here: enter text in text box and click Google. Google for: Google it('should check ng:bind-attr', function(){ expect(using('.doc-example-live').element('a').attr('href')). toBe('http://www.google.com/search?q=AngularJS'); using('.doc-example-live').input('query').enter('google'); expect(using('.doc-example-live').element('a').attr('href')). toBe('http://www.google.com/search?q=google'); }); */ angularDirective("ng:bind-attr", function(expression){ return function(element){ var lastValue = {}; this.$onEval(function(){ var values = this.$eval(expression); for(var key in values) { var value = compileBindTemplate(values[key]).call(this, element), specialName = REMOVE_ATTRIBUTES[lowercase(key)]; if (lastValue[key] !== value) { lastValue[key] = value; if (specialName) { if (toBoolean(value)) { element.attr(specialName, specialName); element.attr('ng-' + specialName, value); } else { element.removeAttr(specialName); element.removeAttr('ng-' + specialName); } (element.data($$validate)||noop)(); } else { element.attr(key, value); } } } }, element); }; }); /** * @workInProgress * @ngdoc directive * @name angular.directive.ng:click * * @description * The ng:click allows you to specify custom behavior when * element is clicked. * * @element ANY * @param {expression} expression {@link guide/dev_guide.expressions Expression} to eval upon click. * * @example count: {{count}} it('should check ng:click', function(){ expect(binding('count')).toBe('0'); element('.doc-example-live :button').click(); expect(binding('count')).toBe('1'); }); */ /* * A directive that allows creation of custom onclick handlers that are defined as angular * expressions and are compiled and executed within the current scope. * * Events that are handled via these handler are always configured not to propagate further. * * TODO: maybe we should consider allowing users to control event propagation in the future. */ angularDirective("ng:click", function(expression, element){ return annotate('$updateView', function($updateView, element){ var self = this; element.bind('click', function(event){ self.$tryEval(expression, element); $updateView(); event.stopPropagation(); }); }); }); /** * @workInProgress * @ngdoc directive * @name angular.directive.ng:submit * * @description * Enables binding angular expressions to onsubmit events. * * Additionally it prevents the default action (which for form means sending the request to the * server and reloading the current page). * * @element form * @param {expression} expression {@link guide/dev_guide.expressions Expression} to eval. * * @example
Enter text and hit enter:
list={{list}}
it('should check ng:submit', function(){ expect(binding('list')).toBe('list=[]'); element('.doc-example-live #submit').click(); expect(binding('list')).toBe('list=["hello"]'); });
*/ angularDirective("ng:submit", function(expression, element) { return annotate('$updateView', function($updateView, element) { var self = this; element.bind('submit', function(event) { self.$tryEval(expression, element); $updateView(); event.preventDefault(); }); }); }); function ngClass(selector) { return function(expression, element){ var existing = element[0].className + ' '; return function(element){ this.$onEval(function(){ var scope = this; if (selector(scope.$index)) { var ngClassVal = scope.$eval(element.attr('ng:class') || ''); if (isArray(ngClassVal)) ngClassVal = ngClassVal.join(' '); var value = scope.$eval(expression); if (isArray(value)) value = value.join(' '); if (ngClassVal && ngClassVal !== value) value = value + ' ' + ngClassVal; element[0].className = trim(existing + value); } }, element); }; }; } /** * @workInProgress * @ngdoc directive * @name angular.directive.ng:class * * @description * The `ng:class` allows you to set CSS class on HTML element * conditionally. * * @element ANY * @param {expression} expression {@link guide/dev_guide.expressions Expression} to eval. * * @example
Sample Text     
it('should check ng:class', function(){ expect(element('.doc-example-live span').attr('className')).not(). toMatch(/ng-input-indicator-wait/); using('.doc-example-live').element(':button:first').click(); expect(element('.doc-example-live span').attr('className')). toMatch(/ng-input-indicator-wait/); using('.doc-example-live').element(':button:last').click(); expect(element('.doc-example-live span').attr('className')).not(). toMatch(/ng-input-indicator-wait/); });
*/ angularDirective("ng:class", ngClass(function(){return true;})); /** * @workInProgress * @ngdoc directive * @name angular.directive.ng:class-odd * * @description * The `ng:class-odd` and `ng:class-even` works exactly as * `ng:class`, except it works in conjunction with `ng:repeat` * and takes affect only on odd (even) rows. * * @element ANY * @param {expression} expression {@link guide/dev_guide.expressions Expression} to eval. Must be * inside `ng:repeat`. * * @example
  1. {{name}}      
it('should check ng:class-odd and ng:class-even', function(){ expect(element('.doc-example-live li:first span').attr('className')). toMatch(/ng-format-negative/); expect(element('.doc-example-live li:last span').attr('className')). toMatch(/ng-input-indicator-wait/); });
*/ angularDirective("ng:class-odd", ngClass(function(i){return i % 2 === 0;})); /** * @workInProgress * @ngdoc directive * @name angular.directive.ng:class-even * * @description * The `ng:class-odd` and `ng:class-even` works exactly as * `ng:class`, except it works in conjunction with `ng:repeat` * and takes affect only on odd (even) rows. * * @element ANY * @param {expression} expression {@link guide/dev_guide.expressions Expression} to eval. Must be * inside `ng:repeat`. * * @example
  1. {{name}}      
it('should check ng:class-odd and ng:class-even', function(){ expect(element('.doc-example-live li:first span').attr('className')). toMatch(/ng-format-negative/); expect(element('.doc-example-live li:last span').attr('className')). toMatch(/ng-input-indicator-wait/); });
*/ angularDirective("ng:class-even", ngClass(function(i){return i % 2 === 1;})); /** * @workInProgress * @ngdoc directive * @name angular.directive.ng:show * * @description * The `ng:show` and `ng:hide` directives show or hide a portion of the DOM tree (HTML) * conditionally. * * @element ANY * @param {expression} expression If the {@link guide/dev_guide.expressions expression} is truthy * then the element is shown or hidden respectively. * * @example Click me:
Show: I show up when your checkbox is checked.
Hide: I hide when your checkbox is checked.
it('should check ng:show / ng:hide', function(){ expect(element('.doc-example-live span:first:hidden').count()).toEqual(1); expect(element('.doc-example-live span:last:visible').count()).toEqual(1); input('checked').check(); expect(element('.doc-example-live span:first:visible').count()).toEqual(1); expect(element('.doc-example-live span:last:hidden').count()).toEqual(1); });
*/ angularDirective("ng:show", function(expression, element){ return function(element){ this.$onEval(function(){ element.css('display', toBoolean(this.$eval(expression)) ? '' : 'none'); }, element); }; }); /** * @workInProgress * @ngdoc directive * @name angular.directive.ng:hide * * @description * The `ng:hide` and `ng:show` directives hide or show a portion * of the HTML conditionally. * * @element ANY * @param {expression} expression If the {@link guide/dev_guide.expressions expression} truthy then * the element is shown or hidden respectively. * * @example Click me:
Show: I show up when you checkbox is checked?
Hide: I hide when you checkbox is checked?
it('should check ng:show / ng:hide', function(){ expect(element('.doc-example-live span:first:hidden').count()).toEqual(1); expect(element('.doc-example-live span:last:visible').count()).toEqual(1); input('checked').check(); expect(element('.doc-example-live span:first:visible').count()).toEqual(1); expect(element('.doc-example-live span:last:hidden').count()).toEqual(1); });
*/ angularDirective("ng:hide", function(expression, element){ return function(element){ this.$onEval(function(){ element.css('display', toBoolean(this.$eval(expression)) ? 'none' : ''); }, element); }; }); /** * @workInProgress * @ngdoc directive * @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 {@link guide/dev_guide.expressions Expression} which evals to an * object whose keys are CSS style names and values are corresponding values for those CSS * keys. * * @example
Sample Text
myStyle={{myStyle}}
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){ return function(element){ var resetStyle = getStyle(element); this.$onEval(function(){ var style = this.$eval(expression) || {}, key, mergedStyle = {}; for(key in style) { if (resetStyle[key] === undefined) resetStyle[key] = ''; mergedStyle[key] = style[key]; } for(key in resetStyle) { mergedStyle[key] = mergedStyle[key] || resetStyle[key]; } element.css(mergedStyle); }, element); }; });