diff options
Diffstat (limited to 'src/ng')
| -rw-r--r-- | src/ng/compile.js | 132 | ||||
| -rw-r--r-- | src/ng/directive/ngClass.js | 35 |
2 files changed, 93 insertions, 74 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; +} diff --git a/src/ng/directive/ngClass.js b/src/ng/directive/ngClass.js index 10ef7fd1..21316c57 100644 --- a/src/ng/directive/ngClass.js +++ b/src/ng/directive/ngClass.js @@ -20,11 +20,10 @@ function classDirective(name, selector) { // jshint bitwise: false var mod = $index & 1; if (mod !== old$index & 1) { - if (mod === selector) { - addClass(flattenClasses(scope.$eval(attr[name]))); - } else { - removeClass(flattenClasses(scope.$eval(attr[name]))); - } + var classes = flattenClasses(scope.$eval(attr[name])); + mod === selector ? + attr.$addClass(classes) : + attr.$removeClass(classes); } }); } @@ -33,34 +32,16 @@ function classDirective(name, selector) { function ngClassWatchAction(newVal) { if (selector === true || scope.$index % 2 === selector) { var newClasses = flattenClasses(newVal || ''); - if (oldVal && !equals(newVal,oldVal)) { - var oldClasses = flattenClasses(oldVal); - var toRemove = tokenDifference(oldClasses, newClasses); - if(toRemove.length > 0) { - removeClass(toRemove); - } - - var toAdd = tokenDifference(newClasses, oldClasses); - if(toAdd.length > 0) { - addClass(toAdd); - } - } else { - addClass(newClasses); + if(!oldVal) { + attr.$addClass(newClasses); + } else if(!equals(newVal,oldVal)) { + attr.$updateClass(newClasses, flattenClasses(oldVal)); } } oldVal = copy(newVal); } - function removeClass(classVal) { - attr.$removeClass(classVal); - } - - - function addClass(classVal) { - attr.$addClass(classVal); - } - function flattenClasses(classVal) { if(isArray(classVal)) { return classVal.join(' '); |
