mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-16 23:30:23 +00:00
refactor(directives): connect new compiler
- turn everything into a directive
This commit is contained in:
parent
8af4fde182
commit
9ee2cdff44
36 changed files with 1568 additions and 1744 deletions
|
|
@ -1,6 +1,7 @@
|
|||
@charset "UTF-8";
|
||||
|
||||
[ng\:cloak], .ng-cloak {
|
||||
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak],
|
||||
.ng-cloak, .x-ng-cloak {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ All `inputType` widgets support:
|
|||
it('should invalidate on wrong input', function() {
|
||||
expect(element('form[name=myForm]').prop('className')).toMatch('ng-valid');
|
||||
input('data').enter('{}');
|
||||
expect(binding('data')).toEqual('data={\n }');
|
||||
expect(binding('data')).toEqual('{}');
|
||||
input('data').enter('{');
|
||||
expect(element('form[name=myForm]').prop('className')).toMatch('ng-invalid');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -185,16 +185,20 @@ Extensions: You can further extend the expression vocabulary by adding new metho
|
|||
{name:'Julie', phone:'555-8765'}]"></div>
|
||||
Search: <input ng:model="searchText"/>
|
||||
<table class="example3">
|
||||
<tr><th>Name</th><th>Phone</th><tr>
|
||||
<tr ng:repeat="friend in friends | filter:searchText">
|
||||
<td>{{friend.name}}</td>
|
||||
<td>{{friend.phone}}</td>
|
||||
</tr>
|
||||
<thead>
|
||||
<tr><th>Name</th><th>Phone</th><tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng:repeat="friend in friends | filter:searchText">
|
||||
<td>{{friend.name}}</td>
|
||||
<td>{{friend.phone}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should filter the list', function() {
|
||||
var tr = using('table.example3').repeater('tr.ng-attr-widget');
|
||||
var tr = using('table.example3 tbody').repeater('tr');
|
||||
expect(tr.count()).toBe(5);
|
||||
input('searchText').enter('a');
|
||||
expect(tr.count()).toBe(2);
|
||||
|
|
|
|||
|
|
@ -277,20 +277,20 @@ The following example demonstrates:
|
|||
|
||||
This example shows how to implement a custom HTML editor widget in Angular.
|
||||
|
||||
<doc:example>
|
||||
<doc:example module="formModule">
|
||||
<doc:source>
|
||||
<script>
|
||||
function EditorCntl($scope) {
|
||||
$scope.htmlContent = '<b>Hello</b> <i>World</i>!';
|
||||
}
|
||||
|
||||
HTMLEditorWidget.$inject = ['$element', '$scope', 'htmlFilter'];
|
||||
function HTMLEditorWidget(element, scope, htmlFilter) {
|
||||
HTMLEditorWidget.$inject = ['$scope', '$element', '$sanitize'];
|
||||
function HTMLEditorWidget(scope, element, $sanitize) {
|
||||
scope.$parseModel = function() {
|
||||
// need to protect for script injection
|
||||
try {
|
||||
this.$viewValue = htmlFilter(
|
||||
this.$modelValue || '').get();
|
||||
this.$viewValue = $sanitize(
|
||||
this.$modelValue || '');
|
||||
if (this.$error.HTML) {
|
||||
// we were invalid, but now we are OK.
|
||||
this.$emit('$valid', 'HTML');
|
||||
|
|
@ -312,24 +312,25 @@ This example shows how to implement a custom HTML editor widget in Angular.
|
|||
});
|
||||
}
|
||||
|
||||
angular.directive('ng:html-editor-model', function() {
|
||||
return ['$formFactory', '$element', function ($formFactory, element) {
|
||||
var exp = element.attr('ng:html-editor-model'),
|
||||
form = $formFactory.forElement(element),
|
||||
widget;
|
||||
element.attr('contentEditable', true);
|
||||
widget = form.$createWidget({
|
||||
scope: this,
|
||||
model: exp,
|
||||
controller: HTMLEditorWidget,
|
||||
controllerArgs: {$element: element}});
|
||||
// if the element is destroyed, then we need to
|
||||
// notify the form.
|
||||
element.bind('$destroy', function() {
|
||||
widget.$destroy();
|
||||
});
|
||||
}];
|
||||
});
|
||||
angular.module.formModule = function($compileProvider){
|
||||
$compileProvider.directive('ngHtmlEditorModel', function ($formFactory) {
|
||||
return function(scope, element, attr) {
|
||||
var form = $formFactory.forElement(element),
|
||||
widget;
|
||||
element.attr('contentEditable', true);
|
||||
widget = form.$createWidget({
|
||||
scope: scope,
|
||||
model: attr.ngHtmlEditorModel,
|
||||
controller: HTMLEditorWidget,
|
||||
controllerArgs: {$element: element}});
|
||||
// if the element is destroyed, then we need to
|
||||
// notify the form.
|
||||
element.bind('$destroy', function() {
|
||||
widget.$destroy();
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
||||
</script>
|
||||
<form name='editorForm' ng:controller="EditorCntl">
|
||||
<div ng:html-editor-model="htmlContent"></div>
|
||||
|
|
@ -337,7 +338,7 @@ This example shows how to implement a custom HTML editor widget in Angular.
|
|||
HTML: <br/>
|
||||
<textarea ng:model="htmlContent" cols="80"></textarea>
|
||||
<hr/>
|
||||
<pre>editorForm = {{editorForm}}</pre>
|
||||
<pre>editorForm = {{editorForm|json}}</pre>
|
||||
</form>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
(function() {
|
||||
angular.module('ngdocs.directives', [], function($compileProvider) {
|
||||
|
||||
var angularJsUrl;
|
||||
var scripts = document.getElementsByTagName("script");
|
||||
|
|
@ -22,119 +22,122 @@
|
|||
' </body>\n' +
|
||||
'</html>';
|
||||
|
||||
angular.widget('doc:example', ['$injector', '$browser', '$location', '$element',
|
||||
function($injector, $browser, $location, element){
|
||||
this.descend(false); // do not compile the example code
|
||||
var module = element.attr('module') || '';
|
||||
$compileProvider.directive('docExample', ['$injector', '$log', '$browser', '$location',
|
||||
function($injector, $log, $browser, $location) {
|
||||
return {
|
||||
terminal: true,
|
||||
compile: function(element, attrs) {
|
||||
var module = element.attr('module') || '';
|
||||
|
||||
//jQuery find() methods in this widget contain primitive selectors on purpose so that we can use
|
||||
//jqlite instead. jqlite's find() method currently supports onlt getElementsByTagName!
|
||||
var example = element.find('pre').eq(0), //doc-source
|
||||
scriptSrc = '',
|
||||
htmlSrc = example.text().replace(/<script[^\>]*>([\s\S]+)<\/script>/im, function(_, script) {
|
||||
scriptSrc = script;
|
||||
return '';
|
||||
}),
|
||||
showSource = example.attr('source') !== 'false',
|
||||
jsfiddle = example.attr('jsfiddle') || true,
|
||||
scenario = element.find('pre').eq(1); //doc-scenario
|
||||
//jQuery find() methods in this widget contain primitive selectors on purpose so that we can use
|
||||
//jqlite instead. jqlite's find() method currently supports onlt getElementsByTagName!
|
||||
var example = element.find('pre').eq(0), //doc-source
|
||||
scriptSrc = '',
|
||||
htmlSrc = example.text().replace(/<script[^\>]*>([\s\S]+)<\/script>/im, function(_, script) {
|
||||
scriptSrc = script;
|
||||
return '';
|
||||
}),
|
||||
showSource = example.attr('source') !== 'false',
|
||||
jsfiddle = example.attr('jsfiddle') || true,
|
||||
scenario = element.find('pre').eq(1); //doc-scenario
|
||||
|
||||
var tabs = angular.element('<ul class="doc-example">');
|
||||
var tabs = angular.element('<ul class="doc-example">');
|
||||
|
||||
// show source tab, if not disabled
|
||||
if (showSource) {
|
||||
tabs.append(
|
||||
'<li class="doc-example-heading"><h3>Source</h3></li>' +
|
||||
'<li class="doc-example-source" ng:non-bindable>' +
|
||||
jsFiddleButton(jsfiddle) + // may or may not have value
|
||||
'<pre class="brush: js; html-script: true; toolbar: false;"></pre></li>');
|
||||
}
|
||||
// show live preview tab
|
||||
var livePreviewTab;
|
||||
tabs.append('<li class="doc-example-heading"><h3>Live Preview</h3></li>');
|
||||
tabs.append(livePreviewTab = angular.element('<li class="doc-example-live">' + htmlSrc +'</li>'));
|
||||
// show scenario tab, if present
|
||||
if (scenario.text()) {
|
||||
tabs.append(
|
||||
'<li class="doc-example-heading"><h3>Scenario Test</h3></li>' +
|
||||
'<li class="doc-example-scenario"><pre class="brush: js">' + scenario.text() + '</pre></li>');
|
||||
}
|
||||
|
||||
tabs.find('li').eq(1).find('pre').text(
|
||||
HTML_TEMPLATE.
|
||||
replace('_SCRIPT_SOURCE_', scriptSrc ? ' <script>\n' + indent(scriptSrc, ' ') + '\n </script>\n' : '').
|
||||
replace('_HTML_SOURCE_', indent(htmlSrc, ' ')).
|
||||
replace('_MODULE_', module ? '="' + module + '"' : ''));
|
||||
|
||||
element.html('');
|
||||
element.append(tabs);
|
||||
|
||||
try {
|
||||
if (window.execScript) { // IE
|
||||
window.execScript(scriptSrc || '"stupid IE!"'); // IE complains when evaling empty string
|
||||
} else {
|
||||
window.eval(scriptSrc);
|
||||
}
|
||||
} catch (e) {
|
||||
alert(e);
|
||||
}
|
||||
|
||||
return function() {
|
||||
var scope = this;
|
||||
var modules = [
|
||||
'ng',
|
||||
function($provide) {
|
||||
$provide.value('$browser', $browser);
|
||||
$provide.value('$location', $location);
|
||||
// show source tab, if not disabled
|
||||
if (showSource) {
|
||||
tabs.append(
|
||||
'<li class="doc-example-heading"><h3>Source</h3></li>' +
|
||||
'<li class="doc-example-source" ng:non-bindable>' +
|
||||
jsFiddleButton(jsfiddle) + // may or may not have value
|
||||
'<pre class="brush: js; html-script: true; toolbar: false;"></pre></li>');
|
||||
}
|
||||
// show live preview tab
|
||||
var livePreviewTab;
|
||||
tabs.append('<li class="doc-example-heading"><h3>Live Preview</h3></li>');
|
||||
tabs.append(livePreviewTab = angular.element('<li class="doc-example-live">' + htmlSrc +'</li>'));
|
||||
// show scenario tab, if present
|
||||
if (scenario.text()) {
|
||||
tabs.append(
|
||||
'<li class="doc-example-heading"><h3>Scenario Test</h3></li>' +
|
||||
'<li class="doc-example-scenario"><pre class="brush: js">' + scenario.text() + '</pre></li>');
|
||||
}
|
||||
];
|
||||
module && modules.push(module);
|
||||
|
||||
angular.bootstrap(livePreviewTab, modules).invoke(function($rootScope) {
|
||||
element.bind('$destroy', scope.$root.$watch(function() {
|
||||
$rootScope.$digest();
|
||||
}));
|
||||
});
|
||||
};
|
||||
tabs.find('li').eq(1).find('pre').text(
|
||||
HTML_TEMPLATE.
|
||||
replace('_SCRIPT_SOURCE_', scriptSrc ? ' <script>\n' + indent(scriptSrc, ' ') + '\n </script>\n' : '').
|
||||
replace('_HTML_SOURCE_', indent(htmlSrc, ' ')).
|
||||
replace('_MODULE_', module ? '="' + module + '"' : ''));
|
||||
|
||||
function jsFiddleButton(jsfiddle) {
|
||||
var fixJsFiddleIssue132 = true;
|
||||
if (jsfiddle !== 'false') {
|
||||
if(jsfiddle === true) {
|
||||
//dynamically generate a fiddle
|
||||
var fiddleUrl = 'http://jsfiddle.net/api/post/library/pure/';
|
||||
element.html('');
|
||||
element.append(tabs);
|
||||
|
||||
function jsFiddleEscape(text, prefix) {
|
||||
return indent(text.replace(/<\/textarea>/gi,'</textarea>'), prefix);
|
||||
try {
|
||||
if (window.execScript) { // IE
|
||||
window.execScript(scriptSrc || '"stupid IE!"'); // IE complains when evaling empty string
|
||||
} else {
|
||||
window.eval(scriptSrc);
|
||||
}
|
||||
|
||||
return '<form class="jsfiddle" method="post" action="' + fiddleUrl + '" target="_blank">' +
|
||||
(fixJsFiddleIssue132 ? '' : '<textarea name="resources">' + angularJsUrl + '</textarea>') +
|
||||
'<textarea name="css">\n' +
|
||||
(fixJsFiddleIssue132 ? '</style>\n<script src="' + angularJsUrl + '"></script>\n<style>\n' : '') +
|
||||
'.ng-invalid { border: 1px solid red; } \n' +
|
||||
'body { font-family: Arial,Helvetica,sans-serif; }\n' +
|
||||
'body, td, th { font-size: 14px; margin: 0; }\n' +
|
||||
'table { border-collapse: separate; border-spacing: 2px; display: table; margin-bottom: 0; margin-top: 0; -moz-box-sizing: border-box; text-indent: 0; }\n' +
|
||||
'a:link, a:visited, a:hover { color: #5D6DB6; text-decoration: none; }\n' +
|
||||
'.error { color: red; }\n' +
|
||||
'</textarea>' +
|
||||
'<input type="text" name="title" value="AngularJS Live Example">' +
|
||||
'<textarea name="html">' +
|
||||
'<div ng:app' + (module ? '="' + module + '"' : '') + '>\n' + jsFiddleEscape(htmlSrc, ' ') + '\n</div>' +
|
||||
'</textarea>' +
|
||||
'<textarea name="js">' + jsFiddleEscape(scriptSrc) + '</textarea>' +
|
||||
'<button>edit at jsFiddle</button>' +
|
||||
'</form>';
|
||||
} else {
|
||||
//use existing fiddle
|
||||
fiddleUrl = "http://jsfiddle.net" + jsfiddle;
|
||||
return '<form class="jsfiddle" method="get" action="' + fiddleUrl + '" target="_blank">' +
|
||||
'<button>edit at jsFiddle</button>' +
|
||||
'</form>';
|
||||
} catch (e) {
|
||||
alert(e);
|
||||
}
|
||||
|
||||
return function(docsAppScope) {
|
||||
var modules = [
|
||||
function($provide) {
|
||||
$provide.value('$browser', $browser);
|
||||
$provide.value('$location', $location);
|
||||
}
|
||||
];
|
||||
module && modules.push(module);
|
||||
|
||||
angular.bootstrap(livePreviewTab, modules).
|
||||
invoke(['$rootScope', function(example$rootScope) {
|
||||
element.bind('$destroy', docsAppScope.$root.$watch(function() {
|
||||
// this propagates the $watch from the docs app to the example app
|
||||
example$rootScope.$digest();
|
||||
}));
|
||||
}]);
|
||||
};
|
||||
|
||||
function jsFiddleButton(jsfiddle) {
|
||||
var fixJsFiddleIssue132 = true;
|
||||
if (jsfiddle !== 'false') {
|
||||
if(jsfiddle === true) {
|
||||
//dynamically generate a fiddle
|
||||
var fiddleUrl = 'http://jsfiddle.net/api/post/library/pure/';
|
||||
|
||||
function jsFiddleEscape(text, prefix) {
|
||||
return indent(text.replace(/<\/textarea>/gi,'</textarea>'), prefix);
|
||||
}
|
||||
|
||||
return '<form class="jsfiddle" method="post" action="' + fiddleUrl + '" target="_blank">' +
|
||||
(fixJsFiddleIssue132 ? '' : '<textarea name="resources">' + angularJsUrl + '</textarea>') +
|
||||
'<textarea name="css">\n' +
|
||||
(fixJsFiddleIssue132 ? '</style>\n<script src="' + angularJsUrl + '"></script>\n<style>\n' : '') +
|
||||
'.ng-invalid { border: 1px solid red; } \n' +
|
||||
'body { font-family: Arial,Helvetica,sans-serif; }\n' +
|
||||
'body, td, th { font-size: 14px; margin: 0; }\n' +
|
||||
'table { border-collapse: separate; border-spacing: 2px; display: table; margin-bottom: 0; margin-top: 0; -moz-box-sizing: border-box; text-indent: 0; }\n' +
|
||||
'a:link, a:visited, a:hover { color: #5D6DB6; text-decoration: none; }\n' +
|
||||
'.error { color: red; }\n' +
|
||||
'</textarea>' +
|
||||
'<input type="text" name="title" value="AngularJS Live Example">' +
|
||||
'<textarea name="html">' +
|
||||
'<div ng:app' + (module ? '="' + module + '"' : '') + '>\n' + jsFiddleEscape(htmlSrc, ' ') + '\n</div>' +
|
||||
'</textarea>' +
|
||||
'<textarea name="js">' + jsFiddleEscape(scriptSrc) + '</textarea>' +
|
||||
'<button>edit at jsFiddle</button>' +
|
||||
'</form>';
|
||||
} else {
|
||||
//use existing fiddle
|
||||
fiddleUrl = "http://jsfiddle.net" + jsfiddle;
|
||||
return '<form class="jsfiddle" method="get" action="' + fiddleUrl + '" target="_blank">' +
|
||||
'<button>edit at jsFiddle</button>' +
|
||||
'</form>';
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}]);
|
||||
|
||||
|
|
@ -163,93 +166,95 @@
|
|||
return lines.join('\n');
|
||||
}
|
||||
|
||||
var HTML_TPL =
|
||||
'<p><a ng:init="showInstructions = {show}" ng:show="!showInstructions" ng:click="showInstructions = true" href>Workspace Reset Instructions ➤</a></p>' +
|
||||
'<div ng:controller="TutorialInstructionsCtrl" ng:show="showInstructions">' +
|
||||
'<div class="tabs-nav">' +
|
||||
'<ul>' +
|
||||
'</ul>' +
|
||||
'</div>' +
|
||||
'<div class="tabs-content"><div class="tabs-content-inner">' +
|
||||
$compileProvider.directive('docTutorialInstructions', function() {
|
||||
var HTML_NAV = '<li ng:class="currentCls(\'{id}\')"><a ng:click="select(\'{id}\')" href>{title}</a></li>';
|
||||
var HTML_CONTENT = '<div ng:show="selected==\'{id}\'">{content}</div>';
|
||||
|
||||
'</div></div>' +
|
||||
var HTML_TPL =
|
||||
'<p><a ng:init="showInstructions = {show}" ng:show="!showInstructions" ng:click="showInstructions = true" href>Workspace Reset Instructions ➤</a></p>' +
|
||||
'<div ng:controller="TutorialInstructionsCtrl" ng:show="showInstructions">' +
|
||||
'<div class="tabs-nav">' +
|
||||
'<ul>' +
|
||||
'</ul>' +
|
||||
'</div>' +
|
||||
'<div class="tabs-content"><div class="tabs-content-inner">' +
|
||||
|
||||
'</div></div>' +
|
||||
'</div>';
|
||||
|
||||
var DEFAULT_NAV =
|
||||
'<li ng:class="currentCls(\'git-mac\')"><a ng:click="select(\'git-mac\')" href>Git on Mac/Linux</a></li>' +
|
||||
'<li ng:class="currentCls(\'git-win\')"><a ng:click="select(\'git-win\')" href>Git on Windows</a></li>' +
|
||||
'<li ng:class="currentCls(\'ss-mac\')"><a ng:click="select(\'ss-mac\')" href>Snapshots on Mac/Linux</a></li>' +
|
||||
'<li ng:class="currentCls(\'ss-win\')"><a ng:click="select(\'ss-win\')" href>Snapshots on Windows</a></li>';
|
||||
|
||||
var DEFAULT_CONTENT =
|
||||
'<div ng:show="selected==\'git-mac\'">' +
|
||||
'<ol>' +
|
||||
'<li><p>Reset the workspace to step {step}.</p>' +
|
||||
'<pre><code> git checkout -f step-{step}</code></pre></li>' +
|
||||
'<li><p>Refresh your browser or check the app out on <a href="http://angular.github.com/angular-phonecat/step-{step}/app">angular\'s server</a>.</p></li>' +
|
||||
'</ol>' +
|
||||
'</div>' +
|
||||
|
||||
'<div ng:show="selected==\'git-win\'">' +
|
||||
'<ol>' +
|
||||
'<li><p>Reset the workspace to step {step}.</p>' +
|
||||
'<pre><code> git checkout -f step-{step}</code></pre></li>' +
|
||||
'<li><p>Refresh your browser or check the app out on <a href="http://angular.github.com/angular-phonecat/step-{step}/app">angular\'s server</a>.</p></li>' +
|
||||
'</ol>' +
|
||||
'</div>' +
|
||||
|
||||
'<div ng:show="selected==\'ss-mac\'">' +
|
||||
'<ol>' +
|
||||
'<li><p>Reset the workspace to step {step}.</p>' +
|
||||
'<pre><code> ./goto_step.sh {step}</code></pre></li>' +
|
||||
'<li><p>Refresh your browser or check the app out on <a href="http://angular.github.com/angular-phonecat/step-{step}/app">angular\'s server</a>.</p></li>' +
|
||||
'</ol>' +
|
||||
'</div>' +
|
||||
|
||||
'<div ng:show="selected==\'ss-win\'">' +
|
||||
'<ol>' +
|
||||
'<li><p>Reset the workspace to step {step}.</p>' +
|
||||
'<pre><code> ./goto_step.bat {step}</code></pre></li>' +
|
||||
'<li><p>Refresh your browser or check the app out on <a href="http://angular.github.com/angular-phonecat/step-{step}/app">angular\'s server</a>.</p></li>' +
|
||||
'</ol>' +
|
||||
'</div>';
|
||||
|
||||
var HTML_NAV = '<li ng:class="currentCls(\'{id}\')"><a ng:click="select(\'{id}\')" href>{title}</a></li>';
|
||||
var HTML_CONTENT = '<div ng:show="selected==\'{id}\'">{content}</div>';
|
||||
return {
|
||||
compile: function(element, attrs) {
|
||||
var tabs = angular.element(HTML_TPL.replace('{show}', attrs.show || 'false')),
|
||||
nav = tabs.find('ul'),
|
||||
// use simple selectors because jqLite find() supports getElementsByTagName only
|
||||
content = tabs.find('div').find('div'),
|
||||
children = element.children();
|
||||
|
||||
var DEFAULT_NAV =
|
||||
'<li ng:class="currentCls(\'git-mac\')"><a ng:click="select(\'git-mac\')" href>Git on Mac/Linux</a></li>' +
|
||||
'<li ng:class="currentCls(\'git-win\')"><a ng:click="select(\'git-win\')" href>Git on Windows</a></li>' +
|
||||
'<li ng:class="currentCls(\'ss-mac\')"><a ng:click="select(\'ss-mac\')" href>Snapshots on Mac/Linux</a></li>' +
|
||||
'<li ng:class="currentCls(\'ss-win\')"><a ng:click="select(\'ss-win\')" href>Snapshots on Windows</a></li>';
|
||||
if (children.length) {
|
||||
// load custom content
|
||||
angular.forEach(element.children(), function(elm) {
|
||||
elm = angular.element(elm);
|
||||
var id = elm.attr('id');
|
||||
|
||||
var DEFAULT_CONTENT =
|
||||
'<div ng:show="selected==\'git-mac\'">' +
|
||||
'<ol>' +
|
||||
'<li><p>Reset the workspace to step {step}.</p>' +
|
||||
'<pre><code> git checkout -f step-{step}</code></pre></li>' +
|
||||
'<li><p>Refresh your browser or check the app out on <a href="http://angular.github.com/angular-phonecat/step-{step}/app">angular\'s server</a>.</p></li>' +
|
||||
'</ol>' +
|
||||
'</div>' +
|
||||
nav.append(HTML_NAV.replace('{title}', elm.attr('title')).replace(/\{id\}/g, id));
|
||||
content.append(HTML_CONTENT.replace('{id}', id).replace('{content}', elm.html()));
|
||||
});
|
||||
} else {
|
||||
// default
|
||||
nav.append(DEFAULT_NAV);
|
||||
content.append(DEFAULT_CONTENT.replace(/\{step\}/g, element.attr('step')));
|
||||
}
|
||||
|
||||
'<div ng:show="selected==\'git-win\'">' +
|
||||
'<ol>' +
|
||||
'<li><p>Reset the workspace to step {step}.</p>' +
|
||||
'<pre><code> git checkout -f step-{step}</code></pre></li>' +
|
||||
'<li><p>Refresh your browser or check the app out on <a href="http://angular.github.com/angular-phonecat/step-{step}/app">angular\'s server</a>.</p></li>' +
|
||||
'</ol>' +
|
||||
'</div>' +
|
||||
|
||||
'<div ng:show="selected==\'ss-mac\'">' +
|
||||
'<ol>' +
|
||||
'<li><p>Reset the workspace to step {step}.</p>' +
|
||||
'<pre><code> ./goto_step.sh {step}</code></pre></li>' +
|
||||
'<li><p>Refresh your browser or check the app out on <a href="http://angular.github.com/angular-phonecat/step-{step}/app">angular\'s server</a>.</p></li>' +
|
||||
'</ol>' +
|
||||
'</div>' +
|
||||
|
||||
'<div ng:show="selected==\'ss-win\'">' +
|
||||
'<ol>' +
|
||||
'<li><p>Reset the workspace to step {step}.</p>' +
|
||||
'<pre><code> ./goto_step.bat {step}</code></pre></li>' +
|
||||
'<li><p>Refresh your browser or check the app out on <a href="http://angular.github.com/angular-phonecat/step-{step}/app">angular\'s server</a>.</p></li>' +
|
||||
'</ol>' +
|
||||
'</div>';
|
||||
|
||||
angular.widget('doc:tutorial-instructions', function(element) {
|
||||
this.descend(true);
|
||||
|
||||
var tabs = angular.element(HTML_TPL.replace('{show}', element.attr('show') || 'false')),
|
||||
nav = tabs.find('ul'),
|
||||
// use simple selectors because jqLite find() supports getElementsByTagName only
|
||||
content = tabs.find('div').find('div'),
|
||||
children = element.children();
|
||||
|
||||
if (children.length) {
|
||||
// load custom content
|
||||
angular.forEach(element.children(), function(elm) {
|
||||
elm = angular.element(elm);
|
||||
var id = elm.attr('id');
|
||||
|
||||
nav.append(HTML_NAV.replace('{title}', elm.attr('title')).replace(/\{id\}/g, id));
|
||||
content.append(HTML_CONTENT.replace('{id}', id).replace('{content}', elm.html()));
|
||||
});
|
||||
} else {
|
||||
// default
|
||||
nav.append(DEFAULT_NAV);
|
||||
content.append(DEFAULT_CONTENT.replace(/\{step\}/g, element.attr('step')));
|
||||
element.html('');
|
||||
element.append(tabs);
|
||||
}
|
||||
}
|
||||
|
||||
element.html('');
|
||||
element.append(tabs);
|
||||
});
|
||||
|
||||
|
||||
angular.directive('doc:tutorial-nav', function(step) {
|
||||
return function(element) {
|
||||
$compileProvider.directive('docTutorialNav', function() {
|
||||
return function(scope, element, attrs) {
|
||||
var prevStep, codeDiff, nextStep,
|
||||
content;
|
||||
content, step = attrs.docTutorialNav;
|
||||
|
||||
step = parseInt(step, 10);
|
||||
|
||||
|
|
@ -282,4 +287,4 @@
|
|||
return (step < 10) ? ('0' + step) : step;
|
||||
}
|
||||
});
|
||||
})();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -125,11 +125,6 @@ function DocsController(scope, $location, $window, $cookies, $filter) {
|
|||
}
|
||||
}
|
||||
|
||||
// prevent compilation of code
|
||||
angular.widget('code', function(element) {
|
||||
element.attr('ng:non-bindable', 'true');
|
||||
});
|
||||
|
||||
SyntaxHighlighter['defaults'].toolbar = false;
|
||||
SyntaxHighlighter['defaults'].gutter = true;
|
||||
|
||||
|
|
@ -151,7 +146,7 @@ function TutorialInstructionsCtrl($cookieStore) {
|
|||
};
|
||||
}
|
||||
|
||||
angular.module('ngdocs', [], function($locationProvider, $filterProvider) {
|
||||
angular.module('ngdocs', ['ngdocs.directives'], function($locationProvider, $filterProvider, $compileProvider) {
|
||||
$locationProvider.html5Mode(true).hashPrefix('!');
|
||||
|
||||
$filterProvider.register('title', function(){
|
||||
|
|
@ -161,4 +156,8 @@ angular.module('ngdocs', [], function($locationProvider, $filterProvider) {
|
|||
});
|
||||
};
|
||||
});
|
||||
|
||||
$compileProvider.directive('code', function() {
|
||||
return { terminal: true };
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -90,15 +90,7 @@ var $$scope = '$scope',
|
|||
|
||||
/** @name angular */
|
||||
angular = window.angular || (window.angular = {}),
|
||||
angularModule = null,
|
||||
/** @name angular.markup */
|
||||
angularTextMarkup = extensionMap(angular, 'markup'),
|
||||
/** @name angular.attrMarkup */
|
||||
angularAttrMarkup = extensionMap(angular, 'attrMarkup'),
|
||||
/** @name angular.directive */
|
||||
angularDirective = extensionMap(angular, 'directive', lowercase),
|
||||
/** @name angular.widget */
|
||||
angularWidget = extensionMap(angular, 'widget', shivForIE),
|
||||
angularModule,
|
||||
/** @name angular.module.ng */
|
||||
angularInputType = extensionMap(angular, 'inputType', lowercase),
|
||||
nodeName_,
|
||||
|
|
@ -988,8 +980,7 @@ function assertArg(arg, name, reason) {
|
|||
}
|
||||
|
||||
function assertArgFn(arg, name) {
|
||||
assertArg(arg, name);
|
||||
assertArg(isFunction(arg), name, 'not a function, got ' +
|
||||
(typeof arg == 'object' ? arg.constructor.name || 'Object' : typeof arg));
|
||||
(arg && typeof arg == 'object' ? arg.constructor.name || 'Object' : typeof arg));
|
||||
return arg;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,16 +60,43 @@ function publishExternalAPI(angular){
|
|||
|
||||
angularModule('ng', ['ngLocale'], ['$provide',
|
||||
function ngModule($provide) {
|
||||
// TODO(misko): temporary services to get the compiler working;
|
||||
$provide.value('$textMarkup', angularTextMarkup);
|
||||
$provide.value('$attrMarkup', angularAttrMarkup);
|
||||
$provide.value('$directive', angularDirective);
|
||||
$provide.value('$widget', angularWidget);
|
||||
|
||||
$provide.service('$anchorScroll', $AnchorScrollProvider);
|
||||
$provide.service('$browser', $BrowserProvider);
|
||||
$provide.service('$cacheFactory', $CacheFactoryProvider);
|
||||
$provide.service('$compile', $CompileProvider);
|
||||
$provide.service('$compile', $CompileProvider).
|
||||
directive({
|
||||
a: htmlAnchorDirective,
|
||||
input: inputDirective,
|
||||
textarea: inputDirective,
|
||||
form: ngFormDirective,
|
||||
select: selectDirective,
|
||||
option: optionDirective,
|
||||
ngBind: ngBindDirective,
|
||||
ngBindHtml: ngBindHtmlDirective,
|
||||
ngBindHtmlUnsafe: ngBindHtmlUnsafeDirective,
|
||||
ngBindTemplate: ngBindTemplateDirective,
|
||||
ngBindAttr: ngBindAttrDirective,
|
||||
ngClass: ngClassDirective,
|
||||
ngClassEven: ngClassEvenDirective,
|
||||
ngClassOdd: ngClassOddDirective,
|
||||
ngCloak: ngCloakDirective,
|
||||
ngController: ngControllerDirective,
|
||||
ngForm: ngFormDirective,
|
||||
ngHide: ngHideDirective,
|
||||
ngInclude: ngIncludeDirective,
|
||||
ngInit: ngInitDirective,
|
||||
ngNonBindable: ngNonBindableDirective,
|
||||
ngPluralize: ngPluralizeDirective,
|
||||
ngRepeat: ngRepeatDirective,
|
||||
ngShow: ngShowDirective,
|
||||
ngSubmit: ngSubmitDirective,
|
||||
ngStyle: ngStyleDirective,
|
||||
ngSwitch: ngSwitchDirective,
|
||||
ngOptions: ngOptionsDirective,
|
||||
ngView: ngViewDirective
|
||||
}).
|
||||
directive(ngEventDirectives).
|
||||
directive(ngAttributeAliasDirectives);
|
||||
$provide.service('$controller', $ControllerProvider);
|
||||
$provide.service('$cookies', $CookiesProvider);
|
||||
$provide.service('$cookieStore', $CookieStoreProvider);
|
||||
|
|
@ -89,9 +116,9 @@ function publishExternalAPI(angular){
|
|||
$provide.service('$routeParams', $RouteParamsProvider);
|
||||
$provide.service('$rootScope', $RootScopeProvider);
|
||||
$provide.service('$q', $QProvider);
|
||||
$provide.service('$sanitize', $SanitizeProvider);
|
||||
$provide.service('$sniffer', $SnifferProvider);
|
||||
$provide.service('$templateCache', $TemplateCacheProvider);
|
||||
$provide.service('$window', $WindowProvider);
|
||||
}]);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -219,6 +219,7 @@ function inferInjectionArgs(fn) {
|
|||
* - `Constructor`: a new instance of the provider will be created using
|
||||
* {@link angular.module.AUTO.$injector#instantiate $injector.instantiate()}, then treated as `object`.
|
||||
*
|
||||
* @returns {Object} registered provider instance
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -232,6 +233,7 @@ function inferInjectionArgs(fn) {
|
|||
* @param {string} name The name of the instance. NOTE: the provider will be available under `name + 'Provide'` key.
|
||||
* @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand for
|
||||
* `$provide.service(name, {$get:$getFn})`.
|
||||
* @returns {Object} registered provider instance
|
||||
*/
|
||||
|
||||
|
||||
|
|
@ -246,6 +248,7 @@ function inferInjectionArgs(fn) {
|
|||
* @param {string} name The name of the instance. NOTE: the provider will be available under `name + 'Provide'` key.
|
||||
* @param {function()} value The $getFn for the instance creation. Internally this is a short hand for
|
||||
* `$provide.service(name, {$get:function(){ return value; }})`.
|
||||
* @returns {Object} registered provider instance
|
||||
*/
|
||||
|
||||
|
||||
|
|
@ -285,7 +288,7 @@ function createInjector(modulesToLoad) {
|
|||
if (isObject(key)) {
|
||||
forEach(key, reverseParams(delegate));
|
||||
} else {
|
||||
delegate(key, value);
|
||||
return delegate(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -297,12 +300,12 @@ function createInjector(modulesToLoad) {
|
|||
if (!provider.$get) {
|
||||
throw Error('Provider ' + name + ' must define $get factory method.');
|
||||
}
|
||||
providerCache[name + providerSuffix] = provider;
|
||||
return providerCache[name + providerSuffix] = provider;
|
||||
}
|
||||
|
||||
function factory(name, factoryFn) { service(name, { $get:factoryFn }); }
|
||||
function factory(name, factoryFn) { return service(name, { $get:factoryFn }); }
|
||||
|
||||
function value(name, value) { factory(name, valueFn(value)); }
|
||||
function value(name, value) { return factory(name, valueFn(value)); }
|
||||
|
||||
function decorator(serviceName, decorFn) {
|
||||
var origProvider = providerInjector.get(serviceName + providerSuffix),
|
||||
|
|
|
|||
|
|
@ -58,10 +58,14 @@
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularDirective("ng:init", function(expression){
|
||||
return function(element){
|
||||
this.$eval(expression);
|
||||
};
|
||||
var ngInitDirective = valueFn({
|
||||
compile: function() {
|
||||
return {
|
||||
pre: function(scope, element, attrs) {
|
||||
scope.$eval(attrs.ngInit);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
@ -158,16 +162,24 @@ angularDirective("ng:init", function(expression){
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularDirective("ng:controller", function(expression) {
|
||||
this.scope(true);
|
||||
return ['$controller', '$window', function($controller, $window) {
|
||||
var scope = this,
|
||||
Controller = getter(scope, expression, true) || getter($window, expression, true);
|
||||
var ngControllerDirective = ['$controller', '$window', function($controller, $window) {
|
||||
return {
|
||||
scope: true,
|
||||
compile: function() {
|
||||
return {
|
||||
pre: function(scope, element, attr) {
|
||||
var expression = attr.ngController,
|
||||
Controller = getter(scope, expression, true) || getter($window, expression, true);
|
||||
|
||||
assertArgFn(Controller, expression);
|
||||
$controller(Controller, scope);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
|
||||
assertArgFn(Controller, expression);
|
||||
$controller(Controller, scope);
|
||||
}];
|
||||
});
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
|
|
@ -208,55 +220,30 @@ angularDirective("ng:controller", function(expression) {
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularDirective("ng:bind", function(expression, element){
|
||||
element.addClass('ng-binding');
|
||||
return ['$exceptionHandler', '$parse', '$element', function($exceptionHandler, $parse, element) {
|
||||
var exprFn = $parse(expression),
|
||||
lastValue = Number.NaN,
|
||||
scope = this;
|
||||
var ngBindDirective = valueFn(function(scope, element, attr) {
|
||||
element.addClass('ng-binding').data('$binding', attr.ngBind);
|
||||
scope.$watch(attr.ngBind, function(value) {
|
||||
element.text(value == undefined ? '' : value);
|
||||
});
|
||||
});
|
||||
|
||||
scope.$watch(function() {
|
||||
// TODO(misko): remove error handling https://github.com/angular/angular.js/issues/347
|
||||
var value, html, isHtml, isDomElement,
|
||||
hadOwnElement = scope.hasOwnProperty('$element'),
|
||||
oldElement = scope.$element;
|
||||
// TODO(misko): get rid of $element https://github.com/angular/angular.js/issues/348
|
||||
scope.$element = element;
|
||||
try {
|
||||
value = exprFn(scope);
|
||||
// If we are HTML than save the raw HTML data so that we don't recompute sanitization since
|
||||
// it is expensive.
|
||||
// TODO(misko): turn this into a more generic way to compute this
|
||||
if ((isHtml = (value instanceof HTML)))
|
||||
value = (html = value).html;
|
||||
if (lastValue === value) return;
|
||||
isDomElement = isElement(value);
|
||||
if (!isHtml && !isDomElement && isObject(value)) {
|
||||
value = toJson(value, true);
|
||||
}
|
||||
if (value != lastValue) {
|
||||
lastValue = value;
|
||||
if (isHtml) {
|
||||
element.html(html.get());
|
||||
} else if (isDomElement) {
|
||||
element.html('');
|
||||
element.append(value);
|
||||
} else {
|
||||
element.text(value == undefined ? '' : value);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
$exceptionHandler(e);
|
||||
} finally {
|
||||
if (hadOwnElement) {
|
||||
scope.$element = oldElement;
|
||||
} else {
|
||||
delete scope.$element;
|
||||
}
|
||||
var ngBindHtmlUnsafeDirective = valueFn(function(scope, element, attr) {
|
||||
element.addClass('ng-binding').data('$binding', attr.ngBindHtmlUnsafe);
|
||||
scope.$watch(attr.ngBindHtmlUnsafe, function(value) {
|
||||
element.html(value == undefined ? '' : value);
|
||||
});
|
||||
});
|
||||
|
||||
var ngBindHtmlDirective = ['$sanitize', function($sanitize) {
|
||||
return function(scope, element, attr) {
|
||||
element.addClass('ng-binding').data('$binding', attr.ngBindHtml);
|
||||
scope.$watch(attr.ngBindHtml, function(value) {
|
||||
if (value = $sanitize(value)) {
|
||||
element.html(value);
|
||||
}
|
||||
});
|
||||
}];
|
||||
});
|
||||
}
|
||||
}];
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -292,32 +279,29 @@ angularDirective("ng:bind", function(expression, element){
|
|||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should check ng:bind', function() {
|
||||
expect(using('.doc-example-live').binding('{{salutation}} {{name}}')).
|
||||
toBe('Hello World!');
|
||||
expect(using('.doc-example-live').binding('salutation')).
|
||||
toBe('Hello');
|
||||
expect(using('.doc-example-live').binding('name')).
|
||||
toBe('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!');
|
||||
expect(using('.doc-example-live').binding('salutation')).
|
||||
toBe('Greetings');
|
||||
expect(using('.doc-example-live').binding('name')).
|
||||
toBe('user');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularDirective("ng:bind-template", function(expression, element){
|
||||
element.addClass('ng-binding');
|
||||
var templateFn = compileBindTemplate(expression);
|
||||
return function(element) {
|
||||
var lastValue,
|
||||
scope = this;
|
||||
|
||||
scope.$watch(function() {
|
||||
var value = templateFn(scope, element, true);
|
||||
if (value != lastValue) {
|
||||
element.text(value);
|
||||
lastValue = value;
|
||||
}
|
||||
var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
|
||||
return function(scope, element, attr) {
|
||||
var interpolateFn = $interpolate(attr.ngBindTemplate);
|
||||
element.addClass('ng-binding').data('$binding', interpolateFn);
|
||||
scope.$watch(interpolateFn, function(value) {
|
||||
element.text(value);
|
||||
});
|
||||
};
|
||||
});
|
||||
}
|
||||
}];
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
|
|
@ -392,23 +376,25 @@ angularDirective("ng:bind-template", function(expression, element){
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularDirective("ng:bind-attr", function(expression){
|
||||
return function(element){
|
||||
var lastValue = {},
|
||||
scope = this;
|
||||
|
||||
var ngBindAttrDirective = ['$interpolate', function($interpolate) {
|
||||
return function(scope, element, attr) {
|
||||
var lastValue = {};
|
||||
var interpolateFns = {};
|
||||
scope.$watch(function() {
|
||||
var values = scope.$eval(expression);
|
||||
var values = scope.$eval(attr.ngBindAttr);
|
||||
for(var key in values) {
|
||||
var value = compileBindTemplate(values[key])(scope, element);
|
||||
var exp = values[key],
|
||||
fn = (interpolateFns[exp] ||
|
||||
(interpolateFns[values[key]] = $interpolate(exp))),
|
||||
value = fn(scope);
|
||||
if (lastValue[key] !== value) {
|
||||
lastValue[key] = value;
|
||||
element.attr(key, BOOLEAN_ATTR[lowercase(key)] ? toBoolean(value) : value);
|
||||
attr.$set(key, lastValue[key] = value);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
}
|
||||
}];
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -448,17 +434,31 @@ angularDirective("ng:bind-attr", function(expression){
|
|||
*
|
||||
* TODO: maybe we should consider allowing users to control event propagation in the future.
|
||||
*/
|
||||
angularDirective("ng:click", function(expression, element){
|
||||
return function(element){
|
||||
var self = this;
|
||||
element.bind('click', function(event){
|
||||
self.$apply(expression);
|
||||
var ngEventDirectives = {};
|
||||
forEach('click dblclick mousedown mouseup mouseover mousemove'.split(' '), function(name) {
|
||||
var directiveName = camelCase('ng-' + name);
|
||||
ngEventDirectives[directiveName] = valueFn(function(scope, element, attr) {
|
||||
element.bind(lowercase(name), function(event) {
|
||||
scope.$apply(attr[directiveName]);
|
||||
event.stopPropagation();
|
||||
});
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name angular.directive.ng:dblclick
|
||||
*
|
||||
* @description
|
||||
* The ng:dblclick allows you to specify custom behavior when
|
||||
* element is double-clicked.
|
||||
*
|
||||
* @element ANY
|
||||
* @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate upon
|
||||
* double-click.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name angular.directive.ng:submit
|
||||
|
|
@ -496,48 +496,42 @@ angularDirective("ng:click", function(expression, element){
|
|||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should check ng:submit', function() {
|
||||
expect(binding('list')).toBe('list=[]');
|
||||
expect(binding('list')).toBe('[]');
|
||||
element('.doc-example-live #submit').click();
|
||||
expect(binding('list')).toBe('list=["hello"]');
|
||||
expect(binding('list')).toBe('["hello"]');
|
||||
expect(input('text').val()).toBe('');
|
||||
});
|
||||
it('should ignore empty strings', function() {
|
||||
expect(binding('list')).toBe('list=[]');
|
||||
expect(binding('list')).toBe('[]');
|
||||
element('.doc-example-live #submit').click();
|
||||
element('.doc-example-live #submit').click();
|
||||
expect(binding('list')).toBe('list=["hello"]');
|
||||
expect(binding('list')).toBe('["hello"]');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularDirective("ng:submit", function(expression, element) {
|
||||
return function(element) {
|
||||
var self = this;
|
||||
element.bind('submit', function() {
|
||||
self.$apply(expression);
|
||||
});
|
||||
};
|
||||
var ngSubmitDirective = valueFn(function(scope, element, attrs) {
|
||||
element.bind('submit', function() {
|
||||
scope.$apply(attrs.ngSubmit);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function ngClass(selector) {
|
||||
return function(expression, element) {
|
||||
return function(element) {
|
||||
var scope = this;
|
||||
scope.$watch(expression, function(newVal, oldVal) {
|
||||
if (selector(scope.$index)) {
|
||||
if (oldVal && (newVal !== oldVal)) {
|
||||
if (isObject(oldVal) && !isArray(oldVal))
|
||||
oldVal = map(oldVal, function(v, k) { if (v) return k });
|
||||
element.removeClass(isArray(oldVal) ? oldVal.join(' ') : oldVal);
|
||||
}
|
||||
if (isObject(newVal) && !isArray(newVal))
|
||||
function classDirective(name, selector) {
|
||||
name = 'ngClass' + name;
|
||||
return valueFn(function(scope, element, attr) {
|
||||
scope.$watch(attr[name], function(newVal, oldVal) {
|
||||
if (selector === true || scope.$index % 2 === selector) {
|
||||
if (oldVal && (newVal !== oldVal)) {
|
||||
if (isObject(oldVal) && !isArray(oldVal))
|
||||
oldVal = map(oldVal, function(v, k) { if (v) return k });
|
||||
element.removeClass(isArray(oldVal) ? oldVal.join(' ') : oldVal);
|
||||
}
|
||||
if (isObject(newVal) && !isArray(newVal))
|
||||
newVal = map(newVal, function(v, k) { if (v) return k });
|
||||
if (newVal) element.addClass(isArray(newVal) ? newVal.join(' ') : newVal);
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
if (newVal) element.addClass(isArray(newVal) ? newVal.join(' ') : newVal); }
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -584,7 +578,7 @@ function ngClass(selector) {
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularDirective("ng:class", ngClass(function() {return true;}));
|
||||
var ngClassDirective = classDirective('', true);
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
|
|
@ -624,7 +618,7 @@ angularDirective("ng:class", ngClass(function() {return true;}));
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularDirective("ng:class-odd", ngClass(function(i){return i % 2 === 0;}));
|
||||
var ngClassOddDirective = classDirective('Odd', 0);
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
|
|
@ -663,7 +657,7 @@ angularDirective("ng:class-odd", ngClass(function(i){return i % 2 === 0;}));
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularDirective("ng:class-even", ngClass(function(i){return i % 2 === 1;}));
|
||||
var ngClassEvenDirective = classDirective('Even', 1);
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
|
|
@ -697,13 +691,11 @@ angularDirective("ng:class-even", ngClass(function(i){return i % 2 === 1;}));
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularDirective("ng:show", function(expression, element){
|
||||
return function(element) {
|
||||
var scope = this;
|
||||
scope.$watch(expression, function(value) {
|
||||
element.css('display', toBoolean(value) ? '' : 'none');
|
||||
});
|
||||
};
|
||||
//TODO(misko): refactor to remove element from the DOM
|
||||
var ngShowDirective = valueFn(function(scope, element, attr){
|
||||
scope.$watch(attr.ngShow, function(value){
|
||||
element.css('display', toBoolean(value) ? '' : 'none');
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
@ -738,13 +730,11 @@ angularDirective("ng:show", function(expression, element){
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularDirective("ng:hide", function(expression, element){
|
||||
return function(element) {
|
||||
var scope = this;
|
||||
scope.$watch(expression, function(value) {
|
||||
element.css('display', toBoolean(value) ? 'none' : '');
|
||||
});
|
||||
};
|
||||
//TODO(misko): refactor to remove element from the DOM
|
||||
var ngHideDirective = valueFn(function(scope, element, attr){
|
||||
scope.$watch(attr.ngHide, function(value){
|
||||
element.css('display', toBoolean(value) ? 'none' : '');
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
@ -779,16 +769,13 @@ angularDirective("ng:hide", function(expression, element){
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularDirective("ng:style", function(expression, element) {
|
||||
return function(element) {
|
||||
var scope = this;
|
||||
scope.$watch(expression, function(newStyles, oldStyles) {
|
||||
if (oldStyles && (newStyles !== oldStyles)) {
|
||||
forEach(oldStyles, function(val, style) { element.css(style, '');});
|
||||
}
|
||||
if (newStyles) element.css(newStyles);
|
||||
});
|
||||
};
|
||||
var ngStyleDirective = valueFn(function(scope, element, attr) {
|
||||
scope.$watch(attr.ngStyle, function(newStyles, oldStyles) {
|
||||
if (oldStyles && (newStyles !== oldStyles)) {
|
||||
forEach(oldStyles, function(val, style) { element.css(style, '');});
|
||||
}
|
||||
if (newStyles) element.css(newStyles);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -845,7 +832,22 @@ angularDirective("ng:style", function(expression, element) {
|
|||
</doc:example>
|
||||
*
|
||||
*/
|
||||
angularDirective("ng:cloak", function(expression, element) {
|
||||
element.removeAttr('ng:cloak');
|
||||
element.removeClass('ng-cloak');
|
||||
var ngCloakDirective = valueFn({
|
||||
compile: function(element, attr) {
|
||||
attr.$set(attr.$attr.ngCloak, undefined);
|
||||
element.removeClass('ng-cloak');
|
||||
}
|
||||
});
|
||||
|
||||
function ngAttributeAliasDirective(propName, attrName) {
|
||||
ngAttributeAliasDirectives[camelCase('ng-' + attrName)] = ['$interpolate', function($interpolate) {
|
||||
return function(scope, element, attr) {
|
||||
scope.$watch($interpolate(attr[camelCase('ng-' + attrName)]), function(value) {
|
||||
attr.$set(attrName, value);
|
||||
});
|
||||
}
|
||||
}];
|
||||
}
|
||||
var ngAttributeAliasDirectives = {};
|
||||
forEach(BOOLEAN_ATTR, ngAttributeAliasDirective);
|
||||
ngAttributeAliasDirective(null, 'src');
|
||||
|
|
|
|||
|
|
@ -405,15 +405,14 @@ forEach({
|
|||
|
||||
text: extend((msie < 9)
|
||||
? function(element, value) {
|
||||
// NodeType == 3 is text node
|
||||
if (element.nodeType == 3) {
|
||||
if (isUndefined(value))
|
||||
return element.nodeValue;
|
||||
element.nodeValue = value;
|
||||
} else {
|
||||
if (element.nodeType == 1 /** Element */) {
|
||||
if (isUndefined(value))
|
||||
return element.innerText;
|
||||
element.innerText = value;
|
||||
} else {
|
||||
if (isUndefined(value))
|
||||
return element.nodeValue;
|
||||
element.nodeValue = value;
|
||||
}
|
||||
}
|
||||
: function(element, value) {
|
||||
|
|
|
|||
128
src/markups.js
128
src/markups.js
|
|
@ -1,109 +1,5 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name angular.markup
|
||||
* @description
|
||||
*
|
||||
* Angular markup transforms the content of DOM elements or portions of the content into other
|
||||
* text or DOM elements for further compilation.
|
||||
*
|
||||
* Markup extensions do not themselves produce linking functions. Think of markup as a way to
|
||||
* produce shorthand for a {@link angular.widget widget} or a {@link angular.directive directive}.
|
||||
*
|
||||
* The most prominent example of a markup in Angular is the built-in, double curly markup
|
||||
* `{{expression}}`, which is shorthand for `<span ng:bind="expression"></span>`.
|
||||
*
|
||||
* Create custom markup like this:
|
||||
*
|
||||
* <pre>
|
||||
* angular.markup('newMarkup', function(text, textNode, parentElement){
|
||||
* //tranformation code
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* For more information, see {@link guide/dev_guide.compiler.markup Understanding Angular Markup}
|
||||
* in the Angular Developer Guide.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name angular.attrMarkup
|
||||
* @description
|
||||
*
|
||||
* Attribute markup allows you to modify the state of an attribute's text.
|
||||
*
|
||||
* Attribute markup extends the Angular complier in a way similar to {@link angular.markup},
|
||||
* which allows you to modify the content of a node.
|
||||
*
|
||||
* The most prominent example of an attribute markup in Angular is the built-in double curly markup
|
||||
* which is a shorthand for {@link angular.directive.ng:bind-attr ng:bind-attr}.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* <pre>
|
||||
* angular.attrMarkup('newAttrMarkup', function(attrValue, attrName, element){
|
||||
* //tranformation code
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* For more information about Angular attribute markup, see {@link guide/dev_guide.compiler.markup
|
||||
* Understanding Angular Markup} in the Angular Developer Guide.
|
||||
*/
|
||||
|
||||
|
||||
angularTextMarkup('{{}}', function(text, textNode, parentElement) {
|
||||
var bindings = parseBindings(text),
|
||||
self = this;
|
||||
if (hasBindings(bindings)) {
|
||||
if (isLeafNode(parentElement[0])) {
|
||||
parentElement.attr('ng:bind-template', text);
|
||||
} else {
|
||||
var cursor = textNode, newElement;
|
||||
forEach(parseBindings(text), function(text){
|
||||
var exp = binding(text);
|
||||
if (exp) {
|
||||
newElement = jqLite('<span>');
|
||||
newElement.attr('ng:bind', exp);
|
||||
} else {
|
||||
newElement = jqLite(document.createTextNode(text));
|
||||
}
|
||||
if (msie && text.charAt(0) == ' ') {
|
||||
newElement = jqLite('<span> </span>');
|
||||
var nbsp = newElement.html();
|
||||
newElement.text(text.substr(1));
|
||||
newElement.html(nbsp + newElement.html());
|
||||
}
|
||||
cursor.after(newElement);
|
||||
cursor = newElement;
|
||||
});
|
||||
textNode.remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* This tries to normalize the behavior of value attribute across browsers. If value attribute is
|
||||
* not specified, then specify it to be that of the text.
|
||||
*/
|
||||
angularTextMarkup('option', function(text, textNode, parentElement){
|
||||
if (lowercase(nodeName_(parentElement)) == 'option') {
|
||||
if (msie <= 7) {
|
||||
// In IE7 The issue is that there is no way to see if the value was specified hence
|
||||
// we have to resort to parsing HTML;
|
||||
htmlParser(parentElement[0].outerHTML, {
|
||||
start: function(tag, attrs) {
|
||||
if (isUndefined(attrs.value)) {
|
||||
parentElement.attr('value', text);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (parentElement[0].getAttribute('value') == null) {
|
||||
// jQuery does normalization on 'value' so we have to bypass it.
|
||||
parentElement.attr('value', text);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
|
|
@ -171,7 +67,7 @@ angularTextMarkup('option', function(text, textNode, parentElement){
|
|||
it('should execute ng:click but not reload when no href but name specified', function() {
|
||||
element('#link-5').click();
|
||||
expect(input('value').val()).toEqual('5');
|
||||
expect(element('#link-5').attr('href')).toBe(undefined);
|
||||
expect(element('#link-5').attr('href')).toBe("");
|
||||
});
|
||||
|
||||
it('should only change url when only ng:href', function() {
|
||||
|
|
@ -371,25 +267,3 @@ angularTextMarkup('option', function(text, textNode, parentElement){
|
|||
* @param {template} template any string which can contain '{{}}' markup.
|
||||
*/
|
||||
|
||||
|
||||
var NG_BIND_ATTR = 'ng:bind-attr';
|
||||
var SIDE_EFFECT_ATTRS = {};
|
||||
|
||||
forEach('src,href,multiple,selected,checked,disabled,readonly,required'.split(','), function(name) {
|
||||
SIDE_EFFECT_ATTRS['ng:' + name] = name;
|
||||
});
|
||||
|
||||
angularAttrMarkup('{{}}', function(value, name, element){
|
||||
// don't process existing attribute markup
|
||||
if (angularDirective(name) || angularDirective("@" + name)) return;
|
||||
if (msie && name == 'src')
|
||||
value = decodeURI(value);
|
||||
var bindings = parseBindings(value),
|
||||
bindAttr;
|
||||
if (hasBindings(bindings) || SIDE_EFFECT_ATTRS[name]) {
|
||||
element.removeAttr(name);
|
||||
bindAttr = fromJson(element.attr(NG_BIND_ATTR) || "{}");
|
||||
bindAttr[SIDE_EFFECT_ATTRS[name] || name] = value;
|
||||
element.attr(NG_BIND_ATTR, toJson(bindAttr));
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -345,25 +345,71 @@ function browserTrigger(element, type, keys) {
|
|||
* Finds all bindings with the substring match of name and returns an
|
||||
* array of their values.
|
||||
*
|
||||
* @param {string} name The name to match
|
||||
* @param {string} bindExp The name to match
|
||||
* @return {Array.<string>} String of binding values
|
||||
*/
|
||||
_jQuery.fn.bindings = function(name) {
|
||||
function contains(text, value) {
|
||||
return value instanceof RegExp
|
||||
? value.test(text)
|
||||
: text && text.indexOf(value) >= 0;
|
||||
_jQuery.fn.bindings = function(windowJquery, bindExp) {
|
||||
var result = [], match,
|
||||
bindSelector = '.ng-binding:visible';
|
||||
if (angular.isString(bindExp)) {
|
||||
bindExp = bindExp.replace(/\s/g, '');
|
||||
match = function (actualExp) {
|
||||
if (actualExp) {
|
||||
actualExp = actualExp.replace(/\s/g, '');
|
||||
if (actualExp == bindExp) return true;
|
||||
if (actualExp.indexOf(bindExp) == 0) {
|
||||
return actualExp.charAt(bindExp.length) == '|';
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (bindExp) {
|
||||
match = function(actualExp) {
|
||||
return actualExp && bindExp.exec(actualExp);
|
||||
}
|
||||
} else {
|
||||
match = function(actualExp) {
|
||||
return !!actualExp;
|
||||
};
|
||||
}
|
||||
var result = [];
|
||||
this.find('.ng-binding:visible').each(function() {
|
||||
var element = new _jQuery(this);
|
||||
if (!angular.isDefined(name) ||
|
||||
contains(element.attr('ng:bind'), name) ||
|
||||
contains(element.attr('ng:bind-template'), name)) {
|
||||
if (element.is('input, textarea')) {
|
||||
result.push(element.val());
|
||||
var selection = this.find(bindSelector);
|
||||
if (this.is(bindSelector)) {
|
||||
selection = selection.add(this);
|
||||
}
|
||||
|
||||
function push(value) {
|
||||
if (value == undefined) {
|
||||
value = '';
|
||||
} else if (typeof value != 'string') {
|
||||
value = angular.toJson(value);
|
||||
}
|
||||
result.push('' + value);
|
||||
}
|
||||
|
||||
selection.each(function() {
|
||||
var element = windowJquery(this),
|
||||
binding;
|
||||
if (binding = element.data('$binding')) {
|
||||
if (typeof binding == 'string') {
|
||||
if (match(binding)) {
|
||||
push(element.scope().$eval(binding));
|
||||
}
|
||||
} else {
|
||||
result.push(element.html());
|
||||
if (!angular.isArray(binding)) {
|
||||
binding = [binding];
|
||||
}
|
||||
for(var fns, j=0, jj=binding.length; j<jj; j++) {
|
||||
fns = binding[j];
|
||||
if (fns.parts) {
|
||||
fns = fns.parts;
|
||||
} else {
|
||||
fns = [fns];
|
||||
}
|
||||
for (var scope, fn, i = 0, ii = fns.length; i < ii; i++) {
|
||||
if(match((fn = fns[i]).exp)) {
|
||||
push(fn(scope = scope || element.scope()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ angular.scenario.dsl('using', function() {
|
|||
angular.scenario.dsl('binding', function() {
|
||||
return function(name) {
|
||||
return this.addFutureAction("select binding '" + name + "'", function($window, $document, done) {
|
||||
var values = $document.elements().bindings(name);
|
||||
var values = $document.elements().bindings($window.angular.element, name);
|
||||
if (!values.length) {
|
||||
return done("Binding selector '" + name + "' did not match.");
|
||||
}
|
||||
|
|
@ -260,7 +260,7 @@ angular.scenario.dsl('repeater', function() {
|
|||
|
||||
chain.column = function(binding) {
|
||||
return this.addFutureAction("repeater '" + this.label + "' column '" + binding + "'", function($window, $document, done) {
|
||||
done(null, $document.elements().bindings(binding));
|
||||
done(null, $document.elements().bindings($window.angular.element, binding));
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -269,7 +269,7 @@ angular.scenario.dsl('repeater', function() {
|
|||
var matches = $document.elements().slice(index, index + 1);
|
||||
if (!matches.length)
|
||||
return done('row ' + index + ' out of bounds');
|
||||
done(null, matches.bindings());
|
||||
done(null, matches.bindings($window.angular.element));
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
// watch the 'compile' expression for changes
|
||||
return scope.$eval(attrs.compile);
|
||||
},
|
||||
function(scope, value) {
|
||||
function(value) {
|
||||
// when the 'compile' expression changes
|
||||
// assign it into the current DOM
|
||||
element.html(value);
|
||||
|
|
@ -631,7 +631,7 @@ function $CompileProvider($provide) {
|
|||
bindings = parent.data('$binding') || [];
|
||||
bindings.push(interpolateFn);
|
||||
parent.data('$binding', bindings).addClass('ng-binding');
|
||||
scope.$watch(interpolateFn, function(scope, value) {
|
||||
scope.$watch(interpolateFn, function(value) {
|
||||
node[0].nodeValue = value;
|
||||
});
|
||||
})
|
||||
|
|
@ -656,7 +656,7 @@ function $CompileProvider($provide) {
|
|||
compile: function(element, attr) {
|
||||
if (interpolateFn) {
|
||||
return function(scope, element, attr) {
|
||||
scope.$watch(interpolateFn, function(scope, value){
|
||||
scope.$watch(interpolateFn, function(value) {
|
||||
attr.$set(name, value);
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -94,7 +94,6 @@ function $FilterProvider($provide) {
|
|||
register('currency', currencyFilter);
|
||||
register('date', dateFilter);
|
||||
register('filter', filterFilter);
|
||||
register('html', htmlFilter);
|
||||
register('json', jsonFilter);
|
||||
register('limitTo', limitToFilter);
|
||||
register('linky', linkyFilter);
|
||||
|
|
|
|||
|
|
@ -64,17 +64,17 @@
|
|||
<doc:scenario>
|
||||
it('should search across all fields when filtering with a string', function() {
|
||||
input('searchText').enter('m');
|
||||
expect(repeater('#searchTextResults tr', 'friend in friends').column('name')).
|
||||
expect(repeater('#searchTextResults tr', 'friend in friends').column('friend.name')).
|
||||
toEqual(['Mary', 'Mike', 'Adam']);
|
||||
|
||||
input('searchText').enter('76');
|
||||
expect(repeater('#searchTextResults tr', 'friend in friends').column('name')).
|
||||
expect(repeater('#searchTextResults tr', 'friend in friends').column('friend.name')).
|
||||
toEqual(['John', 'Julie']);
|
||||
});
|
||||
|
||||
it('should search in specific fields when filtering with a predicate object', function() {
|
||||
input('search.$').enter('i');
|
||||
expect(repeater('#searchObjResults tr', 'friend in friends').column('name')).
|
||||
expect(repeater('#searchObjResults tr', 'friend in friends').column('friend.name')).
|
||||
toEqual(['Mary', 'Mike', 'Julie']);
|
||||
});
|
||||
</doc:scenario>
|
||||
|
|
|
|||
|
|
@ -385,7 +385,7 @@ function dateFilter($locale) {
|
|||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should jsonify filtered objects', function() {
|
||||
expect(binding('| json')).toBe('{\n "name":"value"}');
|
||||
expect(binding("{'name':'value'}")).toBe('{\n "name":"value"}');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
|
@ -420,108 +420,6 @@ var lowercaseFilter = valueFn(lowercase);
|
|||
var uppercaseFilter = valueFn(uppercase);
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc filter
|
||||
* @name angular.module.ng.$filter.html
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Prevents the input from getting escaped by angular. By default the input is sanitized and
|
||||
* inserted into the DOM as is.
|
||||
*
|
||||
* The input is sanitized by parsing the html into tokens. All safe tokens (from a whitelist) are
|
||||
* then serialized back to properly escaped html string. This means that no unsafe input can make
|
||||
* it into the returned string, however, since our parser is more strict than a typical browser
|
||||
* parser, it's possible that some obscure input, which would be recognized as valid HTML by a
|
||||
* browser, won't make it through the sanitizer.
|
||||
*
|
||||
* If you hate your users, you may call the filter with optional 'unsafe' argument, which bypasses
|
||||
* the html sanitizer, but makes your application vulnerable to XSS and other attacks. Using this
|
||||
* option is strongly discouraged and should be used only if you absolutely trust the input being
|
||||
* filtered and you can't get the content through the sanitizer.
|
||||
*
|
||||
* @param {string} html Html input.
|
||||
* @param {string=} option If 'unsafe' then do not sanitize the HTML input.
|
||||
* @returns {string} Sanitized or raw html.
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
function Ctrl($scope) {
|
||||
$scope.snippet =
|
||||
'<p style="color:blue">an html\n' +
|
||||
'<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' +
|
||||
'snippet</p>';
|
||||
}
|
||||
</script>
|
||||
<div ng:controller="Ctrl">
|
||||
Snippet: <textarea ng:model="snippet" cols="60" rows="3"></textarea>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Filter</td>
|
||||
<td>Source</td>
|
||||
<td>Rendered</td>
|
||||
</tr>
|
||||
<tr id="html-filter">
|
||||
<td>html filter</td>
|
||||
<td>
|
||||
<pre><div ng:bind="snippet | html"><br/></div></pre>
|
||||
</td>
|
||||
<td>
|
||||
<div ng:bind="snippet | html"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="escaped-html">
|
||||
<td>no filter</td>
|
||||
<td><pre><div ng:bind="snippet"><br/></div></pre></td>
|
||||
<td><div ng:bind="snippet"></div></td>
|
||||
</tr>
|
||||
<tr id="html-unsafe-filter">
|
||||
<td>unsafe html filter</td>
|
||||
<td><pre><div ng:bind="snippet | html:'unsafe'"><br/></div></pre></td>
|
||||
<td><div ng:bind="snippet | html:'unsafe'"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should sanitize the html snippet ', function() {
|
||||
expect(using('#html-filter').binding('snippet | html')).
|
||||
toBe('<p>an html\n<em>click here</em>\nsnippet</p>');
|
||||
});
|
||||
|
||||
it('should escape snippet without any filter', function() {
|
||||
expect(using('#escaped-html').binding('snippet')).
|
||||
toBe("<p style=\"color:blue\">an html\n" +
|
||||
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
|
||||
"snippet</p>");
|
||||
});
|
||||
|
||||
it('should inline raw snippet if filtered as unsafe', function() {
|
||||
expect(using('#html-unsafe-filter').binding("snippet | html:'unsafe'")).
|
||||
toBe("<p style=\"color:blue\">an html\n" +
|
||||
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
|
||||
"snippet</p>");
|
||||
});
|
||||
|
||||
it('should update', function() {
|
||||
input('snippet').enter('new <b>text</b>');
|
||||
expect(using('#html-filter').binding('snippet | html')).toBe('new <b>text</b>');
|
||||
expect(using('#escaped-html').binding('snippet')).toBe("new <b>text</b>");
|
||||
expect(using('#html-unsafe-filter').binding("snippet | html:'unsafe'")).toBe('new <b>text</b>');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
//TODO(misko): turn sensitization into injectable service
|
||||
function htmlFilter() {
|
||||
return function(html, option){
|
||||
return new HTML(html, option);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc filter
|
||||
* @name angular.module.ng.$filter.linky
|
||||
|
|
@ -558,10 +456,10 @@ function htmlFilter() {
|
|||
<tr id="linky-filter">
|
||||
<td>linky filter</td>
|
||||
<td>
|
||||
<pre><div ng:bind="snippet | linky"><br/></div></pre>
|
||||
<pre><div ng:bind-html="snippet | linky"><br/></div></pre>
|
||||
</td>
|
||||
<td>
|
||||
<div ng:bind="snippet | linky"></div>
|
||||
<div ng:bind-html="snippet | linky"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="escaped-html">
|
||||
|
|
@ -574,10 +472,10 @@ function htmlFilter() {
|
|||
<doc:scenario>
|
||||
it('should linkify the snippet with urls', function() {
|
||||
expect(using('#linky-filter').binding('snippet | linky')).
|
||||
toBe('Pretty text with some links:\n' +
|
||||
'<a href="http://angularjs.org/">http://angularjs.org/</a>,\n' +
|
||||
'<a href="mailto:us@somewhere.org">us@somewhere.org</a>,\n' +
|
||||
'<a href="mailto:another@somewhere.org">another@somewhere.org</a>,\n' +
|
||||
toBe('Pretty text with some links: ' +
|
||||
'<a href="http://angularjs.org/">http://angularjs.org/</a>, ' +
|
||||
'<a href="mailto:us@somewhere.org">us@somewhere.org</a>, ' +
|
||||
'<a href="mailto:another@somewhere.org">another@somewhere.org</a>, ' +
|
||||
'and one more: <a href="ftp://127.0.0.1/">ftp://127.0.0.1/</a>.');
|
||||
});
|
||||
|
||||
|
|
@ -624,6 +522,6 @@ function linkyFilter() {
|
|||
raw = raw.substring(i + match[0].length);
|
||||
}
|
||||
writer.chars(raw);
|
||||
return new HTML(html.join(''));
|
||||
return html.join('');
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@
|
|||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should be reverse ordered by aged', function() {
|
||||
expect(binding('predicate')).toBe('Sorting predicate = -age; reverse = ');
|
||||
expect(binding('predicate')).toBe('-age');
|
||||
expect(repeater('table.friend', 'friend in friends').column('friend.age')).
|
||||
toEqual(['35', '29', '21', '19', '10']);
|
||||
expect(repeater('table.friend', 'friend in friends').column('friend.name')).
|
||||
|
|
|
|||
|
|
@ -82,28 +82,31 @@
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularWidget('form', function(form){
|
||||
this.descend(true);
|
||||
this.directives(true);
|
||||
return ['$formFactory', '$element', function($formFactory, formElement) {
|
||||
var name = formElement.attr('name'),
|
||||
parentForm = $formFactory.forElement(formElement),
|
||||
form = $formFactory(parentForm);
|
||||
formElement.data('$form', form);
|
||||
formElement.bind('submit', function(event) {
|
||||
if (!formElement.attr('action')) event.preventDefault();
|
||||
});
|
||||
if (name) {
|
||||
this[name] = form;
|
||||
var ngFormDirective = ['$formFactory', function($formFactory) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
compile: function() {
|
||||
return {
|
||||
pre: function(scope, formElement, attr) {
|
||||
var name = attr.name,
|
||||
parentForm = $formFactory.forElement(formElement),
|
||||
form = $formFactory(parentForm);
|
||||
formElement.data('$form', form);
|
||||
formElement.bind('submit', function(event){
|
||||
if (!attr.action) event.preventDefault();
|
||||
});
|
||||
if (name) {
|
||||
scope[name] = form;
|
||||
}
|
||||
watch('valid');
|
||||
watch('invalid');
|
||||
function watch(name) {
|
||||
form.$watch('$' + name, function(value) {
|
||||
formElement[value ? 'addClass' : 'removeClass']('ng-' + name);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
watch('valid');
|
||||
watch('invalid');
|
||||
function watch(name) {
|
||||
form.$watch('$' + name, function(value) {
|
||||
formElement[value ? 'addClass' : 'removeClass']('ng-' + name);
|
||||
});
|
||||
}
|
||||
}];
|
||||
});
|
||||
|
||||
angularWidget('ng:form', angularWidget('form'));
|
||||
};
|
||||
}];
|
||||
|
|
|
|||
|
|
@ -542,23 +542,23 @@ angularInputType('checkbox', function(inputElement, widget) {
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularInputType('radio', function(inputElement, widget) {
|
||||
angularInputType('radio', function(inputElement, widget, attr) {
|
||||
//correct the name
|
||||
inputElement.attr('name', widget.$id + '@' + inputElement.attr('name'));
|
||||
attr.$set('name', widget.$id + '@' + attr.name);
|
||||
inputElement.bind('click', function() {
|
||||
widget.$apply(function() {
|
||||
if (inputElement[0].checked) {
|
||||
widget.$emit('$viewChange', widget.$value);
|
||||
widget.$emit('$viewChange', attr.value);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
widget.$render = function() {
|
||||
inputElement[0].checked = isDefined(widget.$value) && (widget.$value == widget.$viewValue);
|
||||
inputElement[0].checked = isDefined(attr.value) && (attr.value == widget.$viewValue);
|
||||
};
|
||||
|
||||
if (inputElement[0].checked) {
|
||||
widget.$viewValue = widget.$value;
|
||||
widget.$viewValue = attr.value;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -664,28 +664,28 @@ var HTML5_INPUTS_TYPES = makeMap(
|
|||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should initialize to model', function() {
|
||||
expect(binding('user')).toEqual('{\n \"last\":\"visitor",\n \"name\":\"guest\"}');
|
||||
expect(binding('user')).toEqual('{"last":"visitor","name":"guest"}');
|
||||
expect(binding('myForm.userName.$valid')).toEqual('true');
|
||||
expect(binding('myForm.$valid')).toEqual('true');
|
||||
});
|
||||
|
||||
it('should be invalid if empty when required', function() {
|
||||
input('user.name').enter('');
|
||||
expect(binding('user')).toEqual('{\n \"last\":\"visitor",\n \"name\":\"\"}');
|
||||
expect(binding('user')).toEqual('{"last":"visitor","name":""}');
|
||||
expect(binding('myForm.userName.$valid')).toEqual('false');
|
||||
expect(binding('myForm.$valid')).toEqual('false');
|
||||
});
|
||||
|
||||
it('should be valid if empty when min length is set', function() {
|
||||
input('user.last').enter('');
|
||||
expect(binding('user')).toEqual('{\n \"last\":\"",\n \"name\":\"guest\"}');
|
||||
expect(binding('user')).toEqual('{"last":"","name":"guest"}');
|
||||
expect(binding('myForm.lastName.$valid')).toEqual('true');
|
||||
expect(binding('myForm.$valid')).toEqual('true');
|
||||
});
|
||||
|
||||
it('should be invalid if less than required min length', function() {
|
||||
input('user.last').enter('xx');
|
||||
expect(binding('user')).toEqual('{\n \"last\":\"xx",\n \"name\":\"guest\"}');
|
||||
expect(binding('user')).toEqual('{"last":"xx","name":"guest"}');
|
||||
expect(binding('myForm.lastName.$valid')).toEqual('false');
|
||||
expect(binding('myForm.lastName.$error')).toMatch(/MINLENGTH/);
|
||||
expect(binding('myForm.$valid')).toEqual('false');
|
||||
|
|
@ -694,7 +694,7 @@ var HTML5_INPUTS_TYPES = makeMap(
|
|||
it('should be valid if longer than max length', function() {
|
||||
input('user.last').enter('some ridiculously long name');
|
||||
expect(binding('user'))
|
||||
.toEqual('{\n \"last\":\"some ridiculously long name",\n \"name\":\"guest\"}');
|
||||
.toEqual('{"last":"some ridiculously long name","name":"guest"}');
|
||||
expect(binding('myForm.lastName.$valid')).toEqual('false');
|
||||
expect(binding('myForm.lastName.$error')).toMatch(/MAXLENGTH/);
|
||||
expect(binding('myForm.$valid')).toEqual('false');
|
||||
|
|
@ -702,26 +702,24 @@ var HTML5_INPUTS_TYPES = makeMap(
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularWidget('input', function(inputElement){
|
||||
this.directives(true);
|
||||
this.descend(true);
|
||||
var modelExp = inputElement.attr('ng:model');
|
||||
return modelExp &&
|
||||
['$defer', '$formFactory', '$element',
|
||||
function($defer, $formFactory, inputElement) {
|
||||
var inputDirective = ['$defer', '$formFactory', function($defer, $formFactory) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
link: function(modelScope, inputElement, attr) {
|
||||
if (!attr.ngModel) return;
|
||||
|
||||
var form = $formFactory.forElement(inputElement),
|
||||
// We have to use .getAttribute, since jQuery tries to be smart and use the
|
||||
// type property. Trouble is some browser change unknown to text.
|
||||
type = inputElement[0].getAttribute('type') || 'text',
|
||||
type = attr.type || 'text',
|
||||
TypeController,
|
||||
modelScope = this,
|
||||
patternMatch, widget,
|
||||
pattern = trim(inputElement.attr('ng:pattern')),
|
||||
minlength = parseInt(inputElement.attr('ng:minlength'), 10),
|
||||
maxlength = parseInt(inputElement.attr('ng:maxlength'), 10),
|
||||
pattern = attr.ngPattern,
|
||||
modelExp = attr.ngModel,
|
||||
minlength = parseInt(attr.ngMinlength, 10),
|
||||
maxlength = parseInt(attr.ngMaxlength, 10),
|
||||
loadFromScope = type.match(/^\s*\@\s*(.*)/);
|
||||
|
||||
|
||||
if (!pattern) {
|
||||
patternMatch = valueFn(true);
|
||||
} else {
|
||||
|
|
@ -743,7 +741,7 @@ angularWidget('input', function(inputElement){
|
|||
|
||||
type = lowercase(type);
|
||||
TypeController = (loadFromScope
|
||||
? (assertArgFn(this.$eval(loadFromScope[1]), loadFromScope[1])).$unboundFn
|
||||
? (assertArgFn(modelScope.$eval(loadFromScope[1]), loadFromScope[1])).$unboundFn
|
||||
: angularInputType(type)) || noop;
|
||||
|
||||
if (!HTML5_INPUTS_TYPES[type]) {
|
||||
|
|
@ -757,26 +755,21 @@ angularWidget('input', function(inputElement){
|
|||
}
|
||||
|
||||
//TODO(misko): setting $inject is a hack
|
||||
!TypeController.$inject && (TypeController.$inject = ['$element', '$scope']);
|
||||
!TypeController.$inject && (TypeController.$inject = ['$element', '$scope', '$attr']);
|
||||
widget = form.$createWidget({
|
||||
scope: modelScope,
|
||||
model: modelExp,
|
||||
onChange: inputElement.attr('ng:change'),
|
||||
alias: inputElement.attr('name'),
|
||||
onChange: attr.ngChange,
|
||||
alias: attr.name,
|
||||
controller: TypeController,
|
||||
controllerArgs: {$element: inputElement}
|
||||
controllerArgs: {$element: inputElement, $attr: attr}
|
||||
});
|
||||
|
||||
watchElementProperty(this, widget, 'value', inputElement);
|
||||
watchElementProperty(this, widget, 'required', inputElement);
|
||||
watchElementProperty(this, widget, 'readonly', inputElement);
|
||||
watchElementProperty(this, widget, 'disabled', inputElement);
|
||||
|
||||
widget.$pristine = !(widget.$dirty = false);
|
||||
|
||||
widget.$on('$validate', function() {
|
||||
var $viewValue = trim(widget.$viewValue),
|
||||
inValid = widget.$required && !$viewValue,
|
||||
inValid = attr.required && !$viewValue,
|
||||
tooLong = maxlength && $viewValue && $viewValue.length > maxlength,
|
||||
tooShort = minlength && $viewValue && $viewValue.length < minlength,
|
||||
missMatch = $viewValue && !patternMatch($viewValue);
|
||||
|
|
@ -812,7 +805,7 @@ angularWidget('input', function(inputElement){
|
|||
inputElement.val(widget.$viewValue || '');
|
||||
};
|
||||
|
||||
inputElement.bind('keydown change input', function(event) {
|
||||
inputElement.bind('keydown change input', function(event){
|
||||
var key = event.keyCode;
|
||||
if (/*command*/ key != 91 &&
|
||||
/*modifiers*/ !(15 < key && key < 19) &&
|
||||
|
|
@ -827,8 +820,9 @@ angularWidget('input', function(inputElement){
|
|||
}
|
||||
});
|
||||
}
|
||||
}];
|
||||
});
|
||||
}
|
||||
};
|
||||
}];
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -856,24 +850,3 @@ angularWidget('input', function(inputElement){
|
|||
* @param {string=} ng:change Angular expression to be executed when input changes due to user
|
||||
* interaction with the input element.
|
||||
*/
|
||||
angularWidget('textarea', angularWidget('input'));
|
||||
|
||||
|
||||
function watchElementProperty(modelScope, widget, name, element) {
|
||||
var bindAttr = fromJson(element.attr('ng:bind-attr') || '{}'),
|
||||
match = /\s*\{\{(.*)\}\}\s*/.exec(bindAttr[name]),
|
||||
isBoolean = BOOLEAN_ATTR[name];
|
||||
widget['$' + name] = isBoolean
|
||||
? ( // some browsers return true some '' when required is set without value.
|
||||
isString(element.prop(name)) || !!element.prop(name) ||
|
||||
// this is needed for ie9, since it will treat boolean attributes as false
|
||||
!!element[0].attributes[name])
|
||||
: element.attr(name);
|
||||
if (bindAttr[name] && match) {
|
||||
modelScope.$watch(match[1], function(value) {
|
||||
widget['$' + name] = isBoolean ? !!value : value;
|
||||
widget.$emit('$validate');
|
||||
widget.$render && widget.$render();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,321 +112,335 @@
|
|||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should check ng:options', function() {
|
||||
expect(binding('color')).toMatch('red');
|
||||
expect(binding('{selected_color:color}')).toMatch('red');
|
||||
select('color').option('0');
|
||||
expect(binding('color')).toMatch('black');
|
||||
expect(binding('{selected_color:color}')).toMatch('black');
|
||||
using('.nullable').select('color').option('');
|
||||
expect(binding('color')).toMatch('null');
|
||||
expect(binding('{selected_color:color}')).toMatch('null');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
|
||||
var ngOptionsDirective = valueFn({ terminal: true });
|
||||
var selectDirective = ['$formFactory', '$compile', '$parse',
|
||||
function($formFactory, $compile, $parse){
|
||||
//00001111100000000000222200000000000000000000003333000000000000044444444444444444000000000555555555555555550000000666666666666666660000000000000007777
|
||||
var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*)$/;
|
||||
|
||||
//00001111100000000000222200000000000000000000003333000000000000044444444444444444000000000555555555555555550000000666666666666666660000000000000007777
|
||||
var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*)$/;
|
||||
return {
|
||||
restrict: 'E',
|
||||
link: function(modelScope, selectElement, attr) {
|
||||
if (!attr.ngModel) return;
|
||||
var form = $formFactory.forElement(selectElement),
|
||||
multiple = attr.multiple,
|
||||
optionsExp = attr.ngOptions,
|
||||
modelExp = attr.ngModel,
|
||||
widget = form.$createWidget({
|
||||
scope: modelScope,
|
||||
model: modelExp,
|
||||
onChange: attr.ngChange,
|
||||
alias: attr.name,
|
||||
controller: ['$scope', optionsExp ? Options : (multiple ? Multiple : Single)]});
|
||||
|
||||
selectElement.bind('$destroy', function() { widget.$destroy(); });
|
||||
|
||||
angularWidget('select', function(element){
|
||||
this.directives(true);
|
||||
this.descend(true);
|
||||
return element.attr('ng:model') &&
|
||||
['$formFactory', '$compile', '$parse', '$element',
|
||||
function($formFactory, $compile, $parse, selectElement){
|
||||
var modelScope = this,
|
||||
match,
|
||||
form = $formFactory.forElement(selectElement),
|
||||
multiple = selectElement.attr('multiple'),
|
||||
optionsExp = selectElement.attr('ng:options'),
|
||||
modelExp = selectElement.attr('ng:model'),
|
||||
widget = form.$createWidget({
|
||||
scope: modelScope,
|
||||
model: modelExp,
|
||||
onChange: selectElement.attr('ng:change'),
|
||||
alias: selectElement.attr('name'),
|
||||
controller: ['$scope', optionsExp ? Options : (multiple ? Multiple : Single)]});
|
||||
widget.$pristine = !(widget.$dirty = false);
|
||||
|
||||
selectElement.bind('$destroy', function() { widget.$destroy(); });
|
||||
|
||||
widget.$pristine = !(widget.$dirty = false);
|
||||
|
||||
watchElementProperty(modelScope, widget, 'required', selectElement);
|
||||
watchElementProperty(modelScope, widget, 'readonly', selectElement);
|
||||
watchElementProperty(modelScope, widget, 'disabled', selectElement);
|
||||
|
||||
widget.$on('$validate', function() {
|
||||
var valid = !widget.$required || !!widget.$modelValue;
|
||||
if (valid && multiple && widget.$required) valid = !!widget.$modelValue.length;
|
||||
if (valid !== !widget.$error.REQUIRED) {
|
||||
widget.$emit(valid ? '$valid' : '$invalid', 'REQUIRED');
|
||||
}
|
||||
});
|
||||
|
||||
widget.$on('$viewChange', function() {
|
||||
widget.$pristine = !(widget.$dirty = true);
|
||||
});
|
||||
|
||||
forEach(['valid', 'invalid', 'pristine', 'dirty'], function(name) {
|
||||
widget.$watch('$' + name, function(value) {
|
||||
selectElement[value ? 'addClass' : 'removeClass']('ng-' + name);
|
||||
});
|
||||
});
|
||||
|
||||
////////////////////////////
|
||||
|
||||
function Multiple(widget) {
|
||||
widget.$render = function() {
|
||||
var items = new HashMap(widget.$viewValue);
|
||||
forEach(selectElement.children(), function(option){
|
||||
option.selected = isDefined(items.get(option.value));
|
||||
});
|
||||
};
|
||||
|
||||
selectElement.bind('change', function() {
|
||||
widget.$apply(function() {
|
||||
var array = [];
|
||||
forEach(selectElement.children(), function(option){
|
||||
if (option.selected) {
|
||||
array.push(option.value);
|
||||
}
|
||||
});
|
||||
widget.$emit('$viewChange', array);
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function Single(widget) {
|
||||
widget.$render = function() {
|
||||
selectElement.val(widget.$viewValue);
|
||||
};
|
||||
|
||||
selectElement.bind('change', function() {
|
||||
widget.$apply(function() {
|
||||
widget.$emit('$viewChange', selectElement.val());
|
||||
});
|
||||
});
|
||||
|
||||
widget.$viewValue = selectElement.val();
|
||||
}
|
||||
|
||||
function Options(widget) {
|
||||
var match;
|
||||
|
||||
if (! (match = optionsExp.match(NG_OPTIONS_REGEXP))) {
|
||||
throw Error(
|
||||
"Expected ng:options in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
|
||||
" but got '" + optionsExp + "'.");
|
||||
}
|
||||
|
||||
var displayFn = $parse(match[2] || match[1]),
|
||||
valueName = match[4] || match[6],
|
||||
keyName = match[5],
|
||||
groupByFn = $parse(match[3] || ''),
|
||||
valueFn = $parse(match[2] ? match[1] : valueName),
|
||||
valuesFn = $parse(match[7]),
|
||||
// we can't just jqLite('<option>') since jqLite is not smart enough
|
||||
// to create it in <select> and IE barfs otherwise.
|
||||
optionTemplate = jqLite(document.createElement('option')),
|
||||
optGroupTemplate = jqLite(document.createElement('optgroup')),
|
||||
nullOption = false, // if false then user will not be able to select it
|
||||
// This is an array of array of existing option groups in DOM. We try to reuse these if possible
|
||||
// optionGroupsCache[0] is the options with no option group
|
||||
// optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element
|
||||
optionGroupsCache = [[{element: selectElement, label:''}]];
|
||||
|
||||
// find existing special options
|
||||
forEach(selectElement.children(), function(option) {
|
||||
if (option.value == '') {
|
||||
// developer declared null option, so user should be able to select it
|
||||
nullOption = jqLite(option).remove();
|
||||
// compile the element since there might be bindings in it
|
||||
$compile(nullOption)(modelScope);
|
||||
widget.$on('$validate', function() {
|
||||
var valid = !attr.required || !!widget.$modelValue;
|
||||
if (valid && multiple && attr.required) valid = !!widget.$modelValue.length;
|
||||
if (valid !== !widget.$error.REQUIRED) {
|
||||
widget.$emit(valid ? '$valid' : '$invalid', 'REQUIRED');
|
||||
}
|
||||
});
|
||||
selectElement.html(''); // clear contents
|
||||
|
||||
selectElement.bind('change', function() {
|
||||
widget.$apply(function() {
|
||||
var optionGroup,
|
||||
collection = valuesFn(modelScope) || [],
|
||||
key = selectElement.val(),
|
||||
tempScope = inherit(modelScope),
|
||||
value, optionElement, index, groupIndex, length, groupLength;
|
||||
widget.$on('$viewChange', function() {
|
||||
widget.$pristine = !(widget.$dirty = true);
|
||||
});
|
||||
|
||||
if (multiple) {
|
||||
value = [];
|
||||
for (groupIndex = 0, groupLength = optionGroupsCache.length;
|
||||
groupIndex < groupLength;
|
||||
groupIndex++) {
|
||||
// list of options for that group. (first item has the parent)
|
||||
optionGroup = optionGroupsCache[groupIndex];
|
||||
forEach(['valid', 'invalid', 'pristine', 'dirty'], function(name) {
|
||||
widget.$watch('$' + name, function(value) {
|
||||
selectElement[value ? 'addClass' : 'removeClass']('ng-' + name);
|
||||
});
|
||||
});
|
||||
|
||||
for(index = 1, length = optionGroup.length; index < length; index++) {
|
||||
if ((optionElement = optionGroup[index].element)[0].selected) {
|
||||
if (keyName) tempScope[keyName] = key;
|
||||
tempScope[valueName] = collection[optionElement.val()];
|
||||
value.push(valueFn(tempScope));
|
||||
////////////////////////////
|
||||
|
||||
function Multiple(widget) {
|
||||
widget.$render = function() {
|
||||
var items = new HashMap(this.$viewValue);
|
||||
forEach(selectElement.children(), function(option){
|
||||
option.selected = isDefined(items.get(option.value));
|
||||
});
|
||||
};
|
||||
|
||||
selectElement.bind('change', function() {
|
||||
widget.$apply(function() {
|
||||
var array = [];
|
||||
forEach(selectElement.children(), function(option){
|
||||
if (option.selected) {
|
||||
array.push(option.value);
|
||||
}
|
||||
});
|
||||
widget.$emit('$viewChange', array);
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function Single(widget) {
|
||||
widget.$render = function() {
|
||||
selectElement.val(widget.$viewValue);
|
||||
};
|
||||
|
||||
selectElement.bind('change', function() {
|
||||
widget.$apply(function() {
|
||||
widget.$emit('$viewChange', selectElement.val());
|
||||
});
|
||||
});
|
||||
|
||||
widget.$viewValue = selectElement.val();
|
||||
}
|
||||
|
||||
function Options(widget) {
|
||||
var match;
|
||||
|
||||
if (! (match = optionsExp.match(NG_OPTIONS_REGEXP))) {
|
||||
throw Error(
|
||||
"Expected ng:options in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
|
||||
" but got '" + optionsExp + "'.");
|
||||
}
|
||||
|
||||
var displayFn = $parse(match[2] || match[1]),
|
||||
valueName = match[4] || match[6],
|
||||
keyName = match[5],
|
||||
groupByFn = $parse(match[3] || ''),
|
||||
valueFn = $parse(match[2] ? match[1] : valueName),
|
||||
valuesFn = $parse(match[7]),
|
||||
// we can't just jqLite('<option>') since jqLite is not smart enough
|
||||
// to create it in <select> and IE barfs otherwise.
|
||||
optionTemplate = jqLite(document.createElement('option')),
|
||||
optGroupTemplate = jqLite(document.createElement('optgroup')),
|
||||
nullOption = false, // if false then user will not be able to select it
|
||||
// This is an array of array of existing option groups in DOM. We try to reuse these if possible
|
||||
// optionGroupsCache[0] is the options with no option group
|
||||
// optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element
|
||||
optionGroupsCache = [[{element: selectElement, label:''}]];
|
||||
|
||||
// find existing special options
|
||||
forEach(selectElement.children(), function(option) {
|
||||
if (option.value == '') {
|
||||
// developer declared null option, so user should be able to select it
|
||||
nullOption = jqLite(option).remove();
|
||||
// compile the element since there might be bindings in it
|
||||
$compile(nullOption)(modelScope);
|
||||
}
|
||||
});
|
||||
selectElement.html(''); // clear contents
|
||||
|
||||
selectElement.bind('change', function() {
|
||||
widget.$apply(function() {
|
||||
var optionGroup,
|
||||
collection = valuesFn(modelScope) || [],
|
||||
key = selectElement.val(),
|
||||
tempScope = inherit(modelScope),
|
||||
value, optionElement, index, groupIndex, length, groupLength;
|
||||
|
||||
if (multiple) {
|
||||
value = [];
|
||||
for (groupIndex = 0, groupLength = optionGroupsCache.length;
|
||||
groupIndex < groupLength;
|
||||
groupIndex++) {
|
||||
// list of options for that group. (first item has the parent)
|
||||
optionGroup = optionGroupsCache[groupIndex];
|
||||
|
||||
for(index = 1, length = optionGroup.length; index < length; index++) {
|
||||
if ((optionElement = optionGroup[index].element)[0].selected) {
|
||||
if (keyName) tempScope[keyName] = key;
|
||||
tempScope[valueName] = collection[optionElement.val()];
|
||||
value.push(valueFn(tempScope));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (key == '?') {
|
||||
value = undefined;
|
||||
} else if (key == ''){
|
||||
value = null;
|
||||
} else {
|
||||
tempScope[valueName] = collection[key];
|
||||
if (keyName) tempScope[keyName] = key;
|
||||
value = valueFn(tempScope);
|
||||
if (key == '?') {
|
||||
value = undefined;
|
||||
} else if (key == ''){
|
||||
value = null;
|
||||
} else {
|
||||
tempScope[valueName] = collection[key];
|
||||
if (keyName) tempScope[keyName] = key;
|
||||
value = valueFn(tempScope);
|
||||
}
|
||||
}
|
||||
if (isDefined(value) && modelScope.$viewVal !== value) {
|
||||
widget.$emit('$viewChange', value);
|
||||
}
|
||||
}
|
||||
if (isDefined(value) && modelScope.$viewVal !== value) {
|
||||
widget.$emit('$viewChange', value);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
widget.$watch(render);
|
||||
widget.$render = render;
|
||||
|
||||
function render() {
|
||||
var optionGroups = {'':[]}, // Temporary location for the option groups before we render them
|
||||
optionGroupNames = [''],
|
||||
optionGroupName,
|
||||
optionGroup,
|
||||
option,
|
||||
existingParent, existingOptions, existingOption,
|
||||
modelValue = widget.$modelValue,
|
||||
values = valuesFn(modelScope) || [],
|
||||
keys = keyName ? sortedKeys(values) : values,
|
||||
groupLength, length,
|
||||
groupIndex, index,
|
||||
optionScope = inherit(modelScope),
|
||||
selected,
|
||||
selectedSet = false, // nothing is selected yet
|
||||
lastElement,
|
||||
element;
|
||||
|
||||
if (multiple) {
|
||||
selectedSet = new HashMap(modelValue);
|
||||
} else if (modelValue === null || nullOption) {
|
||||
// if we are not multiselect, and we are null then we have to add the nullOption
|
||||
optionGroups[''].push({selected:modelValue === null, id:'', label:''});
|
||||
selectedSet = true;
|
||||
}
|
||||
|
||||
// We now build up the list of options we need (we merge later)
|
||||
for (index = 0; length = keys.length, index < length; index++) {
|
||||
optionScope[valueName] = values[keyName ? optionScope[keyName]=keys[index]:index];
|
||||
optionGroupName = groupByFn(optionScope) || '';
|
||||
if (!(optionGroup = optionGroups[optionGroupName])) {
|
||||
optionGroup = optionGroups[optionGroupName] = [];
|
||||
optionGroupNames.push(optionGroupName);
|
||||
}
|
||||
if (multiple) {
|
||||
selected = selectedSet.remove(valueFn(optionScope)) != undefined;
|
||||
} else {
|
||||
selected = modelValue === valueFn(optionScope);
|
||||
selectedSet = selectedSet || selected; // see if at least one item is selected
|
||||
}
|
||||
optionGroup.push({
|
||||
id: keyName ? keys[index] : index, // either the index into array or key from object
|
||||
label: displayFn(optionScope) || '', // what will be seen by the user
|
||||
selected: selected // determine if we should be selected
|
||||
});
|
||||
}
|
||||
if (!multiple && !selectedSet) {
|
||||
// nothing was selected, we have to insert the undefined item
|
||||
optionGroups[''].unshift({id:'?', label:'', selected:true});
|
||||
}
|
||||
});
|
||||
|
||||
// Now we need to update the list of DOM nodes to match the optionGroups we computed above
|
||||
for (groupIndex = 0, groupLength = optionGroupNames.length;
|
||||
groupIndex < groupLength;
|
||||
groupIndex++) {
|
||||
// current option group name or '' if no group
|
||||
optionGroupName = optionGroupNames[groupIndex];
|
||||
widget.$watch(render);
|
||||
widget.$render = render;
|
||||
|
||||
// list of options for that group. (first item has the parent)
|
||||
optionGroup = optionGroups[optionGroupName];
|
||||
function render() {
|
||||
var optionGroups = {'':[]}, // Temporary location for the option groups before we render them
|
||||
optionGroupNames = [''],
|
||||
optionGroupName,
|
||||
optionGroup,
|
||||
option,
|
||||
existingParent, existingOptions, existingOption,
|
||||
modelValue = widget.$modelValue,
|
||||
values = valuesFn(modelScope) || [],
|
||||
keys = keyName ? sortedKeys(values) : values,
|
||||
groupLength, length,
|
||||
groupIndex, index,
|
||||
optionScope = inherit(modelScope),
|
||||
selected,
|
||||
selectedSet = false, // nothing is selected yet
|
||||
lastElement,
|
||||
element;
|
||||
|
||||
if (optionGroupsCache.length <= groupIndex) {
|
||||
// we need to grow the optionGroups
|
||||
existingParent = {
|
||||
element: optGroupTemplate.clone().attr('label', optionGroupName),
|
||||
label: optionGroup.label
|
||||
};
|
||||
existingOptions = [existingParent];
|
||||
optionGroupsCache.push(existingOptions);
|
||||
selectElement.append(existingParent.element);
|
||||
} else {
|
||||
existingOptions = optionGroupsCache[groupIndex];
|
||||
existingParent = existingOptions[0]; // either SELECT (no group) or OPTGROUP element
|
||||
|
||||
// update the OPTGROUP label if not the same.
|
||||
if (existingParent.label != optionGroupName) {
|
||||
existingParent.element.attr('label', existingParent.label = optionGroupName);
|
||||
}
|
||||
if (multiple) {
|
||||
selectedSet = new HashMap(modelValue);
|
||||
} else if (modelValue === null || nullOption) {
|
||||
// if we are not multiselect, and we are null then we have to add the nullOption
|
||||
optionGroups[''].push({selected:modelValue === null, id:'', label:''});
|
||||
selectedSet = true;
|
||||
}
|
||||
|
||||
lastElement = null; // start at the begining
|
||||
for(index = 0, length = optionGroup.length; index < length; index++) {
|
||||
option = optionGroup[index];
|
||||
if ((existingOption = existingOptions[index+1])) {
|
||||
// reuse elements
|
||||
lastElement = existingOption.element;
|
||||
if (existingOption.label !== option.label) {
|
||||
lastElement.text(existingOption.label = option.label);
|
||||
}
|
||||
if (existingOption.id !== option.id) {
|
||||
lastElement.val(existingOption.id = option.id);
|
||||
}
|
||||
if (existingOption.element.selected !== option.selected) {
|
||||
lastElement.prop('selected', (existingOption.selected = option.selected));
|
||||
}
|
||||
// We now build up the list of options we need (we merge later)
|
||||
for (index = 0; length = keys.length, index < length; index++) {
|
||||
optionScope[valueName] = values[keyName ? optionScope[keyName]=keys[index]:index];
|
||||
optionGroupName = groupByFn(optionScope) || '';
|
||||
if (!(optionGroup = optionGroups[optionGroupName])) {
|
||||
optionGroup = optionGroups[optionGroupName] = [];
|
||||
optionGroupNames.push(optionGroupName);
|
||||
}
|
||||
if (multiple) {
|
||||
selected = selectedSet.remove(valueFn(optionScope)) != undefined;
|
||||
} else {
|
||||
// grow elements
|
||||
selected = modelValue === valueFn(optionScope);
|
||||
selectedSet = selectedSet || selected; // see if at least one item is selected
|
||||
}
|
||||
optionGroup.push({
|
||||
id: keyName ? keys[index] : index, // either the index into array or key from object
|
||||
label: displayFn(optionScope) || '', // what will be seen by the user
|
||||
selected: selected // determine if we should be selected
|
||||
});
|
||||
}
|
||||
if (!multiple && !selectedSet) {
|
||||
// nothing was selected, we have to insert the undefined item
|
||||
optionGroups[''].unshift({id:'?', label:'', selected:true});
|
||||
}
|
||||
|
||||
// if it's a null option
|
||||
if (option.id === '' && nullOption) {
|
||||
// put back the pre-compiled element
|
||||
element = nullOption;
|
||||
} else {
|
||||
// jQuery(v1.4.2) Bug: We should be able to chain the method calls, but
|
||||
// in this version of jQuery on some browser the .text() returns a string
|
||||
// rather then the element.
|
||||
(element = optionTemplate.clone())
|
||||
.val(option.id)
|
||||
.attr('selected', option.selected)
|
||||
.text(option.label);
|
||||
}
|
||||
// Now we need to update the list of DOM nodes to match the optionGroups we computed above
|
||||
for (groupIndex = 0, groupLength = optionGroupNames.length;
|
||||
groupIndex < groupLength;
|
||||
groupIndex++) {
|
||||
// current option group name or '' if no group
|
||||
optionGroupName = optionGroupNames[groupIndex];
|
||||
|
||||
existingOptions.push(existingOption = {
|
||||
element: element,
|
||||
label: option.label,
|
||||
id: option.id,
|
||||
selected: option.selected
|
||||
});
|
||||
if (lastElement) {
|
||||
lastElement.after(element);
|
||||
} else {
|
||||
existingParent.element.append(element);
|
||||
// list of options for that group. (first item has the parent)
|
||||
optionGroup = optionGroups[optionGroupName];
|
||||
|
||||
if (optionGroupsCache.length <= groupIndex) {
|
||||
// we need to grow the optionGroups
|
||||
existingParent = {
|
||||
element: optGroupTemplate.clone().attr('label', optionGroupName),
|
||||
label: optionGroup.label
|
||||
};
|
||||
existingOptions = [existingParent];
|
||||
optionGroupsCache.push(existingOptions);
|
||||
selectElement.append(existingParent.element);
|
||||
} else {
|
||||
existingOptions = optionGroupsCache[groupIndex];
|
||||
existingParent = existingOptions[0]; // either SELECT (no group) or OPTGROUP element
|
||||
|
||||
// update the OPTGROUP label if not the same.
|
||||
if (existingParent.label != optionGroupName) {
|
||||
existingParent.element.attr('label', existingParent.label = optionGroupName);
|
||||
}
|
||||
lastElement = element;
|
||||
}
|
||||
|
||||
lastElement = null; // start at the begining
|
||||
for(index = 0, length = optionGroup.length; index < length; index++) {
|
||||
option = optionGroup[index];
|
||||
if ((existingOption = existingOptions[index+1])) {
|
||||
// reuse elements
|
||||
lastElement = existingOption.element;
|
||||
if (existingOption.label !== option.label) {
|
||||
lastElement.text(existingOption.label = option.label);
|
||||
}
|
||||
if (existingOption.id !== option.id) {
|
||||
lastElement.val(existingOption.id = option.id);
|
||||
}
|
||||
if (existingOption.element.selected !== option.selected) {
|
||||
lastElement.prop('selected', (existingOption.selected = option.selected));
|
||||
}
|
||||
} else {
|
||||
// grow elements
|
||||
|
||||
// if it's a null option
|
||||
if (option.id === '' && nullOption) {
|
||||
// put back the pre-compiled element
|
||||
element = nullOption;
|
||||
} else {
|
||||
// jQuery(v1.4.2) Bug: We should be able to chain the method calls, but
|
||||
// in this version of jQuery on some browser the .text() returns a string
|
||||
// rather then the element.
|
||||
(element = optionTemplate.clone())
|
||||
.val(option.id)
|
||||
.attr('selected', option.selected)
|
||||
.text(option.label);
|
||||
}
|
||||
|
||||
existingOptions.push(existingOption = {
|
||||
element: element,
|
||||
label: option.label,
|
||||
id: option.id,
|
||||
selected: option.selected
|
||||
});
|
||||
if (lastElement) {
|
||||
lastElement.after(element);
|
||||
} else {
|
||||
existingParent.element.append(element);
|
||||
}
|
||||
lastElement = element;
|
||||
}
|
||||
}
|
||||
// remove any excessive OPTIONs in a group
|
||||
index++; // increment since the existingOptions[0] is parent element not OPTION
|
||||
while(existingOptions.length > index) {
|
||||
existingOptions.pop().element.remove();
|
||||
}
|
||||
}
|
||||
// remove any excessive OPTIONs in a group
|
||||
index++; // increment since the existingOptions[0] is parent element not OPTION
|
||||
while(existingOptions.length > index) {
|
||||
existingOptions.pop().element.remove();
|
||||
// remove any excessive OPTGROUPs from select
|
||||
while(optionGroupsCache.length > groupIndex) {
|
||||
optionGroupsCache.pop()[0].element.remove();
|
||||
}
|
||||
}
|
||||
// remove any excessive OPTGROUPs from select
|
||||
while(optionGroupsCache.length > groupIndex) {
|
||||
optionGroupsCache.pop()[0].element.remove();
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
}];
|
||||
});
|
||||
}
|
||||
}];
|
||||
|
||||
var optionDirective = ['$interpolate', function($interpolate) {
|
||||
return {
|
||||
priority: 100,
|
||||
compile: function(element, attr) {
|
||||
if (isUndefined(attr.value)) {
|
||||
var interpolateFn = $interpolate(element.text(), true);
|
||||
if (interpolateFn) {
|
||||
return function (scope, element, attr) {
|
||||
scope.$watch(interpolateFn, function(value) {
|
||||
attr.$set('value', value);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
attr.$set('value', element.text());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
|
|
|||
579
src/widgets.js
579
src/widgets.js
|
|
@ -67,7 +67,7 @@
|
|||
</select>
|
||||
url of the template: <tt><a href="{{template.url}}">{{template.url}}</a></tt>
|
||||
<hr/>
|
||||
<ng:include src="template.url"></ng:include>
|
||||
<div class="ng-include" src="template.url"></div>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
|
|
@ -87,64 +87,62 @@
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularWidget('ng:include', function(element){
|
||||
var compiler = this,
|
||||
srcExp = element.attr("src"),
|
||||
scopeExp = element.attr("scope") || '',
|
||||
onloadExp = element[0].getAttribute('onload') || '', //workaround for jquery bug #7537
|
||||
autoScrollExp = element.attr('autoscroll');
|
||||
var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile',
|
||||
function($http, $templateCache, $anchorScroll, $compile) {
|
||||
return {
|
||||
compile: function(element, attr) {
|
||||
var srcExp = attr.src,
|
||||
scopeExp = attr.scope || '',
|
||||
onloadExp = attr.onload || '', //workaround for jquery bug #7537
|
||||
autoScrollExp = attr.autoscroll;
|
||||
if (!element[0]['ng:compiled']) {
|
||||
element[0]['ng:compiled'] = true;
|
||||
return function(scope, element, attr){
|
||||
var changeCounter = 0,
|
||||
childScope;
|
||||
|
||||
if (element[0]['ng:compiled']) {
|
||||
this.descend(true);
|
||||
this.directives(true);
|
||||
} else {
|
||||
element[0]['ng:compiled'] = true;
|
||||
return ['$http', '$templateCache', '$anchorScroll', '$element',
|
||||
function($http, $templateCache, $anchorScroll, element) {
|
||||
var scope = this,
|
||||
changeCounter = 0,
|
||||
childScope;
|
||||
function incrementChange() { changeCounter++;}
|
||||
scope.$watch(srcExp, incrementChange);
|
||||
scope.$watch(function() {
|
||||
var includeScope = scope.$eval(scopeExp);
|
||||
if (includeScope) return includeScope.$id;
|
||||
}, incrementChange);
|
||||
scope.$watch(function() {return changeCounter;}, function(newChangeCounter) {
|
||||
var src = scope.$eval(srcExp),
|
||||
useScope = scope.$eval(scopeExp);
|
||||
|
||||
function incrementChange() { changeCounter++;}
|
||||
this.$watch(srcExp, incrementChange);
|
||||
this.$watch(function() {
|
||||
var includeScope = scope.$eval(scopeExp);
|
||||
if (includeScope) return includeScope.$id;
|
||||
}, incrementChange);
|
||||
this.$watch(function() {return changeCounter;}, function(newChangeCounter) {
|
||||
var src = scope.$eval(srcExp),
|
||||
useScope = scope.$eval(scopeExp);
|
||||
|
||||
function clearContent() {
|
||||
// if this callback is still desired
|
||||
if (newChangeCounter === changeCounter) {
|
||||
if (childScope) childScope.$destroy();
|
||||
childScope = null;
|
||||
element.html('');
|
||||
}
|
||||
}
|
||||
|
||||
if (src) {
|
||||
$http.get(src, {cache: $templateCache}).success(function(response) {
|
||||
// if this callback is still desired
|
||||
if (newChangeCounter === changeCounter) {
|
||||
element.html(response);
|
||||
if (childScope) childScope.$destroy();
|
||||
childScope = useScope ? useScope : scope.$new();
|
||||
compiler.compile(element)(childScope);
|
||||
if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
|
||||
$anchorScroll();
|
||||
function clearContent() {
|
||||
// if this callback is still desired
|
||||
if (newChangeCounter === changeCounter) {
|
||||
if (childScope) childScope.$destroy();
|
||||
childScope = null;
|
||||
element.html('');
|
||||
}
|
||||
scope.$eval(onloadExp);
|
||||
}
|
||||
}).error(clearContent);
|
||||
} else {
|
||||
clearContent();
|
||||
}
|
||||
});
|
||||
}];
|
||||
|
||||
if (src) {
|
||||
$http.get(src, {cache: $templateCache}).success(function(response) {
|
||||
// if this callback is still desired
|
||||
if (newChangeCounter === changeCounter) {
|
||||
element.html(response);
|
||||
if (childScope) childScope.$destroy();
|
||||
childScope = useScope ? useScope : scope.$new();
|
||||
$compile(element)(childScope);
|
||||
if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
|
||||
$anchorScroll();
|
||||
}
|
||||
scope.$eval(onloadExp);
|
||||
}
|
||||
}).error(clearContent);
|
||||
} else {
|
||||
clearContent();
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}];
|
||||
|
||||
/**
|
||||
* @ngdoc widget
|
||||
|
|
@ -203,58 +201,62 @@ angularWidget('ng:include', function(element){
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularWidget('ng:switch', function(element) {
|
||||
var compiler = this,
|
||||
watchExpr = element.attr("on"),
|
||||
changeExpr = element.attr('change'),
|
||||
casesTemplate = {},
|
||||
defaultCaseTemplate,
|
||||
children = element.children(),
|
||||
length = children.length,
|
||||
child,
|
||||
when;
|
||||
var ngSwitchDirective = ['$compile', function($compile){
|
||||
return {
|
||||
compile: function(element, attr) {
|
||||
var watchExpr = attr.on,
|
||||
changeExpr = attr.change,
|
||||
casesTemplate = {},
|
||||
defaultCaseTemplate,
|
||||
children = element.children(),
|
||||
length = children.length,
|
||||
child,
|
||||
when;
|
||||
|
||||
if (!watchExpr) throw new Error("Missing 'on' attribute.");
|
||||
while(length--) {
|
||||
child = jqLite(children[length]);
|
||||
// this needs to be here for IE
|
||||
child.remove();
|
||||
when = child.attr('ng:switch-when');
|
||||
if (isString(when)) {
|
||||
casesTemplate[when] = compiler.compile(child);
|
||||
} else if (isString(child.attr('ng:switch-default'))) {
|
||||
defaultCaseTemplate = compiler.compile(child);
|
||||
}
|
||||
}
|
||||
children = null; // release memory;
|
||||
element.html('');
|
||||
|
||||
return function(element){
|
||||
var changeCounter = 0;
|
||||
var childScope;
|
||||
var selectedTemplate;
|
||||
var scope = this;
|
||||
|
||||
this.$watch(watchExpr, function(value) {
|
||||
element.html('');
|
||||
if ((selectedTemplate = casesTemplate[value] || defaultCaseTemplate)) {
|
||||
changeCounter++;
|
||||
if (childScope) childScope.$destroy();
|
||||
childScope = scope.$new();
|
||||
childScope.$eval(changeExpr);
|
||||
if (!watchExpr) throw new Error("Missing 'on' attribute.");
|
||||
while(length--) {
|
||||
child = jqLite(children[length]);
|
||||
// this needs to be here for IE
|
||||
child.remove();
|
||||
// TODO(misko): this attr reading is not normilized
|
||||
when = child.attr('ng:switch-when');
|
||||
if (isString(when)) {
|
||||
casesTemplate[when] = $compile(child);
|
||||
// TODO(misko): this attr reading is not normilized
|
||||
} else if (isString(child.attr('ng:switch-default'))) {
|
||||
defaultCaseTemplate = $compile(child);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.$watch(function() {return changeCounter;}, function() {
|
||||
children = null; // release memory;
|
||||
element.html('');
|
||||
if (selectedTemplate) {
|
||||
selectedTemplate(childScope, function(caseElement) {
|
||||
element.append(caseElement);
|
||||
|
||||
return function(scope, element, attr){
|
||||
var changeCounter = 0;
|
||||
var childScope;
|
||||
var selectedTemplate;
|
||||
|
||||
scope.$watch(watchExpr, function(value) {
|
||||
element.html('');
|
||||
if ((selectedTemplate = casesTemplate[value] || defaultCaseTemplate)) {
|
||||
changeCounter++;
|
||||
if (childScope) childScope.$destroy();
|
||||
childScope = scope.$new();
|
||||
childScope.$eval(changeExpr);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
scope.$watch(function() {return changeCounter;}, function() {
|
||||
element.html('');
|
||||
if (selectedTemplate) {
|
||||
selectedTemplate(childScope, function(caseElement) {
|
||||
element.append(caseElement);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
||||
}];
|
||||
|
||||
|
||||
/*
|
||||
|
|
@ -265,25 +267,24 @@ angularWidget('ng:switch', function(element) {
|
|||
* changing the location or causing page reloads, e.g.:
|
||||
* <a href="" ng:click="model.$save()">Save</a>
|
||||
*/
|
||||
angularWidget('a', function() {
|
||||
this.descend(true);
|
||||
this.directives(true);
|
||||
|
||||
return function(element) {
|
||||
var hasNgHref = ((element.attr('ng:bind-attr') || '').indexOf('"href":') !== -1);
|
||||
|
||||
var htmlAnchorDirective = valueFn({
|
||||
restrict: 'E',
|
||||
compile: function(element, attr) {
|
||||
// turn <a href ng:click="..">link</a> into a link in IE
|
||||
// but only if it doesn't have name attribute, in which case it's an anchor
|
||||
if (!hasNgHref && !element.attr('name') && !element.attr('href')) {
|
||||
element.attr('href', '');
|
||||
if (!attr.href) {
|
||||
attr.$set('href', '');
|
||||
}
|
||||
|
||||
if (element.attr('href') === '' && !hasNgHref) {
|
||||
return function(scope, element) {
|
||||
element.bind('click', function(event){
|
||||
event.preventDefault();
|
||||
// if we have no href url, then don't navigate anywhere.
|
||||
if (!element.attr('href')) {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -344,125 +345,131 @@ angularWidget('a', function() {
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularWidget('@ng:repeat', function(expression, element){
|
||||
element.removeAttr('ng:repeat');
|
||||
element.replaceWith(jqLite('<!-- ng:repeat: ' + expression + ' -->'));
|
||||
var linker = this.compile(element);
|
||||
return function(iterStartElement){
|
||||
var match = expression.match(/^\s*(.+)\s+in\s+(.*)\s*$/),
|
||||
lhs, rhs, valueIdent, keyIdent;
|
||||
if (! match) {
|
||||
throw Error("Expected ng:repeat in form of '_item_ in _collection_' but got '" +
|
||||
expression + "'.");
|
||||
}
|
||||
lhs = match[1];
|
||||
rhs = match[2];
|
||||
match = lhs.match(/^([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\)$/);
|
||||
if (!match) {
|
||||
throw Error("'item' in 'item in collection' should be identifier or (key, value) but got '" +
|
||||
keyValue + "'.");
|
||||
}
|
||||
valueIdent = match[3] || match[1];
|
||||
keyIdent = match[2];
|
||||
|
||||
var parentScope = this;
|
||||
// Store a list of elements from previous run. This is a hash where key is the item from the
|
||||
// iterator, and the value is an array of objects with following properties.
|
||||
// - scope: bound scope
|
||||
// - element: previous element.
|
||||
// - index: position
|
||||
// We need an array of these objects since the same object can be returned from the iterator.
|
||||
// We expect this to be a rare case.
|
||||
var lastOrder = new HashQueueMap();
|
||||
this.$watch(function(scope){
|
||||
var index, length,
|
||||
collection = scope.$eval(rhs),
|
||||
collectionLength = size(collection, true),
|
||||
childScope,
|
||||
// Same as lastOrder but it has the current state. It will become the
|
||||
// lastOrder on the next iteration.
|
||||
nextOrder = new HashQueueMap(),
|
||||
key, value, // key/value of iteration
|
||||
array, last, // last object information {scope, element, index}
|
||||
cursor = iterStartElement; // current position of the node
|
||||
|
||||
if (!isArray(collection)) {
|
||||
// if object, extract keys, sort them and use to determine order of iteration over obj props
|
||||
array = [];
|
||||
for(key in collection) {
|
||||
if (collection.hasOwnProperty(key) && key.charAt(0) != '$') {
|
||||
array.push(key);
|
||||
}
|
||||
var ngRepeatDirective = ['$compile', function($compile) {
|
||||
return {
|
||||
priority: 1000,
|
||||
terminal: true,
|
||||
compile: function(element, attr) {
|
||||
var expression = attr.ngRepeat;
|
||||
attr.$set(attr.$attr.ngRepeat);
|
||||
element.replaceWith(jqLite('<!-- ng:repeat: ' + expression + ' -->'));
|
||||
var linker = $compile(element);
|
||||
return function(scope, iterStartElement, attr){
|
||||
var match = expression.match(/^\s*(.+)\s+in\s+(.*)\s*$/),
|
||||
lhs, rhs, valueIdent, keyIdent;
|
||||
if (! match) {
|
||||
throw Error("Expected ng:repeat in form of '_item_ in _collection_' but got '" +
|
||||
expression + "'.");
|
||||
}
|
||||
array.sort();
|
||||
} else {
|
||||
array = collection || [];
|
||||
}
|
||||
lhs = match[1];
|
||||
rhs = match[2];
|
||||
match = lhs.match(/^([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\)$/);
|
||||
if (!match) {
|
||||
throw Error("'item' in 'item in collection' should be identifier or (key, value) but got '" +
|
||||
keyValue + "'.");
|
||||
}
|
||||
valueIdent = match[3] || match[1];
|
||||
keyIdent = match[2];
|
||||
|
||||
// we are not using forEach for perf reasons (trying to avoid #call)
|
||||
for (index = 0, length = array.length; index < length; index++) {
|
||||
key = (collection === array) ? index : array[index];
|
||||
value = collection[key];
|
||||
last = lastOrder.shift(value);
|
||||
if (last) {
|
||||
// if we have already seen this object, then we need to reuse the
|
||||
// associated scope/element
|
||||
childScope = last.scope;
|
||||
nextOrder.push(value, last);
|
||||
// Store a list of elements from previous run. This is a hash where key is the item from the
|
||||
// iterator, and the value is an array of objects with following properties.
|
||||
// - scope: bound scope
|
||||
// - element: previous element.
|
||||
// - index: position
|
||||
// We need an array of these objects since the same object can be returned from the iterator.
|
||||
// We expect this to be a rare case.
|
||||
var lastOrder = new HashQueueMap();
|
||||
scope.$watch(function(scope){
|
||||
var index, length,
|
||||
collection = scope.$eval(rhs),
|
||||
collectionLength = size(collection, true),
|
||||
childScope,
|
||||
// Same as lastOrder but it has the current state. It will become the
|
||||
// lastOrder on the next iteration.
|
||||
nextOrder = new HashQueueMap(),
|
||||
key, value, // key/value of iteration
|
||||
array, last, // last object information {scope, element, index}
|
||||
cursor = iterStartElement; // current position of the node
|
||||
|
||||
if (index === last.index) {
|
||||
// do nothing
|
||||
cursor = last.element;
|
||||
if (!isArray(collection)) {
|
||||
// if object, extract keys, sort them and use to determine order of iteration over obj props
|
||||
array = [];
|
||||
for(key in collection) {
|
||||
if (collection.hasOwnProperty(key) && key.charAt(0) != '$') {
|
||||
array.push(key);
|
||||
}
|
||||
}
|
||||
array.sort();
|
||||
} else {
|
||||
// existing item which got moved
|
||||
last.index = index;
|
||||
// This may be a noop, if the element is next, but I don't know of a good way to
|
||||
// figure this out, since it would require extra DOM access, so let's just hope that
|
||||
// the browsers realizes that it is noop, and treats it as such.
|
||||
cursor.after(last.element);
|
||||
cursor = last.element;
|
||||
array = collection || [];
|
||||
}
|
||||
} else {
|
||||
// new item which we don't know about
|
||||
childScope = parentScope.$new();
|
||||
}
|
||||
|
||||
childScope[valueIdent] = value;
|
||||
if (keyIdent) childScope[keyIdent] = key;
|
||||
childScope.$index = index;
|
||||
childScope.$position = index === 0 ?
|
||||
'first' :
|
||||
(index == collectionLength - 1 ? 'last' : 'middle');
|
||||
// we are not using forEach for perf reasons (trying to avoid #call)
|
||||
for (index = 0, length = array.length; index < length; index++) {
|
||||
key = (collection === array) ? index : array[index];
|
||||
value = collection[key];
|
||||
last = lastOrder.shift(value);
|
||||
if (last) {
|
||||
// if we have already seen this object, then we need to reuse the
|
||||
// associated scope/element
|
||||
childScope = last.scope;
|
||||
nextOrder.push(value, last);
|
||||
|
||||
if (!last) {
|
||||
linker(childScope, function(clone){
|
||||
cursor.after(clone);
|
||||
last = {
|
||||
scope: childScope,
|
||||
element: (cursor = clone),
|
||||
index: index
|
||||
};
|
||||
nextOrder.push(value, last);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (index === last.index) {
|
||||
// do nothing
|
||||
cursor = last.element;
|
||||
} else {
|
||||
// existing item which got moved
|
||||
last.index = index;
|
||||
// This may be a noop, if the element is next, but I don't know of a good way to
|
||||
// figure this out, since it would require extra DOM access, so let's just hope that
|
||||
// the browsers realizes that it is noop, and treats it as such.
|
||||
cursor.after(last.element);
|
||||
cursor = last.element;
|
||||
}
|
||||
} else {
|
||||
// new item which we don't know about
|
||||
childScope = scope.$new();
|
||||
}
|
||||
|
||||
//shrink children
|
||||
for (key in lastOrder) {
|
||||
if (lastOrder.hasOwnProperty(key)) {
|
||||
array = lastOrder[key];
|
||||
while(array.length) {
|
||||
value = array.pop();
|
||||
value.element.remove();
|
||||
value.scope.$destroy();
|
||||
childScope[valueIdent] = value;
|
||||
if (keyIdent) childScope[keyIdent] = key;
|
||||
childScope.$index = index;
|
||||
childScope.$position = index === 0 ?
|
||||
'first' :
|
||||
(index == collectionLength - 1 ? 'last' : 'middle');
|
||||
|
||||
if (!last) {
|
||||
linker(childScope, function(clone){
|
||||
cursor.after(clone);
|
||||
last = {
|
||||
scope: childScope,
|
||||
element: (cursor = clone),
|
||||
index: index
|
||||
};
|
||||
nextOrder.push(value, last);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastOrder = nextOrder;
|
||||
});
|
||||
//shrink children
|
||||
for (key in lastOrder) {
|
||||
if (lastOrder.hasOwnProperty(key)) {
|
||||
array = lastOrder[key];
|
||||
while(array.length) {
|
||||
value = array.pop();
|
||||
value.element.remove();
|
||||
value.scope.$destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastOrder = nextOrder;
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
||||
}];
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -496,7 +503,7 @@ angularWidget('@ng:repeat', function(expression, element){
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularWidget("@ng:non-bindable", noop);
|
||||
var ngNonBindableDirective = valueFn({ terminal: true });
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -564,49 +571,48 @@ angularWidget("@ng:non-bindable", noop);
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularWidget('ng:view', function(element) {
|
||||
var compiler = this;
|
||||
var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$compile',
|
||||
function($http, $templateCache, $route, $anchorScroll, $compile) {
|
||||
return {
|
||||
compile: function(element, attr) {
|
||||
if (!element[0]['ng:compiled']) {
|
||||
element[0]['ng:compiled'] = true;
|
||||
|
||||
if (!element[0]['ng:compiled']) {
|
||||
element[0]['ng:compiled'] = true;
|
||||
return ['$http', '$templateCache', '$route', '$anchorScroll', '$element',
|
||||
function($http, $templateCache, $route, $anchorScroll, element) {
|
||||
var template;
|
||||
var changeCounter = 0;
|
||||
return function(scope, element, attrs) {
|
||||
var changeCounter = 0;
|
||||
|
||||
this.$on('$afterRouteChange', function() {
|
||||
changeCounter++;
|
||||
});
|
||||
scope.$on('$afterRouteChange', function() {
|
||||
changeCounter++;
|
||||
});
|
||||
|
||||
this.$watch(function() {return changeCounter;}, function(newChangeCounter) {
|
||||
var template = $route.current && $route.current.template;
|
||||
scope.$watch(function() {return changeCounter;}, function(newChangeCounter) {
|
||||
var template = $route.current && $route.current.template;
|
||||
|
||||
function clearContent() {
|
||||
// ignore callback if another route change occured since
|
||||
if (newChangeCounter == changeCounter) {
|
||||
element.html('');
|
||||
}
|
||||
}
|
||||
|
||||
if (template) {
|
||||
$http.get(template, {cache: $templateCache}).success(function(response) {
|
||||
// ignore callback if another route change occured since
|
||||
if (newChangeCounter == changeCounter) {
|
||||
element.html(response);
|
||||
compiler.compile(element)($route.current.scope);
|
||||
$anchorScroll();
|
||||
function clearContent() {
|
||||
// ignore callback if another route change occured since
|
||||
if (newChangeCounter == changeCounter) {
|
||||
element.html('');
|
||||
}
|
||||
}
|
||||
}).error(clearContent);
|
||||
} else {
|
||||
clearContent();
|
||||
}
|
||||
});
|
||||
}];
|
||||
} else {
|
||||
compiler.descend(true);
|
||||
compiler.directives(true);
|
||||
}
|
||||
});
|
||||
|
||||
if (template) {
|
||||
$http.get(template, {cache: $templateCache}).success(function(response) {
|
||||
// ignore callback if another route change occured since
|
||||
if (newChangeCounter == changeCounter) {
|
||||
element.html(response);
|
||||
$compile(element)($route.current.scope);
|
||||
$anchorScroll();
|
||||
}
|
||||
}).error(clearContent);
|
||||
} else {
|
||||
clearContent();
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}];
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -715,81 +721,80 @@ angularWidget('ng:view', function(element) {
|
|||
|
||||
<!--- Example with simple pluralization rules for en locale --->
|
||||
Without Offset:
|
||||
<ng:pluralize count="personCount"
|
||||
<ng-pluralize count="personCount"
|
||||
when="{'0': 'Nobody is viewing.',
|
||||
'one': '1 person is viewing.',
|
||||
'other': '{} people are viewing.'}">
|
||||
</ng:pluralize><br>
|
||||
</ng-pluralize><br>
|
||||
|
||||
<!--- Example with offset --->
|
||||
With Offset(2):
|
||||
<ng:pluralize count="personCount" offset=2
|
||||
<ng-pluralize count="personCount" offset=2
|
||||
when="{'0': 'Nobody is viewing.',
|
||||
'1': '{{person1}} is viewing.',
|
||||
'2': '{{person1}} and {{person2}} are viewing.',
|
||||
'one': '{{person1}}, {{person2}} and one other person are viewing.',
|
||||
'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
|
||||
</ng:pluralize>
|
||||
</ng-pluralize>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should show correct pluralized string', function() {
|
||||
expect(element('.doc-example-live .ng-pluralize:first').text()).
|
||||
expect(element('.doc-example-live ng-pluralize:first').text()).
|
||||
toBe('1 person is viewing.');
|
||||
expect(element('.doc-example-live .ng-pluralize:last').text()).
|
||||
expect(element('.doc-example-live ng-pluralize:last').text()).
|
||||
toBe('Igor is viewing.');
|
||||
|
||||
using('.doc-example-live').input('personCount').enter('0');
|
||||
expect(element('.doc-example-live .ng-pluralize:first').text()).
|
||||
expect(element('.doc-example-live ng-pluralize:first').text()).
|
||||
toBe('Nobody is viewing.');
|
||||
expect(element('.doc-example-live .ng-pluralize:last').text()).
|
||||
expect(element('.doc-example-live ng-pluralize:last').text()).
|
||||
toBe('Nobody is viewing.');
|
||||
|
||||
using('.doc-example-live').input('personCount').enter('2');
|
||||
expect(element('.doc-example-live .ng-pluralize:first').text()).
|
||||
expect(element('.doc-example-live ng-pluralize:first').text()).
|
||||
toBe('2 people are viewing.');
|
||||
expect(element('.doc-example-live .ng-pluralize:last').text()).
|
||||
expect(element('.doc-example-live ng-pluralize:last').text()).
|
||||
toBe('Igor and Misko are viewing.');
|
||||
|
||||
using('.doc-example-live').input('personCount').enter('3');
|
||||
expect(element('.doc-example-live .ng-pluralize:first').text()).
|
||||
expect(element('.doc-example-live ng-pluralize:first').text()).
|
||||
toBe('3 people are viewing.');
|
||||
expect(element('.doc-example-live .ng-pluralize:last').text()).
|
||||
expect(element('.doc-example-live ng-pluralize:last').text()).
|
||||
toBe('Igor, Misko and one other person are viewing.');
|
||||
|
||||
using('.doc-example-live').input('personCount').enter('4');
|
||||
expect(element('.doc-example-live .ng-pluralize:first').text()).
|
||||
expect(element('.doc-example-live ng-pluralize:first').text()).
|
||||
toBe('4 people are viewing.');
|
||||
expect(element('.doc-example-live .ng-pluralize:last').text()).
|
||||
expect(element('.doc-example-live ng-pluralize:last').text()).
|
||||
toBe('Igor, Misko and 2 other people are viewing.');
|
||||
});
|
||||
|
||||
it('should show data-binded names', function() {
|
||||
using('.doc-example-live').input('personCount').enter('4');
|
||||
expect(element('.doc-example-live .ng-pluralize:last').text()).
|
||||
expect(element('.doc-example-live ng-pluralize:last').text()).
|
||||
toBe('Igor, Misko and 2 other people are viewing.');
|
||||
|
||||
using('.doc-example-live').input('person1').enter('Di');
|
||||
using('.doc-example-live').input('person2').enter('Vojta');
|
||||
expect(element('.doc-example-live .ng-pluralize:last').text()).
|
||||
expect(element('.doc-example-live ng-pluralize:last').text()).
|
||||
toBe('Di, Vojta and 2 other people are viewing.');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularWidget('ng:pluralize', function(element) {
|
||||
var numberExp = element.attr('count'),
|
||||
whenExp = element.attr('when'),
|
||||
offset = element.attr('offset') || 0;
|
||||
|
||||
return ['$locale', '$element', function($locale, element) {
|
||||
var scope = this,
|
||||
var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) {
|
||||
var BRACE = /{}/g;
|
||||
return function(scope, element, attr) {
|
||||
var numberExp = attr.count,
|
||||
whenExp = attr.when,
|
||||
offset = attr.offset || 0,
|
||||
whens = scope.$eval(whenExp),
|
||||
whensExpFns = {};
|
||||
|
||||
forEach(whens, function(expression, key) {
|
||||
whensExpFns[key] = compileBindTemplate(expression.replace(/{}/g,
|
||||
'{{' + numberExp + '-' + offset + '}}'));
|
||||
whensExpFns[key] =
|
||||
$interpolate(expression.replace(BRACE, '{{' + numberExp + '-' + offset + '}}'));
|
||||
});
|
||||
|
||||
scope.$watch(function() {
|
||||
|
|
@ -806,5 +811,5 @@ angularWidget('ng:pluralize', function(element) {
|
|||
}, function(newVal) {
|
||||
element.text(newVal);
|
||||
});
|
||||
}];
|
||||
});
|
||||
};
|
||||
}];
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
'use strict';
|
||||
|
||||
describe('angular', function() {
|
||||
var element;
|
||||
|
||||
afterEach(function(){
|
||||
dealoc(element);
|
||||
});
|
||||
|
||||
describe('case', function() {
|
||||
it('should change case', function() {
|
||||
expect(lowercase('ABC90')).toEqual('abc90');
|
||||
|
|
@ -382,28 +388,6 @@ describe('angular', function() {
|
|||
});
|
||||
|
||||
|
||||
describe('directive', function() {
|
||||
it('should register directives with case-insensitive id', inject(function($compile) {
|
||||
angularDirective('ALLCAPS', function(val, el) {el.text('+' + val + '+')});
|
||||
angularDirective('lowercase', function(val, el) {el.text('-' + val + '-')});
|
||||
|
||||
var el = jqLite('<div>' +
|
||||
'<span allcaps="xx1"></span>' +
|
||||
'<span ALLcaps="xx2"></span>' +
|
||||
'<span ALLCAPS="xx3"></span>' +
|
||||
'<span lowerCASE="XX4">xx4</span>' +
|
||||
'</div>');
|
||||
$compile(el);
|
||||
expect(lowercase(sortedHtml(el))).toBe('<div>' +
|
||||
'<span allcaps="xx1">+xx1+</span>' +
|
||||
'<span allcaps="xx2">+xx2+</span>' +
|
||||
'<span allcaps="xx3">+xx3+</span>' +
|
||||
'<span lowercase="xx4">-xx4-</span>' +
|
||||
'</div>');
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
describe('isDate', function() {
|
||||
it('should return true for Date object', function() {
|
||||
expect(isDate(new Date())).toBe(true);
|
||||
|
|
@ -420,7 +404,7 @@ describe('angular', function() {
|
|||
describe('compile', function() {
|
||||
it('should link to existing node and create scope', inject(function($rootScope, $compile) {
|
||||
var template = angular.element('<div>{{greeting = "hello world"}}</div>');
|
||||
$compile(template)($rootScope);
|
||||
element = $compile(template)($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(template.text()).toEqual('hello world');
|
||||
expect($rootScope.greeting).toEqual('hello world');
|
||||
|
|
@ -428,7 +412,7 @@ describe('angular', function() {
|
|||
|
||||
it('should link to existing node and given scope', inject(function($rootScope, $compile) {
|
||||
var template = angular.element('<div>{{greeting = "hello world"}}</div>');
|
||||
$compile(template)($rootScope);
|
||||
element = $compile(template)($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(template.text()).toEqual('hello world');
|
||||
}));
|
||||
|
|
@ -436,15 +420,15 @@ describe('angular', function() {
|
|||
it('should link to new node and given scope', inject(function($rootScope, $compile) {
|
||||
var template = jqLite('<div>{{greeting = "hello world"}}</div>');
|
||||
|
||||
var templateFn = $compile(template);
|
||||
var compile = $compile(template);
|
||||
var templateClone = template.clone();
|
||||
|
||||
var element = templateFn($rootScope, function(clone){
|
||||
element = compile($rootScope, function(clone){
|
||||
templateClone = clone;
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(template.text()).toEqual('');
|
||||
expect(template.text()).toEqual('{{greeting = "hello world"}}');
|
||||
expect(element.text()).toEqual('hello world');
|
||||
expect(element).toEqual(templateClone);
|
||||
expect($rootScope.greeting).toEqual('hello world');
|
||||
|
|
@ -452,9 +436,9 @@ describe('angular', function() {
|
|||
|
||||
it('should link to cloned node and create scope', inject(function($rootScope, $compile) {
|
||||
var template = jqLite('<div>{{greeting = "hello world"}}</div>');
|
||||
var element = $compile(template)($rootScope, noop);
|
||||
element = $compile(template)($rootScope, noop);
|
||||
$rootScope.$digest();
|
||||
expect(template.text()).toEqual('');
|
||||
expect(template.text()).toEqual('{{greeting = "hello world"}}');
|
||||
expect(element.text()).toEqual('hello world');
|
||||
expect($rootScope.greeting).toEqual('hello world');
|
||||
}));
|
||||
|
|
@ -524,4 +508,11 @@ describe('angular', function() {
|
|||
expect(startingTag('<ng:abc x="2"><div>text</div></ng:abc>')).toEqual('<ng:abc x="2">');
|
||||
});
|
||||
});
|
||||
|
||||
describe('snake_case', function(){
|
||||
it('should convert to snake_case', function() {
|
||||
expect(snake_case('ABC')).toEqual('a_b_c');
|
||||
expect(snake_case('alanBobCharles')).toEqual('alan_bob_charles');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
describe('Binder', function() {
|
||||
|
||||
var element;
|
||||
|
||||
function childNode(element, index) {
|
||||
return jqLite(element[0].childNodes[index]);
|
||||
}
|
||||
|
|
@ -19,9 +21,8 @@ describe('Binder', function() {
|
|||
});
|
||||
|
||||
afterEach(function() {
|
||||
if (this.element && this.element.dealoc) {
|
||||
this.element.dealoc();
|
||||
}
|
||||
dealoc(element);
|
||||
dealoc(this.element);
|
||||
});
|
||||
|
||||
it('BindUpdate', inject(function($rootScope, $compile) {
|
||||
|
|
@ -42,60 +43,18 @@ describe('Binder', function() {
|
|||
}));
|
||||
|
||||
it('ApplyTextBindings', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div ng:bind="model.a">x</div>')($rootScope);
|
||||
element = $compile('<div ng:bind="model.a">x</div>')($rootScope);
|
||||
$rootScope.model = {a:123};
|
||||
$rootScope.$apply();
|
||||
expect(element.text()).toBe('123');
|
||||
}));
|
||||
|
||||
it('ReplaceBindingInTextWithSpan preserve surounding text', function() {
|
||||
expect(this.compileToHtml('<b>a{{b}}c</b>')).toBe('<b>a<span ng:bind="b"></span>c</b>');
|
||||
});
|
||||
|
||||
it('ReplaceBindingInTextWithSpan', function() {
|
||||
expect(this.compileToHtml('<b>{{b}}</b>')).toBe('<b><span ng:bind="b"></span></b>');
|
||||
});
|
||||
|
||||
it('BindingSpaceConfusesIE', inject(function($rootScope, $compile) {
|
||||
if (!msie) return;
|
||||
var span = document.createElement('span');
|
||||
span.innerHTML = ' ';
|
||||
var nbsp = span.firstChild.nodeValue;
|
||||
expect(this.compileToHtml('<b>{{a}} {{b}}</b>')).
|
||||
toBe('<b><span ng:bind="a"></span><span>' + nbsp + '</span><span ng:bind="b"></span></b>');
|
||||
dealoc(($rootScope));
|
||||
expect(this.compileToHtml('<b>{{A}} x {{B}} ({{C}})</b>')).
|
||||
toBe('<b><span ng:bind="A"></span><span>' + nbsp + 'x </span><span ng:bind="B"></span>' +
|
||||
'<span>' + nbsp + '(</span><span ng:bind="C"></span>)</b>');
|
||||
}));
|
||||
|
||||
it('BindingOfAttributes', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<a href="http://s/a{{b}}c" foo="x"></a>')($rootScope);
|
||||
var attrbinding = element.attr('ng:bind-attr');
|
||||
var bindings = fromJson(attrbinding);
|
||||
expect(decodeURI(bindings.href)).toBe('http://s/a{{b}}c');
|
||||
expect(bindings.foo).toBeFalsy();
|
||||
}));
|
||||
|
||||
it('MarkMultipleAttributes', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<a href="http://s/a{{b}}c" foo="{{d}}"></a>')($rootScope);
|
||||
var attrbinding = element.attr('ng:bind-attr');
|
||||
var bindings = fromJson(attrbinding);
|
||||
expect(bindings.foo).toBe('{{d}}');
|
||||
expect(decodeURI(bindings.href)).toBe('http://s/a{{b}}c');
|
||||
}));
|
||||
|
||||
it('AttributesNoneBound', inject(function($rootScope, $compile) {
|
||||
var a = $compile('<a href="abc" foo="def"></a>')($rootScope);
|
||||
expect(a[0].nodeName).toBe('A');
|
||||
expect(a.attr('ng:bind-attr')).toBeFalsy();
|
||||
}));
|
||||
|
||||
it('ExistingAttrbindingIsAppended', inject(function($rootScope, $compile) {
|
||||
var a = $compile('<a href="http://s/{{abc}}" ng:bind-attr="{\'b\':\'{{def}}\'}"></a>')($rootScope);
|
||||
expect(a.attr('ng:bind-attr')).toBe('{"b":"{{def}}","href":"http://s/{{abc}}"}');
|
||||
}));
|
||||
|
||||
it('AttributesAreEvaluated', inject(function($rootScope, $compile) {
|
||||
var a = $compile('<a ng:bind-attr=\'{"a":"a", "b":"a+b={{a+b}}"}\'></a>')($rootScope);
|
||||
$rootScope.$eval('a=1;b=2');
|
||||
|
|
@ -106,7 +65,7 @@ describe('Binder', function() {
|
|||
|
||||
it('InputTypeButtonActionExecutesInScope', inject(function($rootScope, $compile) {
|
||||
var savedCalled = false;
|
||||
var element = $compile(
|
||||
element = $compile(
|
||||
'<input type="button" ng:click="person.save()" value="Apply">')($rootScope);
|
||||
$rootScope.person = {};
|
||||
$rootScope.person.save = function() {
|
||||
|
|
@ -117,8 +76,8 @@ describe('Binder', function() {
|
|||
}));
|
||||
|
||||
it('InputTypeButtonActionExecutesInScope2', inject(function($rootScope, $compile) {
|
||||
var log = '';
|
||||
var element = $compile('<input type="image" ng:click="action()">')($rootScope);
|
||||
var log = "";
|
||||
element = $compile('<input type="image" ng:click="action()">')($rootScope);
|
||||
$rootScope.action = function() {
|
||||
log += 'click;';
|
||||
};
|
||||
|
|
@ -129,7 +88,7 @@ describe('Binder', function() {
|
|||
|
||||
it('ButtonElementActionExecutesInScope', inject(function($rootScope, $compile) {
|
||||
var savedCalled = false;
|
||||
var element = $compile('<button ng:click="person.save()">Apply</button>')($rootScope);
|
||||
element = $compile('<button ng:click="person.save()">Apply</button>')($rootScope);
|
||||
$rootScope.person = {};
|
||||
$rootScope.person.save = function() {
|
||||
savedCalled = true;
|
||||
|
|
@ -179,7 +138,7 @@ describe('Binder', function() {
|
|||
}));
|
||||
|
||||
it('RepeaterContentDoesNotBind', inject(function($rootScope, $compile) {
|
||||
var element = $compile(
|
||||
element = $compile(
|
||||
'<ul>' +
|
||||
'<LI ng:repeat="item in model.items"><span ng:bind="item.a"></span></li>' +
|
||||
'</ul>')($rootScope);
|
||||
|
|
@ -198,8 +157,8 @@ describe('Binder', function() {
|
|||
});
|
||||
|
||||
it('RepeaterAdd', inject(function($rootScope, $compile, $browser) {
|
||||
var element = $compile('<div><input type="text" ng:model="item.x" ng:repeat="item in items"></div>')($rootScope);
|
||||
$rootScope.items = [{x: 'a'}, {x: 'b'}];
|
||||
element = $compile('<div><input type="text" ng:model="item.x" ng:repeat="item in items"></div>')($rootScope);
|
||||
$rootScope.items = [{x:'a'}, {x:'b'}];
|
||||
$rootScope.$apply();
|
||||
var first = childNode(element, 1);
|
||||
var second = childNode(element, 2);
|
||||
|
|
@ -213,7 +172,7 @@ describe('Binder', function() {
|
|||
}));
|
||||
|
||||
it('ItShouldRemoveExtraChildrenWhenIteratingOverHash', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div><div ng:repeat="i in items">{{i}}</div></div>')($rootScope);
|
||||
element = $compile('<div><div ng:repeat="i in items">{{i}}</div></div>')($rootScope);
|
||||
var items = {};
|
||||
$rootScope.items = items;
|
||||
|
||||
|
|
@ -234,7 +193,7 @@ describe('Binder', function() {
|
|||
$exceptionHandlerProvider.mode('log');
|
||||
});
|
||||
inject(function($rootScope, $exceptionHandler, $compile) {
|
||||
$compile('<div>{{error.throw()}}</div>', null, true)($rootScope);
|
||||
element = $compile('<div>{{error.throw()}}</div>', null, true)($rootScope);
|
||||
var errorLogs = $exceptionHandler.errors;
|
||||
|
||||
$rootScope.error = {
|
||||
|
|
@ -277,7 +236,7 @@ describe('Binder', function() {
|
|||
});
|
||||
|
||||
it('NestedRepeater', inject(function($rootScope, $compile) {
|
||||
var element = $compile(
|
||||
element = $compile(
|
||||
'<div>' +
|
||||
'<div ng:repeat="m in model" name="{{m.name}}">' +
|
||||
'<ul name="{{i}}" ng:repeat="i in m.item"></ul>' +
|
||||
|
|
@ -290,21 +249,21 @@ describe('Binder', function() {
|
|||
expect(sortedHtml(element)).toBe(
|
||||
'<div>'+
|
||||
'<#comment></#comment>'+
|
||||
'<div name="a" ng:bind-attr="{"name":"{{m.name}}"}">'+
|
||||
'<div name="a">'+
|
||||
'<#comment></#comment>'+
|
||||
'<ul name="a1" ng:bind-attr="{"name":"{{i}}"}"></ul>'+
|
||||
'<ul name="a2" ng:bind-attr="{"name":"{{i}}"}"></ul>'+
|
||||
'<ul name="a1"></ul>'+
|
||||
'<ul name="a2"></ul>'+
|
||||
'</div>'+
|
||||
'<div name="b" ng:bind-attr="{"name":"{{m.name}}"}">'+
|
||||
'<div name="b">'+
|
||||
'<#comment></#comment>'+
|
||||
'<ul name="b1" ng:bind-attr="{"name":"{{i}}"}"></ul>'+
|
||||
'<ul name="b2" ng:bind-attr="{"name":"{{i}}"}"></ul>'+
|
||||
'<ul name="b1"></ul>'+
|
||||
'<ul name="b2"></ul>'+
|
||||
'</div>' +
|
||||
'</div>');
|
||||
}));
|
||||
|
||||
it('HideBindingExpression', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div ng:hide="hidden == 3"/>')($rootScope);
|
||||
element = $compile('<div ng:hide="hidden == 3"/>')($rootScope);
|
||||
|
||||
$rootScope.hidden = 3;
|
||||
$rootScope.$apply();
|
||||
|
|
@ -318,7 +277,7 @@ describe('Binder', function() {
|
|||
}));
|
||||
|
||||
it('HideBinding', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div ng:hide="hidden"/>')($rootScope);
|
||||
element = $compile('<div ng:hide="hidden"/>')($rootScope);
|
||||
|
||||
$rootScope.hidden = 'true';
|
||||
$rootScope.$apply();
|
||||
|
|
@ -337,7 +296,7 @@ describe('Binder', function() {
|
|||
}));
|
||||
|
||||
it('ShowBinding', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div ng:show="show"/>')($rootScope);
|
||||
element = $compile('<div ng:show="show"/>')($rootScope);
|
||||
|
||||
$rootScope.show = 'true';
|
||||
$rootScope.$apply();
|
||||
|
|
@ -357,7 +316,7 @@ describe('Binder', function() {
|
|||
|
||||
|
||||
it('BindClass', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div ng:class="clazz"/>')($rootScope);
|
||||
element = $compile('<div ng:class="clazz"/>')($rootScope);
|
||||
|
||||
$rootScope.clazz = 'testClass';
|
||||
$rootScope.$apply();
|
||||
|
|
@ -371,7 +330,7 @@ describe('Binder', function() {
|
|||
}));
|
||||
|
||||
it('BindClassEvenOdd', inject(function($rootScope, $compile) {
|
||||
var element = $compile(
|
||||
element = $compile(
|
||||
'<div>' +
|
||||
'<div ng:repeat="i in [0,1]" ng:class-even="\'e\'" ng:class-odd="\'o\'"></div>' +
|
||||
'</div>')($rootScope);
|
||||
|
|
@ -387,7 +346,7 @@ describe('Binder', function() {
|
|||
}));
|
||||
|
||||
it('BindStyle', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div ng:style="style"/>')($rootScope);
|
||||
element = $compile('<div ng:style="style"/>')($rootScope);
|
||||
|
||||
$rootScope.$eval('style={height: "10px"}');
|
||||
$rootScope.$apply();
|
||||
|
|
@ -413,28 +372,29 @@ describe('Binder', function() {
|
|||
});
|
||||
|
||||
it('ShoulIgnoreVbNonBindable', inject(function($rootScope, $compile) {
|
||||
var element = $compile(
|
||||
'<div>{{a}}' +
|
||||
'<div ng:non-bindable>{{a}}</div>' +
|
||||
'<div ng:non-bindable="">{{b}}</div>' +
|
||||
'<div ng:non-bindable="true">{{c}}</div>' +
|
||||
'</div>')($rootScope);
|
||||
element = $compile(
|
||||
"<div>{{a}}" +
|
||||
"<div ng:non-bindable>{{a}}</div>" +
|
||||
"<div ng:non-bindable=''>{{b}}</div>" +
|
||||
"<div ng:non-bindable='true'>{{c}}</div>" +
|
||||
"</div>")($rootScope);
|
||||
$rootScope.a = 123;
|
||||
$rootScope.$apply();
|
||||
expect(element.text()).toBe('123{{a}}{{b}}{{c}}');
|
||||
}));
|
||||
|
||||
it('ShouldTemplateBindPreElements', inject(function ($rootScope, $compile) {
|
||||
var element = $compile('<pre>Hello {{name}}!</pre>')($rootScope);
|
||||
$rootScope.name = 'World';
|
||||
element = $compile('<pre>Hello {{name}}!</pre>')($rootScope);
|
||||
$rootScope.name = "World";
|
||||
$rootScope.$apply();
|
||||
|
||||
expect( sortedHtml(element)).toBe(
|
||||
'<pre ng:bind-template="Hello {{name}}!">Hello World!</pre>');
|
||||
assertEquals(
|
||||
'<pre>Hello World!</pre>',
|
||||
sortedHtml(element));
|
||||
}));
|
||||
|
||||
it('FillInOptionValueWhenMissing', inject(function($rootScope, $compile) {
|
||||
var element = $compile(
|
||||
element = $compile(
|
||||
'<select ng:model="foo">' +
|
||||
'<option selected="true">{{a}}</option>' +
|
||||
'<option value="">{{b}}</option>' +
|
||||
|
|
@ -458,7 +418,7 @@ describe('Binder', function() {
|
|||
}));
|
||||
|
||||
it('DeleteAttributeIfEvaluatesFalse', inject(function($rootScope, $compile) {
|
||||
var element = $compile(
|
||||
element = $compile(
|
||||
'<div>' +
|
||||
'<input ng:model="a0" ng:bind-attr="{disabled:\'{{true}}\'}">' +
|
||||
'<input ng:model="a1" ng:bind-attr="{disabled:\'{{false}}\'}">' +
|
||||
|
|
@ -485,7 +445,7 @@ describe('Binder', function() {
|
|||
$exceptionHandlerProvider.mode('log');
|
||||
});
|
||||
inject(function($rootScope, $exceptionHandler, $log, $compile) {
|
||||
var element = $compile(
|
||||
element = $compile(
|
||||
'<div>' +
|
||||
'<input type="button" ng:click="greeting=\'ABC\'"/>' +
|
||||
'<input type="button" ng:click=":garbage:"/>' +
|
||||
|
|
@ -505,7 +465,7 @@ describe('Binder', function() {
|
|||
});
|
||||
|
||||
it('ItShouldSelectTheCorrectRadioBox', inject(function($rootScope, $compile) {
|
||||
var element = $compile(
|
||||
element = $compile(
|
||||
'<div>' +
|
||||
'<input type="radio" ng:model="sex" value="female">' +
|
||||
'<input type="radio" ng:model="sex" value="male">' +
|
||||
|
|
@ -527,7 +487,7 @@ describe('Binder', function() {
|
|||
}));
|
||||
|
||||
it('ItShouldRepeatOnHashes', inject(function($rootScope, $compile) {
|
||||
var element = $compile(
|
||||
element = $compile(
|
||||
'<ul>' +
|
||||
'<li ng:repeat="(k,v) in {a:0,b:1}" ng:bind=\"k + v\"></li>' +
|
||||
'</ul>')($rootScope);
|
||||
|
|
@ -541,7 +501,7 @@ describe('Binder', function() {
|
|||
}));
|
||||
|
||||
it('ItShouldFireChangeListenersBeforeUpdate', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div ng:bind="name"></div>')($rootScope);
|
||||
element = $compile('<div ng:bind="name"></div>')($rootScope);
|
||||
$rootScope.name = '';
|
||||
$rootScope.$watch('watched', 'name=123');
|
||||
$rootScope.watched = 'change';
|
||||
|
|
@ -551,7 +511,7 @@ describe('Binder', function() {
|
|||
}));
|
||||
|
||||
it('ItShouldHandleMultilineBindings', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div>{{\n 1 \n + \n 2 \n}}</div>')($rootScope);
|
||||
element = $compile('<div>{{\n 1 \n + \n 2 \n}}</div>')($rootScope);
|
||||
$rootScope.$apply();
|
||||
expect(element.text()).toBe('3');
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -1,23 +1,30 @@
|
|||
'use strict';
|
||||
|
||||
describe("ScenarioSpec: Compilation", function() {
|
||||
var element;
|
||||
|
||||
afterEach(function() {
|
||||
dealoc(element);
|
||||
});
|
||||
|
||||
|
||||
describe('compilation', function() {
|
||||
it("should compile dom node and return scope", inject(function($rootScope, $compile) {
|
||||
var node = jqLite('<div ng:init="a=1">{{b=a+1}}</div>')[0];
|
||||
$compile(node)($rootScope);
|
||||
element = $compile(node)($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.a).toEqual(1);
|
||||
expect($rootScope.b).toEqual(2);
|
||||
}));
|
||||
|
||||
it("should compile jQuery node and return scope", inject(function($rootScope, $compile) {
|
||||
var element = $compile(jqLite('<div>{{a=123}}</div>'))($rootScope);
|
||||
element = $compile(jqLite('<div>{{a=123}}</div>'))($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(jqLite(element).text()).toEqual('123');
|
||||
}));
|
||||
|
||||
it("should compile text node and return scope", inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div>{{a=123}}</div>')($rootScope);
|
||||
element = $compile('<div>{{a=123}}</div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(jqLite(element).text()).toEqual('123');
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,16 @@
|
|||
'use strict';
|
||||
|
||||
describe("directive", function() {
|
||||
var element;
|
||||
|
||||
beforeEach(function() {
|
||||
element = null;
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
dealoc(element);
|
||||
});
|
||||
|
||||
|
||||
var $filterProvider, element;
|
||||
|
||||
|
|
@ -19,7 +29,7 @@ describe("directive", function() {
|
|||
|
||||
describe('ng:bind', function() {
|
||||
it('should set text', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div ng:bind="a"></div>')($rootScope);
|
||||
element = $compile('<div ng:bind="a"></div>')($rootScope);
|
||||
expect(element.text()).toEqual('');
|
||||
$rootScope.a = 'misko';
|
||||
$rootScope.$digest();
|
||||
|
|
@ -28,47 +38,40 @@ describe("directive", function() {
|
|||
}));
|
||||
|
||||
it('should set text to blank if undefined', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div ng:bind="a"></div>')($rootScope);
|
||||
element = $compile('<div ng:bind="a"></div>')($rootScope);
|
||||
$rootScope.a = 'misko';
|
||||
$rootScope.$digest();
|
||||
expect(element.text()).toEqual('misko');
|
||||
$rootScope.a = undefined;
|
||||
$rootScope.$digest();
|
||||
expect(element.text()).toEqual('');
|
||||
$rootScope.a = null;
|
||||
$rootScope.$digest();
|
||||
expect(element.text()).toEqual('');
|
||||
}));
|
||||
|
||||
it('should set html', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div ng:bind="html|html"></div>')($rootScope);
|
||||
element = $compile('<div ng:bind-html="html"></div>')($rootScope);
|
||||
$rootScope.html = '<div unknown>hello</div>';
|
||||
$rootScope.$digest();
|
||||
expect(lowercase(element.html())).toEqual('<div>hello</div>');
|
||||
}));
|
||||
|
||||
it('should set unsafe html', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div ng:bind="html|html:\'unsafe\'"></div>')($rootScope);
|
||||
element = $compile('<div ng:bind-html-unsafe="html"></div>')($rootScope);
|
||||
$rootScope.html = '<div onclick="">hello</div>';
|
||||
$rootScope.$digest();
|
||||
expect(lowercase(element.html())).toEqual('<div onclick="">hello</div>');
|
||||
}));
|
||||
|
||||
it('should set element element', inject(function($rootScope, $compile) {
|
||||
$filterProvider.register('myElement', valueFn(function() {
|
||||
return jqLite('<a>hello</a>');
|
||||
}));
|
||||
var element = $compile('<div ng:bind="0|myElement"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(lowercase(element.html())).toEqual('<a>hello</a>');
|
||||
}));
|
||||
|
||||
|
||||
it('should suppress rendering of falsy values', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div>{{ null }}{{ undefined }}{{ "" }}-{{ 0 }}{{ false }}</div>')($rootScope);
|
||||
element = $compile('<div>{{ null }}{{ undefined }}{{ "" }}-{{ 0 }}{{ false }}</div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(element.text()).toEqual('-0false');
|
||||
}));
|
||||
|
||||
it('should render object as JSON ignore $$', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div>{{ {key:"value", $$key:"hide"} }}</div>')($rootScope);
|
||||
element = $compile('<div>{{ {key:"value", $$key:"hide"} }}</div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(fromJson(element.text())).toEqual({key:'value'});
|
||||
}));
|
||||
|
|
@ -76,27 +79,15 @@ describe("directive", function() {
|
|||
|
||||
describe('ng:bind-template', function() {
|
||||
it('should ng:bind-template', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div ng:bind-template="Hello {{name}}!"></div>')($rootScope);
|
||||
element = $compile('<div ng:bind-template="Hello {{name}}!"></div>')($rootScope);
|
||||
$rootScope.name = 'Misko';
|
||||
$rootScope.$digest();
|
||||
expect(element.hasClass('ng-binding')).toEqual(true);
|
||||
expect(element.text()).toEqual('Hello Misko!');
|
||||
}));
|
||||
|
||||
it('should have $element set to current bind element', inject(function($rootScope, $compile) {
|
||||
var innerText;
|
||||
$filterProvider.register('myFilter', valueFn(function(text) {
|
||||
innerText = innerText || this.$element.text();
|
||||
return text;
|
||||
}));
|
||||
var element = $compile('<div>before<span ng:bind-template="{{\'HELLO\'|myFilter}}">INNER</span>after</div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(element.text()).toEqual("beforeHELLOafter");
|
||||
expect(innerText).toEqual('INNER');
|
||||
}));
|
||||
|
||||
it('should render object as JSON ignore $$', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<pre>{{ {key:"value", $$key:"hide"} }}</pre>')($rootScope);
|
||||
element = $compile('<pre>{{ {key:"value", $$key:"hide"} }}</pre>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(fromJson(element.text())).toEqual({key:'value'});
|
||||
}));
|
||||
|
|
@ -105,39 +96,40 @@ describe("directive", function() {
|
|||
|
||||
describe('ng:bind-attr', function() {
|
||||
it('should bind attributes', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div ng:bind-attr="{src:\'http://localhost/mysrc\', alt:\'myalt\'}"/>')($rootScope);
|
||||
element = $compile('<div ng:bind-attr="{src:\'http://localhost/mysrc\', alt:\'myalt\'}"/>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(element.attr('src')).toEqual('http://localhost/mysrc');
|
||||
expect(element.attr('alt')).toEqual('myalt');
|
||||
}));
|
||||
|
||||
it('should not pretty print JSON in attributes', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<img alt="{{ {a:1} }}"/>')($rootScope);
|
||||
element = $compile('<img alt="{{ {a:1} }}"/>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(element.attr('alt')).toEqual('{"a":1}');
|
||||
}));
|
||||
|
||||
it('should remove special attributes on false', inject(function($rootScope, $compile) {
|
||||
element = $compile('<input ng:bind-attr="{disabled:\'{{disabled}}\', readonly:\'{{readonly}}\', checked:\'{{checked}}\'}"/>')($rootScope);
|
||||
var input = element[0];
|
||||
expect(input.disabled).toEqual(false);
|
||||
expect(input.readOnly).toEqual(false);
|
||||
expect(input.checked).toEqual(false);
|
||||
|
||||
$rootScope.disabled = true;
|
||||
$rootScope.readonly = true;
|
||||
$rootScope.checked = true;
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(input.disabled).toEqual(true);
|
||||
expect(input.readOnly).toEqual(true);
|
||||
expect(input.checked).toEqual(true);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
it('should remove special attributes on false', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<input ng:bind-attr="{disabled:\'{{disabled}}\', readonly:\'{{readonly}}\', checked:\'{{checked}}\'}"/>')($rootScope);
|
||||
var input = element[0];
|
||||
expect(input.disabled).toEqual(false);
|
||||
expect(input.readOnly).toEqual(false);
|
||||
expect(input.checked).toEqual(false);
|
||||
|
||||
$rootScope.disabled = true;
|
||||
$rootScope.readonly = true;
|
||||
$rootScope.checked = true;
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(input.disabled).toEqual(true);
|
||||
expect(input.readOnly).toEqual(true);
|
||||
expect(input.checked).toEqual(true);
|
||||
}));
|
||||
|
||||
describe('ng:click', function() {
|
||||
it('should get called on a click', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div ng:click="clicked = true"></div>')($rootScope);
|
||||
element = $compile('<div ng:click="clicked = true"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.clicked).toBeFalsy();
|
||||
|
||||
|
|
@ -146,14 +138,12 @@ describe("directive", function() {
|
|||
}));
|
||||
|
||||
it('should stop event propagation', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div ng:click="outer = true"><div ng:click="inner = true"></div></div>')($rootScope);
|
||||
element = $compile('<div ng:click="outer = true"><div ng:click="inner = true"></div></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.outer).not.toBeDefined();
|
||||
expect($rootScope.inner).not.toBeDefined();
|
||||
|
||||
var innerDiv = element.children()[0];
|
||||
|
||||
browserTrigger(innerDiv, 'click');
|
||||
browserTrigger(element.find('div'), 'click');
|
||||
expect($rootScope.outer).not.toBeDefined();
|
||||
expect($rootScope.inner).toEqual(true);
|
||||
}));
|
||||
|
|
@ -162,7 +152,7 @@ describe("directive", function() {
|
|||
|
||||
describe('ng:submit', function() {
|
||||
it('should get called on form submit', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<form action="" ng:submit="submitted = true">' +
|
||||
element = $compile('<form action="" ng:submit="submitted = true">' +
|
||||
'<input type="submit"/>' +
|
||||
'</form>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
|
@ -175,7 +165,7 @@ describe("directive", function() {
|
|||
|
||||
describe('ng:class', function() {
|
||||
it('should add new and remove old classes dynamically', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div class="existing" ng:class="dynClass"></div>')($rootScope);
|
||||
element = $compile('<div class="existing" ng:class="dynClass"></div>')($rootScope);
|
||||
$rootScope.dynClass = 'A';
|
||||
$rootScope.$digest();
|
||||
expect(element.hasClass('existing')).toBe(true);
|
||||
|
|
@ -196,7 +186,7 @@ describe("directive", function() {
|
|||
|
||||
|
||||
it('should support adding multiple classes via an array', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div class="existing" ng:class="[\'A\', \'B\']"></div>')($rootScope);
|
||||
element = $compile('<div class="existing" ng:class="[\'A\', \'B\']"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(element.hasClass('existing')).toBeTruthy();
|
||||
expect(element.hasClass('A')).toBeTruthy();
|
||||
|
|
@ -227,7 +217,7 @@ describe("directive", function() {
|
|||
|
||||
|
||||
it('should support adding multiple classes via a space delimited string', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div class="existing" ng:class="\'A B\'"></div>')($rootScope);
|
||||
element = $compile('<div class="existing" ng:class="\'A B\'"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(element.hasClass('existing')).toBeTruthy();
|
||||
expect(element.hasClass('A')).toBeTruthy();
|
||||
|
|
@ -236,7 +226,7 @@ describe("directive", function() {
|
|||
|
||||
|
||||
it('should preserve class added post compilation with pre-existing classes', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div class="existing" ng:class="dynClass"></div>')($rootScope);
|
||||
element = $compile('<div class="existing" ng:class="dynClass"></div>')($rootScope);
|
||||
$rootScope.dynClass = 'A';
|
||||
$rootScope.$digest();
|
||||
expect(element.hasClass('existing')).toBe(true);
|
||||
|
|
@ -253,7 +243,7 @@ describe("directive", function() {
|
|||
|
||||
|
||||
it('should preserve class added post compilation without pre-existing classes"', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div ng:class="dynClass"></div>')($rootScope);
|
||||
element = $compile('<div ng:class="dynClass"></div>')($rootScope);
|
||||
$rootScope.dynClass = 'A';
|
||||
$rootScope.$digest();
|
||||
expect(element.hasClass('A')).toBe(true);
|
||||
|
|
@ -269,119 +259,116 @@ describe("directive", function() {
|
|||
|
||||
|
||||
it('should preserve other classes with similar name"', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div class="ui-panel ui-selected" ng:class="dynCls"></div>')($rootScope);
|
||||
element = $compile('<div class="ui-panel ui-selected" ng:class="dynCls"></div>')($rootScope);
|
||||
$rootScope.dynCls = 'panel';
|
||||
$rootScope.$digest();
|
||||
$rootScope.dynCls = 'foo';
|
||||
$rootScope.$digest();
|
||||
expect(element[0].className).toBe('ui-panel ui-selected ng-directive foo');
|
||||
expect(element[0].className).toBe('ui-panel ui-selected foo');
|
||||
}));
|
||||
|
||||
|
||||
it('should not add duplicate classes', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div class="panel bar" ng:class="dynCls"></div>')($rootScope);
|
||||
element = $compile('<div class="panel bar" ng:class="dynCls"></div>')($rootScope);
|
||||
$rootScope.dynCls = 'panel';
|
||||
$rootScope.$digest();
|
||||
expect(element[0].className).toBe('panel bar ng-directive');
|
||||
expect(element[0].className).toBe('panel bar');
|
||||
}));
|
||||
|
||||
|
||||
it('should remove classes even if it was specified via class attribute', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div class="panel bar" ng:class="dynCls"></div>')($rootScope);
|
||||
element = $compile('<div class="panel bar" ng:class="dynCls"></div>')($rootScope);
|
||||
$rootScope.dynCls = 'panel';
|
||||
$rootScope.$digest();
|
||||
$rootScope.dynCls = 'window';
|
||||
$rootScope.$digest();
|
||||
expect(element[0].className).toBe('bar ng-directive window');
|
||||
expect(element[0].className).toBe('bar window');
|
||||
}));
|
||||
|
||||
|
||||
it('should remove classes even if they were added by another code', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div ng:class="dynCls"></div>')($rootScope);
|
||||
element = $compile('<div ng:class="dynCls"></div>')($rootScope);
|
||||
$rootScope.dynCls = 'foo';
|
||||
$rootScope.$digest();
|
||||
element.addClass('foo');
|
||||
$rootScope.dynCls = '';
|
||||
$rootScope.$digest();
|
||||
expect(element[0].className).toBe('ng-directive');
|
||||
}));
|
||||
|
||||
|
||||
it('should convert undefined and null values to an empty string', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div ng:class="dynCls"></div>')($rootScope);
|
||||
element = $compile('<div ng:class="dynCls"></div>')($rootScope);
|
||||
$rootScope.dynCls = [undefined, null];
|
||||
$rootScope.$digest();
|
||||
expect(element[0].className).toBe('ng-directive');
|
||||
}));
|
||||
|
||||
|
||||
it('should ng:class odd/even', inject(function($rootScope, $compile) {
|
||||
element = $compile('<ul><li ng:repeat="i in [0,1]" class="existing" ng:class-odd="\'odd\'" ng:class-even="\'even\'"></li><ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
var e1 = jqLite(element[0].childNodes[1]);
|
||||
var e2 = jqLite(element[0].childNodes[2]);
|
||||
expect(e1.hasClass('existing')).toBeTruthy();
|
||||
expect(e1.hasClass('odd')).toBeTruthy();
|
||||
expect(e2.hasClass('existing')).toBeTruthy();
|
||||
expect(e2.hasClass('even')).toBeTruthy();
|
||||
}));
|
||||
|
||||
|
||||
it('should allow both ng:class and ng:class-odd/even on the same element', inject(function($rootScope, $compile) {
|
||||
element = $compile('<ul>' +
|
||||
'<li ng:repeat="i in [0,1]" ng:class="\'plainClass\'" ' +
|
||||
'ng:class-odd="\'odd\'" ng:class-even="\'even\'"></li>' +
|
||||
'<ul>')($rootScope);
|
||||
$rootScope.$apply();
|
||||
var e1 = jqLite(element[0].childNodes[1]);
|
||||
var e2 = jqLite(element[0].childNodes[2]);
|
||||
|
||||
expect(e1.hasClass('plainClass')).toBeTruthy();
|
||||
expect(e1.hasClass('odd')).toBeTruthy();
|
||||
expect(e1.hasClass('even')).toBeFalsy();
|
||||
expect(e2.hasClass('plainClass')).toBeTruthy();
|
||||
expect(e2.hasClass('even')).toBeTruthy();
|
||||
expect(e2.hasClass('odd')).toBeFalsy();
|
||||
}));
|
||||
|
||||
|
||||
it('should allow both ng:class and ng:class-odd/even with multiple classes', inject(function($rootScope, $compile) {
|
||||
element = $compile('<ul>' +
|
||||
'<li ng:repeat="i in [0,1]" ng:class="[\'A\', \'B\']" ' +
|
||||
'ng:class-odd="[\'C\', \'D\']" ng:class-even="[\'E\', \'F\']"></li>' +
|
||||
'<ul>')($rootScope);
|
||||
$rootScope.$apply();
|
||||
var e1 = jqLite(element[0].childNodes[1]);
|
||||
var e2 = jqLite(element[0].childNodes[2]);
|
||||
|
||||
expect(e1.hasClass('A')).toBeTruthy();
|
||||
expect(e1.hasClass('B')).toBeTruthy();
|
||||
expect(e1.hasClass('C')).toBeTruthy();
|
||||
expect(e1.hasClass('D')).toBeTruthy();
|
||||
expect(e1.hasClass('E')).toBeFalsy();
|
||||
expect(e1.hasClass('F')).toBeFalsy();
|
||||
|
||||
expect(e2.hasClass('A')).toBeTruthy();
|
||||
expect(e2.hasClass('B')).toBeTruthy();
|
||||
expect(e2.hasClass('E')).toBeTruthy();
|
||||
expect(e2.hasClass('F')).toBeTruthy();
|
||||
expect(e2.hasClass('C')).toBeFalsy();
|
||||
expect(e2.hasClass('D')).toBeFalsy();
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
it('should ng:class odd/even', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<ul><li ng:repeat="i in [0,1]" class="existing" ng:class-odd="\'odd\'" ng:class-even="\'even\'"></li><ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
var e1 = jqLite(element[0].childNodes[1]);
|
||||
var e2 = jqLite(element[0].childNodes[2]);
|
||||
expect(e1.hasClass('existing')).toBeTruthy();
|
||||
expect(e1.hasClass('odd')).toBeTruthy();
|
||||
expect(e2.hasClass('existing')).toBeTruthy();
|
||||
expect(e2.hasClass('even')).toBeTruthy();
|
||||
}));
|
||||
|
||||
|
||||
it('should allow both ng:class and ng:class-odd/even on the same element', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<ul>' +
|
||||
'<li ng:repeat="i in [0,1]" ng:class="\'plainClass\'" ' +
|
||||
'ng:class-odd="\'odd\'" ng:class-even="\'even\'"></li>' +
|
||||
'<ul>')($rootScope);
|
||||
$rootScope.$apply();
|
||||
var e1 = jqLite(element[0].childNodes[1]);
|
||||
var e2 = jqLite(element[0].childNodes[2]);
|
||||
|
||||
expect(e1.hasClass('plainClass')).toBeTruthy();
|
||||
expect(e1.hasClass('odd')).toBeTruthy();
|
||||
expect(e1.hasClass('even')).toBeFalsy();
|
||||
expect(e2.hasClass('plainClass')).toBeTruthy();
|
||||
expect(e2.hasClass('even')).toBeTruthy();
|
||||
expect(e2.hasClass('odd')).toBeFalsy();
|
||||
}));
|
||||
|
||||
|
||||
it('should allow both ng:class and ng:class-odd/even with multiple classes', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<ul>' +
|
||||
'<li ng:repeat="i in [0,1]" ng:class="[\'A\', \'B\']" ' +
|
||||
'ng:class-odd="[\'C\', \'D\']" ng:class-even="[\'E\', \'F\']"></li>' +
|
||||
'<ul>')($rootScope);
|
||||
$rootScope.$apply();
|
||||
var e1 = jqLite(element[0].childNodes[1]);
|
||||
var e2 = jqLite(element[0].childNodes[2]);
|
||||
|
||||
expect(e1.hasClass('A')).toBeTruthy();
|
||||
expect(e1.hasClass('B')).toBeTruthy();
|
||||
expect(e1.hasClass('C')).toBeTruthy();
|
||||
expect(e1.hasClass('D')).toBeTruthy();
|
||||
expect(e1.hasClass('E')).toBeFalsy();
|
||||
expect(e1.hasClass('F')).toBeFalsy();
|
||||
|
||||
expect(e2.hasClass('A')).toBeTruthy();
|
||||
expect(e2.hasClass('B')).toBeTruthy();
|
||||
expect(e2.hasClass('E')).toBeTruthy();
|
||||
expect(e2.hasClass('F')).toBeTruthy();
|
||||
expect(e2.hasClass('C')).toBeFalsy();
|
||||
expect(e2.hasClass('D')).toBeFalsy();
|
||||
}));
|
||||
|
||||
|
||||
describe('ng:style', function() {
|
||||
|
||||
it('should set', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div ng:style="{height: \'40px\'}"></div>')($rootScope);
|
||||
element = $compile('<div ng:style="{height: \'40px\'}"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(element.css('height')).toEqual('40px');
|
||||
}));
|
||||
|
||||
|
||||
it('should silently ignore undefined style', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div ng:style="myStyle"></div>')($rootScope);
|
||||
element = $compile('<div ng:style="myStyle"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(element.hasClass('ng-exception')).toBeFalsy();
|
||||
}));
|
||||
|
|
@ -454,8 +441,8 @@ describe("directive", function() {
|
|||
|
||||
describe('ng:show', function() {
|
||||
it('should show and hide an element', inject(function($rootScope, $compile) {
|
||||
var element = jqLite('<div ng:show="exp"></div>');
|
||||
var element = $compile(element)($rootScope);
|
||||
element = jqLite('<div ng:show="exp"></div>');
|
||||
element = $compile(element)($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(isCssVisible(element)).toEqual(false);
|
||||
$rootScope.exp = true;
|
||||
|
|
@ -465,8 +452,8 @@ describe("directive", function() {
|
|||
|
||||
|
||||
it('should make hidden element visible', inject(function($rootScope, $compile) {
|
||||
var element = jqLite('<div style="display: none" ng:show="exp"></div>');
|
||||
var element = $compile(element)($rootScope);
|
||||
element = jqLite('<div style="display: none" ng:show="exp"></div>');
|
||||
element = $compile(element)($rootScope);
|
||||
expect(isCssVisible(element)).toBe(false);
|
||||
$rootScope.exp = true;
|
||||
$rootScope.$digest();
|
||||
|
|
@ -476,8 +463,8 @@ describe("directive", function() {
|
|||
|
||||
describe('ng:hide', function() {
|
||||
it('should hide an element', inject(function($rootScope, $compile) {
|
||||
var element = jqLite('<div ng:hide="exp"></div>');
|
||||
var element = $compile(element)($rootScope);
|
||||
element = jqLite('<div ng:hide="exp"></div>');
|
||||
element = $compile(element)($rootScope);
|
||||
expect(isCssVisible(element)).toBe(true);
|
||||
$rootScope.exp = true;
|
||||
$rootScope.$digest();
|
||||
|
|
@ -552,7 +539,7 @@ describe("directive", function() {
|
|||
describe('ng:cloak', function() {
|
||||
|
||||
it('should get removed when an element is compiled', inject(function($rootScope, $compile) {
|
||||
var element = jqLite('<div ng:cloak></div>');
|
||||
element = jqLite('<div ng:cloak></div>');
|
||||
expect(element.attr('ng:cloak')).toBe('');
|
||||
$compile(element);
|
||||
expect(element.attr('ng:cloak')).toBeUndefined();
|
||||
|
|
@ -560,7 +547,7 @@ describe("directive", function() {
|
|||
|
||||
|
||||
it('should remove ng-cloak class from a compiled element', inject(function($rootScope, $compile) {
|
||||
var element = jqLite('<div ng:cloak class="foo ng-cloak bar"></div>');
|
||||
element = jqLite('<div ng:cloak class="foo ng-cloak bar"></div>');
|
||||
|
||||
expect(element.hasClass('foo')).toBe(true);
|
||||
expect(element.hasClass('ng-cloak')).toBe(true);
|
||||
|
|
|
|||
|
|
@ -1,33 +1,38 @@
|
|||
'use strict';
|
||||
|
||||
describe("markups", function() {
|
||||
var element;
|
||||
|
||||
afterEach(function() {
|
||||
dealoc(element);
|
||||
});
|
||||
|
||||
it('should translate {{}} in text', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div>hello {{name}}!</div>')($rootScope)
|
||||
expect(sortedHtml(element)).toEqual('<div>hello <span ng:bind="name"></span>!</div>');
|
||||
element = $compile('<div>hello {{name}}!</div>')($rootScope)
|
||||
$rootScope.$digest();
|
||||
expect(sortedHtml(element)).toEqual('<div>hello !</div>');
|
||||
$rootScope.name = 'Misko';
|
||||
$rootScope.$digest();
|
||||
expect(sortedHtml(element)).toEqual('<div>hello <span ng:bind="name">Misko</span>!</div>');
|
||||
expect(sortedHtml(element)).toEqual('<div>hello Misko!</div>');
|
||||
}));
|
||||
|
||||
it('should translate {{}} in terminal nodes', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<select ng:model="x"><option value="">Greet {{name}}!</option></select>')($rootScope)
|
||||
element = $compile('<select ng:model="x"><option value="">Greet {{name}}!</option></select>')($rootScope)
|
||||
$rootScope.$digest();
|
||||
expect(sortedHtml(element).replace(' selected="true"', '')).
|
||||
toEqual('<select ng:model="x">' +
|
||||
'<option ng:bind-template="Greet {{name}}!">Greet !</option>' +
|
||||
'<option>Greet !</option>' +
|
||||
'</select>');
|
||||
$rootScope.name = 'Misko';
|
||||
$rootScope.$digest();
|
||||
expect(sortedHtml(element).replace(' selected="true"', '')).
|
||||
toEqual('<select ng:model="x">' +
|
||||
'<option ng:bind-template="Greet {{name}}!">Greet Misko!</option>' +
|
||||
'<option>Greet Misko!</option>' +
|
||||
'</select>');
|
||||
}));
|
||||
|
||||
it('should translate {{}} in attributes', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div src="http://server/{{path}}.png"/>')($rootScope)
|
||||
expect(element.attr('ng:bind-attr')).toEqual('{"src":"http://server/{{path}}.png"}');
|
||||
element = $compile('<div src="http://server/{{path}}.png"/>')($rootScope)
|
||||
$rootScope.path = 'a/b';
|
||||
$rootScope.$digest();
|
||||
expect(element.attr('src')).toEqual("http://server/a/b.png");
|
||||
|
|
@ -56,36 +61,38 @@ describe("markups", function() {
|
|||
|
||||
|
||||
it('should populate value attribute on OPTION', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<select ng:model="x"><option>abc</option></select>')($rootScope)
|
||||
element = $compile('<select ng:model="x"><option>abc</option></select>')($rootScope)
|
||||
expect(element).toHaveValue('abc');
|
||||
}));
|
||||
|
||||
it('should ignore value if already exists', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<select ng:model="x"><option value="abc">xyz</option></select>')($rootScope)
|
||||
element = $compile('<select ng:model="x"><option value="abc">xyz</option></select>')($rootScope)
|
||||
expect(element).toHaveValue('abc');
|
||||
}));
|
||||
|
||||
it('should set value even if newlines present', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<select ng:model="x"><option attr="\ntext\n" \n>\nabc\n</option></select>')($rootScope)
|
||||
element = $compile('<select ng:model="x"><option attr="\ntext\n" \n>\nabc\n</option></select>')($rootScope)
|
||||
expect(element).toHaveValue('\nabc\n');
|
||||
}));
|
||||
|
||||
it('should set value even if self closing HTML', inject(function($rootScope, $compile) {
|
||||
// IE removes the \n from option, which makes this test pointless
|
||||
if (msie) return;
|
||||
var element = $compile('<select ng:model="x"><option>\n</option></select>')($rootScope)
|
||||
element = $compile('<select ng:model="x"><option>\n</option></select>')($rootScope)
|
||||
expect(element).toHaveValue('\n');
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
it('should bind href', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<a ng:href="{{url}}"></a>')($rootScope)
|
||||
expect(sortedHtml(element)).toEqual('<a ng:bind-attr="{"href":"{{url}}"}"></a>');
|
||||
element = $compile('<a ng:href="{{url}}"></a>')($rootScope)
|
||||
$rootScope.url = 'http://server'
|
||||
$rootScope.$digest();
|
||||
expect(element.attr('href')).toEqual('http://server');
|
||||
}));
|
||||
|
||||
it('should bind disabled', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<button ng:disabled="{{isDisabled}}">Button</button>')($rootScope)
|
||||
element = $compile('<button ng:disabled="{{isDisabled}}">Button</button>')($rootScope)
|
||||
$rootScope.isDisabled = false;
|
||||
$rootScope.$digest();
|
||||
expect(element.attr('disabled')).toBeFalsy();
|
||||
|
|
@ -95,7 +102,7 @@ describe("markups", function() {
|
|||
}));
|
||||
|
||||
it('should bind checked', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<input type="checkbox" ng:checked="{{isChecked}}" />')($rootScope)
|
||||
element = $compile('<input type="checkbox" ng:checked="{{isChecked}}" />')($rootScope)
|
||||
$rootScope.isChecked = false;
|
||||
$rootScope.$digest();
|
||||
expect(element.attr('checked')).toBeFalsy();
|
||||
|
|
@ -105,7 +112,7 @@ describe("markups", function() {
|
|||
}));
|
||||
|
||||
it('should bind selected', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<select><option value=""></option><option ng:selected="{{isSelected}}">Greetings!</option></select>')($rootScope)
|
||||
element = $compile('<select><option value=""></option><option ng:selected="{{isSelected}}">Greetings!</option></select>')($rootScope)
|
||||
jqLite(document.body).append(element)
|
||||
$rootScope.isSelected=false;
|
||||
$rootScope.$digest();
|
||||
|
|
@ -116,7 +123,7 @@ describe("markups", function() {
|
|||
}));
|
||||
|
||||
it('should bind readonly', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<input type="text" ng:readonly="{{isReadonly}}" />')($rootScope)
|
||||
element = $compile('<input type="text" ng:readonly="{{isReadonly}}" />')($rootScope)
|
||||
$rootScope.isReadonly=false;
|
||||
$rootScope.$digest();
|
||||
expect(element.attr('readOnly')).toBeFalsy();
|
||||
|
|
@ -126,7 +133,7 @@ describe("markups", function() {
|
|||
}));
|
||||
|
||||
it('should bind multiple', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<select ng:multiple="{{isMultiple}}"></select>')($rootScope)
|
||||
element = $compile('<select ng:multiple="{{isMultiple}}"></select>')($rootScope)
|
||||
$rootScope.isMultiple=false;
|
||||
$rootScope.$digest();
|
||||
expect(element.attr('multiple')).toBeFalsy();
|
||||
|
|
@ -136,38 +143,37 @@ describe("markups", function() {
|
|||
}));
|
||||
|
||||
it('should bind src', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div ng:src="{{url}}" />')($rootScope)
|
||||
element = $compile('<div ng:src="{{url}}" />')($rootScope)
|
||||
$rootScope.url = 'http://localhost/';
|
||||
$rootScope.$digest();
|
||||
expect(element.attr('src')).toEqual('http://localhost/');
|
||||
}));
|
||||
|
||||
it('should bind href and merge with other attrs', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<a ng:href="{{url}}" rel="{{rel}}"></a>')($rootScope)
|
||||
expect(sortedHtml(element)).toEqual('<a ng:bind-attr="{"href":"{{url}}","rel":"{{rel}}"}"></a>');
|
||||
element = $compile('<a ng:href="{{url}}" rel="{{rel}}"></a>')($rootScope);
|
||||
$rootScope.url = 'http://server';
|
||||
$rootScope.rel = 'REL';
|
||||
$rootScope.$digest();
|
||||
expect(element.attr('href')).toEqual('http://server');
|
||||
expect(element.attr('rel')).toEqual('REL');
|
||||
}));
|
||||
|
||||
it('should bind Text with no Bindings', inject(function($compile) {
|
||||
var $rootScope;
|
||||
function newScope (){
|
||||
return $rootScope = angular.injector(['ng']).get('$rootScope');
|
||||
}
|
||||
it('should bind Text with no Bindings', inject(function($compile, $rootScope) {
|
||||
forEach(['checked', 'disabled', 'multiple', 'readonly', 'selected'], function(name) {
|
||||
var element = $compile('<div ng:' + name + '="some"></div>')(newScope())
|
||||
expect(element.attr('ng:bind-attr')).toBe('{"' + name +'":"some"}');
|
||||
element = $compile('<div ng:' + name + '="some"></div>')($rootScope)
|
||||
$rootScope.$digest();
|
||||
expect(element.attr(name)).toBe(name);
|
||||
dealoc(element);
|
||||
});
|
||||
|
||||
var element = $compile('<div ng:src="some"></div>')(newScope())
|
||||
element = $compile('<div ng:src="some"></div>')($rootScope)
|
||||
$rootScope.$digest();
|
||||
expect(sortedHtml(element)).toEqual('<div ng:bind-attr="{"src":"some"}" src="some"></div>');
|
||||
expect(element.attr('src')).toEqual('some');
|
||||
dealoc(element);
|
||||
|
||||
var element = $compile('<div ng:href="some"></div>')(newScope())
|
||||
element = $compile('<div ng:href="some"></div>')($rootScope)
|
||||
$rootScope.$digest();
|
||||
expect(sortedHtml(element)).toEqual('<div href="some" ng:bind-attr="{"href":"some"}"></div>');
|
||||
expect(element.attr('href')).toEqual('some');
|
||||
dealoc(element);
|
||||
}));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,9 +2,13 @@
|
|||
|
||||
describe('HTML', function() {
|
||||
|
||||
function expectHTML(html) {
|
||||
return expect(new HTML(html).get());
|
||||
}
|
||||
var expectHTML;
|
||||
|
||||
beforeEach(inject(function($sanitize) {
|
||||
expectHTML = function(html){
|
||||
return expect($sanitize(html));
|
||||
};
|
||||
}));
|
||||
|
||||
describe('htmlParser', function() {
|
||||
var handler, start, text;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
'use strict';
|
||||
|
||||
describe("angular.scenario.dsl", function() {
|
||||
var element;
|
||||
var $window, $root;
|
||||
var application, eventLog;
|
||||
var eventLog;
|
||||
|
||||
afterEach(function() {
|
||||
dealoc(element);
|
||||
});
|
||||
|
||||
beforeEach(inject(function($injector) {
|
||||
eventLog = [];
|
||||
|
|
@ -393,28 +398,26 @@ describe("angular.scenario.dsl", function() {
|
|||
|
||||
describe('Repeater', function() {
|
||||
var chain;
|
||||
beforeEach(function() {
|
||||
doc.append(
|
||||
'<ul>' +
|
||||
' <li><span ng:bind="name" class="ng-binding">misko</span>' +
|
||||
' <span ng:bind="test && gender" class="ng-binding">male</span></li>' +
|
||||
' <li><span ng:bind="name" class="ng-binding">felisa</span>' +
|
||||
' <span ng:bind="gender | uppercase" class="ng-binding">female</span></li>' +
|
||||
'</ul>'
|
||||
);
|
||||
beforeEach(inject(function($compile, $rootScope) {
|
||||
element = $compile(
|
||||
'<ul><li ng-repeat="i in items">{{i.name}} {{i.gender}}</li></ul>')($rootScope);
|
||||
$rootScope.items = [{name:'misko', gender:'male'}, {name:'felisa', gender:'female'}];
|
||||
$rootScope.$apply();
|
||||
doc.append(element);
|
||||
chain = $root.dsl.repeater('ul li');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should get the row count', function() {
|
||||
chain.count();
|
||||
expect($root.futureResult).toEqual(2);
|
||||
});
|
||||
|
||||
it('should return 0 if repeater doesnt match', function() {
|
||||
doc.find('ul').html('');
|
||||
it('should return 0 if repeater doesnt match', inject(function($rootScope) {
|
||||
$rootScope.items = [];
|
||||
$rootScope.$apply();
|
||||
chain.count();
|
||||
expect($root.futureResult).toEqual(0);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should get a row of bindings', function() {
|
||||
chain.row(1);
|
||||
|
|
@ -422,7 +425,7 @@ describe("angular.scenario.dsl", function() {
|
|||
});
|
||||
|
||||
it('should get a column of bindings', function() {
|
||||
chain.column('gender');
|
||||
chain.column('i.gender');
|
||||
expect($root.futureResult).toEqual(['male', 'female']);
|
||||
});
|
||||
|
||||
|
|
@ -437,45 +440,60 @@ describe("angular.scenario.dsl", function() {
|
|||
});
|
||||
|
||||
describe('Binding', function() {
|
||||
var compile;
|
||||
|
||||
beforeEach(inject(function($compile, $rootScope) {
|
||||
compile = function(html, value) {
|
||||
element = $compile(html)($rootScope);
|
||||
doc.append(element);
|
||||
$rootScope.foo = {bar: value || 'some value'};
|
||||
$rootScope.$apply();
|
||||
};
|
||||
}));
|
||||
|
||||
|
||||
it('should select binding in interpolation', function() {
|
||||
compile('<span>{{ foo.bar }}</span>');
|
||||
$root.dsl.binding('foo.bar');
|
||||
expect($root.futureResult).toEqual('some value');
|
||||
});
|
||||
|
||||
it('should select binding in multiple interpolations', function() {
|
||||
compile('<span>{{ foo.bar }}<hr/> {{ true }}</span>');
|
||||
$root.dsl.binding('foo.bar');
|
||||
expect($root.futureResult).toEqual('some value');
|
||||
|
||||
$root.dsl.binding('true');
|
||||
expect($root.futureResult).toEqual('true');
|
||||
});
|
||||
|
||||
it('should select binding by name', function() {
|
||||
doc.append('<span class="ng-binding" ng:bind="foo.bar">some value</span>');
|
||||
compile('<span ng:bind=" foo.bar "></span>');
|
||||
$root.dsl.binding('foo.bar');
|
||||
expect($root.futureResult).toEqual('some value');
|
||||
});
|
||||
|
||||
it('should select binding by regexp', function() {
|
||||
doc.append('<span class="ng-binding" ng:bind="foo.bar">some value</span>');
|
||||
compile('<span ng:bind="foo.bar">some value</span>');
|
||||
$root.dsl.binding(/^foo\..+/);
|
||||
expect($root.futureResult).toEqual('some value');
|
||||
});
|
||||
|
||||
it('should return value for input elements', function() {
|
||||
doc.append('<input type="text" class="ng-binding" ng:bind="foo.bar" value="some value"/>');
|
||||
$root.dsl.binding('foo.bar');
|
||||
expect($root.futureResult).toEqual('some value');
|
||||
});
|
||||
|
||||
it('should return value for textarea elements', function() {
|
||||
doc.append('<textarea class="ng-binding" ng:bind="foo.bar">some value</textarea>');
|
||||
$root.dsl.binding('foo.bar');
|
||||
expect($root.futureResult).toEqual('some value');
|
||||
});
|
||||
|
||||
it('should return innerHTML for all the other elements', function() {
|
||||
doc.append('<div class="ng-binding" ng:bind="foo.bar">some <b>value</b></div>');
|
||||
compile('<div ng-bind-html="foo.bar"></div>', 'some <b>value</b>');
|
||||
$root.dsl.binding('foo.bar');
|
||||
expect($root.futureResult.toLowerCase()).toEqual('some <b>value</b>');
|
||||
});
|
||||
|
||||
it('should select binding in template by name', function() {
|
||||
doc.append('<pre class="ng-binding" ng:bind-template="foo {{bar}} baz">foo some baz</pre>');
|
||||
$root.dsl.binding('bar');
|
||||
expect($root.futureResult).toEqual('foo some baz');
|
||||
compile('<pre ng:bind-template="foo {{foo.bar}} baz"></pre>', 'bar');
|
||||
$root.dsl.binding('foo.bar');
|
||||
expect($root.futureResult).toEqual('bar');
|
||||
});
|
||||
|
||||
it('should match bindings by substring match', function() {
|
||||
doc.append('<pre class="ng-binding" ng:bind="foo.bar() && test.baz() | filter">binding value</pre>');
|
||||
$root.dsl.binding('test.baz');
|
||||
compile('<pre ng:bind="foo.bar | filter"></pre>', 'binding value');
|
||||
$root.dsl.binding('foo . bar');
|
||||
expect($root.futureResult).toEqual('binding value');
|
||||
});
|
||||
|
||||
|
|
@ -485,7 +503,7 @@ describe("angular.scenario.dsl", function() {
|
|||
});
|
||||
|
||||
it('should return error if no binding matches', function() {
|
||||
doc.append('<span class="ng-binding" ng:bind="foo">some value</span>');
|
||||
compile('<span ng:bind="foo">some value</span>');
|
||||
$root.dsl.binding('foo.bar');
|
||||
expect($root.futureError).toMatch(/did not match/);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -153,14 +153,6 @@ describe('filters', function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe('html', function() {
|
||||
it('should do basic filter', function() {
|
||||
var html = filter('html')("a<b>c</b>d");
|
||||
expect(html instanceof HTML).toBeTruthy();
|
||||
expect(html.html).toEqual("a<b>c</b>d");
|
||||
});
|
||||
});
|
||||
|
||||
describe('linky', function() {
|
||||
var linky;
|
||||
|
||||
|
|
@ -169,7 +161,7 @@ describe('filters', function() {
|
|||
}));
|
||||
|
||||
it('should do basic filter', function() {
|
||||
expect(linky("http://ab/ (http://a/) <http://a/> http://1.2/v:~-123. c").html).
|
||||
expect(linky("http://ab/ (http://a/) <http://a/> http://1.2/v:~-123. c")).
|
||||
toEqual('<a href="http://ab/">http://ab/</a> ' +
|
||||
'(<a href="http://a/">http://a/</a>) ' +
|
||||
'<<a href="http://a/">http://a/</a>> ' +
|
||||
|
|
@ -178,11 +170,11 @@ describe('filters', function() {
|
|||
});
|
||||
|
||||
it('should handle mailto:', function() {
|
||||
expect(linky("mailto:me@example.com").html).
|
||||
expect(linky("mailto:me@example.com")).
|
||||
toEqual('<a href="mailto:me@example.com">me@example.com</a>');
|
||||
expect(linky("me@example.com").html).
|
||||
expect(linky("me@example.com")).
|
||||
toEqual('<a href="mailto:me@example.com">me@example.com</a>');
|
||||
expect(linky("send email to me@example.com, but").html).
|
||||
expect(linky("send email to me@example.com, but")).
|
||||
toEqual('send email to <a href="mailto:me@example.com">me@example.com</a>, but');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ describe('$log', function() {
|
|||
e.stack = undefined;
|
||||
|
||||
$log = new $LogProvider().$get[1]({console:{error:function() {
|
||||
errorArgs = arguments;
|
||||
errorArgs = [].slice.call(arguments, 0);
|
||||
}}});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -61,9 +61,19 @@ afterEach(function() {
|
|||
|
||||
function dealoc(obj) {
|
||||
if (obj) {
|
||||
var element = obj.$element || obj || {};
|
||||
if (element.nodeName) element = jqLite(element);
|
||||
if (element.dealoc) element.dealoc();
|
||||
if (isElement(obj)) {
|
||||
var element = obj;
|
||||
if (element.nodeName) element = jqLite(element);
|
||||
if (element.dealoc) element.dealoc();
|
||||
} else {
|
||||
for(var key in jqCache) {
|
||||
var value = jqCache[key];
|
||||
if (value.$scope == obj) {
|
||||
delete jqCache[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ describe('widget: input', function() {
|
|||
|
||||
it('should bind update scope from model', function() {
|
||||
createInput();
|
||||
expect(scope.form.name.$required).toBe(false);
|
||||
scope.name = 'misko';
|
||||
scope.$digest();
|
||||
expect(inputElement.val()).toEqual('misko');
|
||||
|
|
@ -60,7 +59,6 @@ describe('widget: input', function() {
|
|||
|
||||
it('should require', function() {
|
||||
createInput({required:''});
|
||||
expect(scope.form.name.$required).toBe(true);
|
||||
scope.$digest();
|
||||
expect(scope.form.name.$valid).toBe(false);
|
||||
scope.name = 'misko';
|
||||
|
|
@ -159,7 +157,7 @@ describe('widget: input', function() {
|
|||
'</div>');
|
||||
scope.obj = { abc: { name: 'Misko'} };
|
||||
scope.$digest();
|
||||
expect(scope.$element.find('input').val()).toEqual('Misko');
|
||||
expect(element.find('input').val()).toEqual('Misko');
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -181,7 +179,7 @@ describe('widget: input', function() {
|
|||
it("should render as blank if null", function() {
|
||||
compile('<input type="text" ng:model="age" ng:format="number" ng:init="age=null"/>');
|
||||
expect(scope.age).toBeNull();
|
||||
expect(scope.$element[0].value).toEqual('');
|
||||
expect(element[0].value).toEqual('');
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -189,19 +187,19 @@ describe('widget: input', function() {
|
|||
compile('<input type="number" ng:model="age"/>');
|
||||
scope.age = 123;
|
||||
scope.$digest();
|
||||
expect(scope.$element.val()).toEqual('123');
|
||||
expect(element.val()).toEqual('123');
|
||||
try {
|
||||
// to allow non-number values, we have to change type so that
|
||||
// the browser which have number validation will not interfere with
|
||||
// this test. IE8 won't allow it hence the catch.
|
||||
scope.$element[0].setAttribute('type', 'text');
|
||||
element[0].setAttribute('type', 'text');
|
||||
} catch (e){}
|
||||
scope.$element.val('123X');
|
||||
browserTrigger(scope.$element, 'change');
|
||||
element.val('123X');
|
||||
browserTrigger(element, 'change');
|
||||
defer.flush();
|
||||
expect(scope.$element.val()).toEqual('123X');
|
||||
expect(element.val()).toEqual('123X');
|
||||
expect(scope.age).toEqual(123);
|
||||
expect(scope.$element).toBeInvalid();
|
||||
expect(element).toBeInvalid();
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -211,28 +209,28 @@ describe('widget: input', function() {
|
|||
// the user from ever typying ','.
|
||||
compile('<input type="list" ng:model="list"/>');
|
||||
|
||||
scope.$element.val('a ');
|
||||
browserTrigger(scope.$element, 'change');
|
||||
element.val('a ');
|
||||
browserTrigger(element, 'change');
|
||||
defer.flush();
|
||||
expect(scope.$element.val()).toEqual('a ');
|
||||
expect(element.val()).toEqual('a ');
|
||||
expect(scope.list).toEqual(['a']);
|
||||
|
||||
scope.$element.val('a ,');
|
||||
browserTrigger(scope.$element, 'change');
|
||||
element.val('a ,');
|
||||
browserTrigger(element, 'change');
|
||||
defer.flush();
|
||||
expect(scope.$element.val()).toEqual('a ,');
|
||||
expect(element.val()).toEqual('a ,');
|
||||
expect(scope.list).toEqual(['a']);
|
||||
|
||||
scope.$element.val('a , ');
|
||||
browserTrigger(scope.$element, 'change');
|
||||
element.val('a , ');
|
||||
browserTrigger(element, 'change');
|
||||
defer.flush();
|
||||
expect(scope.$element.val()).toEqual('a , ');
|
||||
expect(element.val()).toEqual('a , ');
|
||||
expect(scope.list).toEqual(['a']);
|
||||
|
||||
scope.$element.val('a , b');
|
||||
browserTrigger(scope.$element, 'change');
|
||||
element.val('a , b');
|
||||
browserTrigger(element, 'change');
|
||||
defer.flush();
|
||||
expect(scope.$element.val()).toEqual('a , b');
|
||||
expect(element.val()).toEqual('a , b');
|
||||
expect(scope.list).toEqual(['a', 'b']);
|
||||
});
|
||||
|
||||
|
|
@ -240,7 +238,7 @@ describe('widget: input', function() {
|
|||
it("should come up blank when no value specified", function() {
|
||||
compile('<input type="number" ng:model="age"/>');
|
||||
scope.$digest();
|
||||
expect(scope.$element.val()).toEqual('');
|
||||
expect(element.val()).toEqual('');
|
||||
expect(scope.age).toEqual(null);
|
||||
});
|
||||
});
|
||||
|
|
@ -250,7 +248,7 @@ describe('widget: input', function() {
|
|||
it("should format booleans", function() {
|
||||
compile('<input type="checkbox" ng:model="name" ng:init="name=false"/>');
|
||||
expect(scope.name).toBe(false);
|
||||
expect(scope.$element[0].checked).toBe(false);
|
||||
expect(element[0].checked).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -270,15 +268,15 @@ describe('widget: input', function() {
|
|||
|
||||
scope.name='y';
|
||||
scope.$digest();
|
||||
expect(scope.$element[0].checked).toBe(true);
|
||||
expect(element[0].checked).toBe(true);
|
||||
|
||||
scope.name='n';
|
||||
scope.$digest();
|
||||
expect(scope.$element[0].checked).toBe(false);
|
||||
expect(element[0].checked).toBe(false);
|
||||
|
||||
scope.name='abc';
|
||||
scope.$digest();
|
||||
expect(scope.$element[0].checked).toBe(false);
|
||||
expect(element[0].checked).toBe(false);
|
||||
|
||||
browserTrigger(element);
|
||||
expect(scope.name).toEqual('y');
|
||||
|
|
@ -302,7 +300,6 @@ describe('widget: input', function() {
|
|||
|
||||
it("should process required", inject(function($formFactory) {
|
||||
compile('<input type="text" ng:model="price" name="p" required/>', jqLite(document.body));
|
||||
expect($formFactory.rootForm.p.$required).toBe(true);
|
||||
expect(element.hasClass('ng-invalid')).toBeTruthy();
|
||||
|
||||
scope.price = 'xxx';
|
||||
|
|
@ -394,7 +391,7 @@ describe('widget: input', function() {
|
|||
'</div>');
|
||||
|
||||
expect(scope.choose).toEqual('C');
|
||||
var inputs = scope.$element.find('input');
|
||||
var inputs = element.find('input');
|
||||
expect(inputs[1].checked).toBe(false);
|
||||
expect(inputs[2].checked).toBe(true);
|
||||
});
|
||||
|
|
@ -408,7 +405,7 @@ describe('widget: input', function() {
|
|||
'</div>');
|
||||
|
||||
expect(scope.choose).toEqual('A');
|
||||
var inputs = scope.$element.find('input');
|
||||
var inputs = element.find('input');
|
||||
expect(inputs[0].checked).toBe(true);
|
||||
expect(inputs[1].checked).toBe(false);
|
||||
});
|
||||
|
|
@ -421,7 +418,7 @@ describe('widget: input', function() {
|
|||
' type="radio" ng:model="choice" value="{{item}}" name="choice">'+
|
||||
'</li>');
|
||||
|
||||
var inputs = scope.$element.find('input');
|
||||
var inputs = element.find('input');
|
||||
expect(inputs[0].checked).toBe(false);
|
||||
expect(inputs[1].checked).toBe(false);
|
||||
|
||||
|
|
@ -435,7 +432,7 @@ describe('widget: input', function() {
|
|||
function($rootScope, $compile){
|
||||
$rootScope.choice = 'b';
|
||||
$rootScope.items = ['a', 'b'];
|
||||
var element = $compile(
|
||||
element = $compile(
|
||||
'<li>'+
|
||||
'<input ng:repeat="item in items" ' +
|
||||
' type="radio" ng:model="choice" value="{{item}}" name="choice">'+
|
||||
|
|
@ -465,21 +462,22 @@ describe('widget: input', function() {
|
|||
$rootScope.value = undefined;
|
||||
$rootScope.$digest();
|
||||
expect(element.val()).toEqual('');
|
||||
dealoc(element);
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
it('should ignore text widget which have no name', function() {
|
||||
compile('<input type="text"/>');
|
||||
expect(scope.$element.attr('ng-exception')).toBeFalsy();
|
||||
expect(scope.$element.hasClass('ng-exception')).toBeFalsy();
|
||||
expect(element.attr('ng-exception')).toBeFalsy();
|
||||
expect(element.hasClass('ng-exception')).toBeFalsy();
|
||||
});
|
||||
|
||||
|
||||
it('should ignore checkbox widget which have no name', function() {
|
||||
compile('<input type="checkbox"/>');
|
||||
expect(scope.$element.attr('ng-exception')).toBeFalsy();
|
||||
expect(scope.$element.hasClass('ng-exception')).toBeFalsy();
|
||||
expect(element.attr('ng-exception')).toBeFalsy();
|
||||
expect(element.hasClass('ng-exception')).toBeFalsy();
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -506,6 +504,7 @@ describe('widget: input', function() {
|
|||
|
||||
expect(formFactory).toBe($formFactory);
|
||||
expect(input[0]).toBe(element[0]);
|
||||
dealoc(element);
|
||||
}));
|
||||
|
||||
it('should throw an error of Controller not declared in scope', inject(function($rootScope, $compile) {
|
||||
|
|
@ -530,13 +529,13 @@ describe('widget: input', function() {
|
|||
forEach(validList, function(value){
|
||||
it('should validate "' + value + '"', function() {
|
||||
setup(value);
|
||||
expect(scope.$element).toBeValid();
|
||||
expect(element).toBeValid();
|
||||
});
|
||||
});
|
||||
forEach(invalidList, function(value){
|
||||
it('should NOT validate "' + value + '"', function() {
|
||||
setup(value);
|
||||
expect(scope.$element).toBeInvalid();
|
||||
expect(element).toBeInvalid();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -553,10 +552,10 @@ describe('widget: input', function() {
|
|||
// to allow non-number values, we have to change type so that
|
||||
// the browser which have number validation will not interfere with
|
||||
// this test. IE8 won't allow it hence the catch.
|
||||
scope.$element[0].setAttribute('type', 'text');
|
||||
element[0].setAttribute('type', 'text');
|
||||
} catch (e){}
|
||||
if (value != undefined) {
|
||||
scope.$element.val(value);
|
||||
element.val(value);
|
||||
browserTrigger(element, 'keydown');
|
||||
defer.flush();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ describe('select', function() {
|
|||
} else {
|
||||
element = jqLite(html);
|
||||
}
|
||||
$compile(element)($rootScope);
|
||||
element = $compile(element)($rootScope);
|
||||
scope.$apply();
|
||||
return scope;
|
||||
};
|
||||
|
|
@ -37,7 +37,7 @@ describe('select', function() {
|
|||
scope.b = 'bar';
|
||||
scope.$digest();
|
||||
|
||||
expect(scope.$element.text()).toBe('foobarC');
|
||||
expect(element.text()).toBe('foobarC');
|
||||
});
|
||||
|
||||
it('should require', inject(function($formFactory) {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,15 @@
|
|||
'use strict';
|
||||
|
||||
describe('widget', function() {
|
||||
describe('ng:switch', function() {
|
||||
var element;
|
||||
|
||||
afterEach(function(){
|
||||
dealoc(element);
|
||||
});
|
||||
|
||||
describe('ng:switch', inject(function($rootScope, $compile) {
|
||||
it('should switch on value change', inject(function($rootScope, $compile) {
|
||||
var element = $compile(
|
||||
element = $compile(
|
||||
'<ng:switch on="select">' +
|
||||
'<div ng:switch-when="1">first:{{name}}</div>' +
|
||||
'<div ng:switch-when="2">second:{{name}}</div>' +
|
||||
|
|
@ -29,7 +35,7 @@ describe('widget', function() {
|
|||
|
||||
|
||||
it('should switch on switch-when-default', inject(function($rootScope, $compile) {
|
||||
var element = $compile(
|
||||
element = $compile(
|
||||
'<ng:switch on="select">' +
|
||||
'<div ng:switch-when="1">one</div>' +
|
||||
'<div ng:switch-default>other</div>' +
|
||||
|
|
@ -43,7 +49,7 @@ describe('widget', function() {
|
|||
|
||||
|
||||
it('should call change on switch', inject(function($rootScope, $compile) {
|
||||
var element = $compile(
|
||||
element = $compile(
|
||||
'<ng:switch on="url" change="name=\'works\'">' +
|
||||
'<div ng:switch-when="a">{{name}}</div>' +
|
||||
'</ng:switch>')($rootScope);
|
||||
|
|
@ -52,7 +58,7 @@ describe('widget', function() {
|
|||
expect($rootScope.name).toEqual(undefined);
|
||||
expect(element.text()).toEqual('works');
|
||||
}));
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
describe('ng:include', function() {
|
||||
|
|
@ -66,7 +72,7 @@ describe('widget', function() {
|
|||
|
||||
it('should include on external file', inject(putIntoCache('myUrl', '{{name}}'),
|
||||
function($rootScope, $compile, $browser) {
|
||||
var element = jqLite('<ng:include src="url" scope="childScope"></ng:include>');
|
||||
element = jqLite('<ng:include src="url" scope="childScope"></ng:include>');
|
||||
element = $compile(element)($rootScope);
|
||||
$rootScope.childScope = $rootScope.$new();
|
||||
$rootScope.childScope.name = 'misko';
|
||||
|
|
@ -79,7 +85,7 @@ describe('widget', function() {
|
|||
it('should remove previously included text if a falsy value is bound to src', inject(
|
||||
putIntoCache('myUrl', '{{name}}'),
|
||||
function($rootScope, $compile, $browser) {
|
||||
var element = jqLite('<ng:include src="url" scope="childScope"></ng:include>');
|
||||
element = jqLite('<ng:include src="url" scope="childScope"></ng:include>');
|
||||
element = $compile(element)($rootScope);
|
||||
$rootScope.childScope = $rootScope.$new();
|
||||
$rootScope.childScope.name = 'igor';
|
||||
|
|
@ -97,7 +103,7 @@ describe('widget', function() {
|
|||
|
||||
it('should allow this for scope', inject(putIntoCache('myUrl', '{{"abc"}}'),
|
||||
function($rootScope, $compile, $browser) {
|
||||
var element = jqLite('<ng:include src="url" scope="this"></ng:include>');
|
||||
element = jqLite('<ng:include src="url" scope="this"></ng:include>');
|
||||
element = $compile(element)($rootScope);
|
||||
$rootScope.url = 'myUrl';
|
||||
$rootScope.$digest();
|
||||
|
|
@ -115,7 +121,7 @@ describe('widget', function() {
|
|||
it('should evaluate onload expression when a partial is loaded', inject(
|
||||
putIntoCache('myUrl', 'my partial'),
|
||||
function($rootScope, $compile, $browser) {
|
||||
var element = jqLite('<ng:include src="url" onload="loaded = true"></ng:include>');
|
||||
element = jqLite('<ng:include src="url" onload="loaded = true"></ng:include>');
|
||||
element = $compile(element)($rootScope);
|
||||
|
||||
expect($rootScope.loaded).not.toBeDefined();
|
||||
|
|
@ -130,7 +136,7 @@ describe('widget', function() {
|
|||
|
||||
it('should destroy old scope', inject(putIntoCache('myUrl', 'my partial'),
|
||||
function($rootScope, $compile, $browser) {
|
||||
var element = jqLite('<ng:include src="url"></ng:include>');
|
||||
element = jqLite('<ng:include src="url"></ng:include>');
|
||||
element = $compile(element)($rootScope);
|
||||
|
||||
expect($rootScope.$$childHead).toBeFalsy();
|
||||
|
|
@ -147,7 +153,7 @@ describe('widget', function() {
|
|||
|
||||
it('should do xhr request and cache it',
|
||||
inject(function($rootScope, $httpBackend, $compile, $browser) {
|
||||
var element = $compile('<ng:include src="url"></ng:include>')($rootScope);
|
||||
element = $compile('<ng:include src="url"></ng:include>')($rootScope);
|
||||
$httpBackend.expect('GET', 'myUrl').respond('my partial');
|
||||
|
||||
$rootScope.url = 'myUrl';
|
||||
|
|
@ -168,7 +174,7 @@ describe('widget', function() {
|
|||
|
||||
it('should clear content when error during xhr request',
|
||||
inject(function($httpBackend, $compile, $rootScope) {
|
||||
var element = $compile('<ng:include src="url">content</ng:include>')($rootScope);
|
||||
element = $compile('<ng:include src="url">content</ng:include>')($rootScope);
|
||||
$httpBackend.expect('GET', 'myUrl').respond(404, '');
|
||||
|
||||
$rootScope.url = 'myUrl';
|
||||
|
|
@ -182,7 +188,7 @@ describe('widget', function() {
|
|||
it('should be async even if served from cache', inject(
|
||||
putIntoCache('myUrl', 'my partial'),
|
||||
function($rootScope, $compile, $browser) {
|
||||
var element = $compile('<ng:include src="url"></ng:include>')($rootScope);
|
||||
element = $compile('<ng:include src="url"></ng:include>')($rootScope);
|
||||
|
||||
$rootScope.url = 'myUrl';
|
||||
|
||||
|
|
@ -199,8 +205,8 @@ describe('widget', function() {
|
|||
|
||||
it('should discard pending xhr callbacks if a new template is requested before the current ' +
|
||||
'finished loading', inject(function($rootScope, $compile, $httpBackend) {
|
||||
var element = jqLite("<ng:include src='templateUrl'></ng:include>"),
|
||||
log = [];
|
||||
element = jqLite("<ng:include src='templateUrl'></ng:include>");
|
||||
var log = [];
|
||||
|
||||
$rootScope.templateUrl = 'myUrl1';
|
||||
$rootScope.logger = function(msg) {
|
||||
|
|
@ -234,7 +240,7 @@ describe('widget', function() {
|
|||
|
||||
function compileAndLink(tpl) {
|
||||
return function($compile, $rootScope) {
|
||||
$compile(tpl)($rootScope);
|
||||
element = $compile(tpl)($rootScope);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -294,7 +300,7 @@ describe('widget', function() {
|
|||
preventDefaultCalled = false,
|
||||
event;
|
||||
|
||||
var element = $compile('<a href="">empty link</a>')($rootScope);
|
||||
element = $compile('<a href="">empty link</a>')($rootScope);
|
||||
|
||||
if (msie < 9) {
|
||||
|
||||
|
|
@ -327,7 +333,7 @@ describe('widget', function() {
|
|||
|
||||
describe('@ng:repeat', function() {
|
||||
it('should ng:repeat over array', inject(function($rootScope, $compile) {
|
||||
var element = $compile(
|
||||
element = $compile(
|
||||
'<ul>' +
|
||||
'<li ng:repeat="item in items" ng:init="suffix = \';\'" ng:bind="item + suffix"></li>' +
|
||||
'</ul>')($rootScope);
|
||||
|
|
@ -355,7 +361,7 @@ describe('widget', function() {
|
|||
|
||||
|
||||
it('should ng:repeat over object', inject(function($rootScope, $compile) {
|
||||
var element = $compile(
|
||||
element = $compile(
|
||||
'<ul>' +
|
||||
'<li ng:repeat="(key, value) in items" ng:bind="key + \':\' + value + \';\' "></li>' +
|
||||
'</ul>')($rootScope);
|
||||
|
|
@ -370,7 +376,7 @@ describe('widget', function() {
|
|||
Class.prototype.abc = function() {};
|
||||
Class.prototype.value = 'abc';
|
||||
|
||||
var element = $compile(
|
||||
element = $compile(
|
||||
'<ul>' +
|
||||
'<li ng:repeat="(key, value) in items" ng:bind="key + \':\' + value + \';\' "></li>' +
|
||||
'</ul>')($rootScope);
|
||||
|
|
@ -383,7 +389,7 @@ describe('widget', function() {
|
|||
|
||||
it('should error on wrong parsing of ng:repeat', inject(function($rootScope, $compile, $log) {
|
||||
expect(function() {
|
||||
var element = $compile('<ul><li ng:repeat="i dont parse"></li></ul>')($rootScope);
|
||||
element = $compile('<ul><li ng:repeat="i dont parse"></li></ul>')($rootScope);
|
||||
}).toThrow("Expected ng:repeat in form of '_item_ in _collection_' but got 'i dont parse'.");
|
||||
|
||||
$log.error.logs.shift();
|
||||
|
|
@ -392,7 +398,7 @@ describe('widget', function() {
|
|||
|
||||
it('should expose iterator offset as $index when iterating over arrays',
|
||||
inject(function($rootScope, $compile) {
|
||||
var element = $compile(
|
||||
element = $compile(
|
||||
'<ul>' +
|
||||
'<li ng:repeat="item in items" ng:bind="item + $index + \'|\'"></li>' +
|
||||
'</ul>')($rootScope);
|
||||
|
|
@ -404,7 +410,7 @@ describe('widget', function() {
|
|||
|
||||
it('should expose iterator offset as $index when iterating over objects',
|
||||
inject(function($rootScope, $compile) {
|
||||
var element = $compile(
|
||||
element = $compile(
|
||||
'<ul>' +
|
||||
'<li ng:repeat="(key, val) in items" ng:bind="key + \':\' + val + $index + \'|\'"></li>' +
|
||||
'</ul>')($rootScope);
|
||||
|
|
@ -416,7 +422,7 @@ describe('widget', function() {
|
|||
|
||||
it('should expose iterator position as $position when iterating over arrays',
|
||||
inject(function($rootScope, $compile) {
|
||||
var element = $compile(
|
||||
element = $compile(
|
||||
'<ul>' +
|
||||
'<li ng:repeat="item in items" ng:bind="item + \':\' + $position + \'|\'"></li>' +
|
||||
'</ul>')($rootScope);
|
||||
|
|
@ -437,7 +443,7 @@ describe('widget', function() {
|
|||
|
||||
it('should expose iterator position as $position when iterating over objects',
|
||||
inject(function($rootScope, $compile) {
|
||||
var element = $compile(
|
||||
element = $compile(
|
||||
'<ul>' +
|
||||
'<li ng:repeat="(key, val) in items" ng:bind="key + \':\' + val + \':\' + $position + \'|\'">' +
|
||||
'</li>' +
|
||||
|
|
@ -454,7 +460,7 @@ describe('widget', function() {
|
|||
|
||||
|
||||
it('should ignore $ and $$ properties', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<ul><li ng:repeat="i in items">{{i}}|</li></ul>')($rootScope);
|
||||
element = $compile('<ul><li ng:repeat="i in items">{{i}}|</li></ul>')($rootScope);
|
||||
$rootScope.items = ['a', 'b', 'c'];
|
||||
$rootScope.items.$$hashkey = 'xxx';
|
||||
$rootScope.items.$root = 'yyy';
|
||||
|
|
@ -465,7 +471,7 @@ describe('widget', function() {
|
|||
|
||||
|
||||
it('should repeat over nested arrays', inject(function($rootScope, $compile) {
|
||||
var element = $compile(
|
||||
element = $compile(
|
||||
'<ul>' +
|
||||
'<li ng:repeat="subgroup in groups">' +
|
||||
'<div ng:repeat="group in subgroup">{{group}}|</div>X' +
|
||||
|
|
@ -480,7 +486,7 @@ describe('widget', function() {
|
|||
|
||||
it('should ignore non-array element properties when iterating over an array',
|
||||
inject(function($rootScope, $compile) {
|
||||
var element = $compile('<ul><li ng:repeat="item in array">{{item}}|</li></ul>')($rootScope);
|
||||
element = $compile('<ul><li ng:repeat="item in array">{{item}}|</li></ul>')($rootScope);
|
||||
$rootScope.array = ['a', 'b', 'c'];
|
||||
$rootScope.array.foo = '23';
|
||||
$rootScope.array.bar = function() {};
|
||||
|
|
@ -492,7 +498,7 @@ describe('widget', function() {
|
|||
|
||||
it('should iterate over non-existent elements of a sparse array',
|
||||
inject(function($rootScope, $compile) {
|
||||
var element = $compile('<ul><li ng:repeat="item in array">{{item}}|</li></ul>')($rootScope);
|
||||
element = $compile('<ul><li ng:repeat="item in array">{{item}}|</li></ul>')($rootScope);
|
||||
$rootScope.array = ['a', 'b'];
|
||||
$rootScope.array[4] = 'c';
|
||||
$rootScope.array[6] = 'd';
|
||||
|
|
@ -503,7 +509,7 @@ describe('widget', function() {
|
|||
|
||||
|
||||
it('should iterate over all kinds of types', inject(function($rootScope, $compile) {
|
||||
var element = $compile('<ul><li ng:repeat="item in array">{{item}}|</li></ul>')($rootScope);
|
||||
element = $compile('<ul><li ng:repeat="item in array">{{item}}|</li></ul>')($rootScope);
|
||||
$rootScope.array = ['a', 1, null, undefined, {}];
|
||||
$rootScope.$digest();
|
||||
|
||||
|
|
@ -512,7 +518,7 @@ describe('widget', function() {
|
|||
|
||||
|
||||
describe('stability', function() {
|
||||
var a, b, c, d, lis, element;
|
||||
var a, b, c, d, lis;
|
||||
|
||||
beforeEach(inject(function($rootScope, $compile) {
|
||||
element = $compile(
|
||||
|
|
@ -602,7 +608,7 @@ describe('widget', function() {
|
|||
describe('@ng:non-bindable', function() {
|
||||
it('should prevent compilation of the owning element and its children',
|
||||
inject(function($rootScope, $compile) {
|
||||
var element = $compile('<div ng:non-bindable><span ng:bind="name"></span></div>')($rootScope);
|
||||
element = $compile('<div ng:non-bindable><span ng:bind="name"></span></div>')($rootScope);
|
||||
$rootScope.name = 'misko';
|
||||
$rootScope.$digest();
|
||||
expect(element.text()).toEqual('');
|
||||
|
|
@ -611,7 +617,6 @@ describe('widget', function() {
|
|||
|
||||
|
||||
describe('ng:view', function() {
|
||||
var element;
|
||||
beforeEach(inject(function($rootScope, $compile) {
|
||||
element = $compile('<ng:view></ng:view>')($rootScope);
|
||||
}));
|
||||
|
|
@ -658,7 +663,7 @@ describe('widget', function() {
|
|||
|
||||
$location.path('/unknown');
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$element.text()).toEqual('');
|
||||
expect(element.text()).toEqual('');
|
||||
}));
|
||||
|
||||
|
||||
|
|
@ -675,12 +680,13 @@ describe('widget', function() {
|
|||
|
||||
$rootScope.parentVar = 'new parent';
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.$element.text()).toEqual('new parent');
|
||||
expect(element.text()).toEqual('new parent');
|
||||
}));
|
||||
|
||||
|
||||
it('should be possible to nest ng:view in ng:include', inject(function() {
|
||||
// TODO(vojta): refactor this test
|
||||
dealoc(element);
|
||||
var injector = angular.injector(['ng', 'ngMock']);
|
||||
var myApp = injector.get('$rootScope');
|
||||
var $httpBackend = injector.get('$httpBackend');
|
||||
|
|
@ -690,7 +696,7 @@ describe('widget', function() {
|
|||
var $route = injector.get('$route');
|
||||
$route.when('/foo', {controller: angular.noop, template: 'viewPartial.html'});
|
||||
|
||||
var element = injector.get('$compile')(
|
||||
element = injector.get('$compile')(
|
||||
'<div>' +
|
||||
'include: <ng:include src="\'includePartial.html\'"> </ng:include>' +
|
||||
'</div>')(myApp);
|
||||
|
|
@ -699,15 +705,16 @@ describe('widget', function() {
|
|||
$httpBackend.expect('GET', 'viewPartial.html').respond('content');
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(myApp.$element.text()).toEqual('include: view: content');
|
||||
expect(element.text()).toEqual('include: view: content');
|
||||
expect($route.current.template).toEqual('viewPartial.html');
|
||||
dealoc(myApp);
|
||||
dealoc(element);
|
||||
}));
|
||||
|
||||
|
||||
it('should initialize view template after the view controller was initialized even when ' +
|
||||
'templates were cached',
|
||||
inject(function($rootScope, $compile, $location, $httpBackend, $route, $browser) {
|
||||
inject(function($rootScope, $compile, $location, $httpBackend, $route) {
|
||||
//this is a test for a regression that was introduced by making the ng:view cache sync
|
||||
|
||||
$route.when('/foo', {controller: ParentCtrl, template: 'viewPartial.html'});
|
||||
|
|
@ -750,7 +757,7 @@ describe('widget', function() {
|
|||
$route.when('/foo', {template: 'myUrl1'});
|
||||
$route.when('/bar', {template: 'myUrl2'});
|
||||
|
||||
expect($rootScope.$element.text()).toEqual('');
|
||||
expect(element.text()).toEqual('');
|
||||
|
||||
$location.path('/foo');
|
||||
$httpBackend.expect('GET', 'myUrl1').respond('<div>{{1+3}}</div>');
|
||||
|
|
@ -760,7 +767,7 @@ describe('widget', function() {
|
|||
$rootScope.$digest();
|
||||
$httpBackend.flush(); // now that we have two requests pending, flush!
|
||||
|
||||
expect($rootScope.$element.text()).toEqual('2');
|
||||
expect(element.text()).toEqual('2');
|
||||
}));
|
||||
|
||||
|
||||
|
|
@ -770,12 +777,12 @@ describe('widget', function() {
|
|||
|
||||
$location.path('/foo');
|
||||
$httpBackend.expect('GET', 'myUrl1').respond(404, '');
|
||||
$rootScope.$element.text('content');
|
||||
element.text('content');
|
||||
|
||||
$rootScope.$digest();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect($rootScope.$element.text()).toBe('');
|
||||
expect(element.text()).toBe('');
|
||||
}));
|
||||
|
||||
|
||||
|
|
@ -800,7 +807,6 @@ describe('widget', function() {
|
|||
describe('ng:pluralize', function() {
|
||||
|
||||
describe('deal with pluralized strings without offset', function() {
|
||||
var element;
|
||||
beforeEach(inject(function($rootScope, $compile) {
|
||||
element = $compile(
|
||||
'<ng:pluralize count="email"' +
|
||||
|
|
@ -892,7 +898,7 @@ describe('widget', function() {
|
|||
|
||||
describe('deal with pluralized strings with offset', function() {
|
||||
it('should show single/plural strings with offset', inject(function($rootScope, $compile) {
|
||||
var element = $compile(
|
||||
element = $compile(
|
||||
"<ng:pluralize count=\"viewCount\" offset=2 " +
|
||||
"when=\"{'0': 'Nobody is viewing.'," +
|
||||
"'1': '{{p1}} is viewing.'," +
|
||||
|
|
|
|||
Loading…
Reference in a new issue