aboutsummaryrefslogtreecommitdiffstats
path: root/src/ng/compile.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/ng/compile.js')
-rw-r--r--src/ng/compile.js132
1 files changed, 85 insertions, 47 deletions
diff --git a/src/ng/compile.js b/src/ng/compile.js
index ce3d0514..d977f173 100644
--- a/src/ng/compile.js
+++ b/src/ng/compile.js
@@ -673,6 +673,24 @@ function $CompileProvider($provide) {
},
/**
+ * @ngdoc function
+ * @name ng.$compile.directive.Attributes#$updateClass
+ * @methodOf ng.$compile.directive.Attributes
+ * @function
+ *
+ * @description
+ * Adds and removes the appropriate CSS class values to the element based on the difference
+ * between the new and old CSS class values (specified as newClasses and oldClasses).
+ *
+ * @param {string} newClasses The current CSS className value
+ * @param {string} oldClasses The former CSS className value
+ */
+ $updateClass : function(newClasses, oldClasses) {
+ this.$removeClass(tokenDifference(oldClasses, newClasses));
+ this.$addClass(tokenDifference(newClasses, oldClasses));
+ },
+
+ /**
* Set a normalized attribute on the element in a way such that all directives
* can share the attribute. This function properly handles boolean attributes.
* @param {string} key Normalized key. (ie ngAttribute)
@@ -682,59 +700,53 @@ function $CompileProvider($provide) {
* @param {string=} attrName Optional none normalized name. Defaults to key.
*/
$set: function(key, value, writeAttr, attrName) {
- //special case for class attribute addition + removal
- //so that class changes can tap into the animation
- //hooks provided by the $animate service
- if(key == 'class') {
- value = value || '';
- var current = this.$$element.attr('class') || '';
- this.$removeClass(tokenDifference(current, value));
- this.$addClass(tokenDifference(value, current));
- } else {
- var booleanKey = getBooleanAttrName(this.$$element[0], key),
- normalizedVal,
- nodeName;
+ // TODO: decide whether or not to throw an error if "class"
+ //is set through this function since it may cause $updateClass to
+ //become unstable.
- if (booleanKey) {
- this.$$element.prop(key, value);
- attrName = booleanKey;
- }
+ var booleanKey = getBooleanAttrName(this.$$element[0], key),
+ normalizedVal,
+ nodeName;
- this[key] = value;
+ if (booleanKey) {
+ this.$$element.prop(key, value);
+ attrName = booleanKey;
+ }
- // translate normalized key to actual key
- if (attrName) {
- this.$attr[key] = attrName;
- } else {
- attrName = this.$attr[key];
- if (!attrName) {
- this.$attr[key] = attrName = snake_case(key, '-');
- }
+ this[key] = value;
+
+ // translate normalized key to actual key
+ if (attrName) {
+ this.$attr[key] = attrName;
+ } else {
+ attrName = this.$attr[key];
+ if (!attrName) {
+ this.$attr[key] = attrName = snake_case(key, '-');
}
+ }
- nodeName = nodeName_(this.$$element);
-
- // sanitize a[href] and img[src] values
- if ((nodeName === 'A' && key === 'href') ||
- (nodeName === 'IMG' && key === 'src')) {
- // NOTE: urlResolve() doesn't support IE < 8 so we don't sanitize for that case.
- if (!msie || msie >= 8 ) {
- normalizedVal = urlResolve(value).href;
- if (normalizedVal !== '') {
- if ((key === 'href' && !normalizedVal.match(aHrefSanitizationWhitelist)) ||
- (key === 'src' && !normalizedVal.match(imgSrcSanitizationWhitelist))) {
- this[key] = value = 'unsafe:' + normalizedVal;
- }
+ nodeName = nodeName_(this.$$element);
+
+ // sanitize a[href] and img[src] values
+ if ((nodeName === 'A' && key === 'href') ||
+ (nodeName === 'IMG' && key === 'src')) {
+ // NOTE: urlResolve() doesn't support IE < 8 so we don't sanitize for that case.
+ if (!msie || msie >= 8 ) {
+ normalizedVal = urlResolve(value).href;
+ if (normalizedVal !== '') {
+ if ((key === 'href' && !normalizedVal.match(aHrefSanitizationWhitelist)) ||
+ (key === 'src' && !normalizedVal.match(imgSrcSanitizationWhitelist))) {
+ this[key] = value = 'unsafe:' + normalizedVal;
}
}
}
+ }
- if (writeAttr !== false) {
- if (value === null || value === undefined) {
- this.$$element.removeAttr(attrName);
- } else {
- this.$$element.attr(attrName, value);
- }
+ if (writeAttr !== false) {
+ if (value === null || value === undefined) {
+ this.$$element.removeAttr(attrName);
+ } else {
+ this.$$element.attr(attrName, value);
}
}
@@ -1816,9 +1828,19 @@ function $CompileProvider($provide) {
attr[name] = interpolateFn(scope);
($$observers[name] || ($$observers[name] = [])).$$inter = true;
(attr.$$observers && attr.$$observers[name].$$scope || scope).
- $watch(interpolateFn, function interpolateFnWatchAction(value) {
- attr.$set(name, value);
- });
+ $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
+ //special case for class attribute addition + removal
+ //so that class changes can tap into the animation
+ //hooks provided by the $animate service. Be sure to
+ //skip animations when the first digest occurs (when
+ //both the new and the old values are the same) since
+ //the CSS classes are the non-interpolated values
+ if(name === 'class' && newValue != oldValue) {
+ attr.$updateClass(newValue, oldValue);
+ } else {
+ attr.$set(name, newValue);
+ }
+ });
}
};
}
@@ -1958,3 +1980,19 @@ function directiveLinkingFn(
/* Element */ rootElement,
/* function(Function) */ boundTranscludeFn
){}
+
+function tokenDifference(str1, str2) {
+ var values = '',
+ tokens1 = str1.split(/\s+/),
+ tokens2 = str2.split(/\s+/);
+
+ outer:
+ for(var i = 0; i < tokens1.length; i++) {
+ var token = tokens1[i];
+ for(var j = 0; j < tokens2.length; j++) {
+ if(token == tokens2[j]) continue outer;
+ }
+ values += (values.length > 0 ? ' ' : '') + token;
+ }
+ return values;
+}