1 // ========================================================================== 2 // Project: SproutCore - JavaScript Application Framework 3 // Copyright: ©2008-2011 Apple Inc. All rights reserved. 4 // License: Licensed under MIT license (see license.js) 5 // ========================================================================== 6 7 /** 8 @namespace 9 A view is empty if all of its children are empty. A view is automatically 10 counted as empty if it is not visible, and not empty if it is being edited. 11 12 Any field that does not mix in CalculatesEmptiness will be considered empty. 13 */ 14 SC.CalculatesEmptiness = { 15 16 hasCalculatesEmptiness: YES, 17 18 /** 19 YES if the value of the field is empty. Defaults to yes so if you don't override this, it will only consider child fields in emptiness calculation (this is the desired behavior for forms). 20 */ 21 isValueEmpty: YES, 22 23 /** 24 Defaults to YES so that a field with no children will act properly. 25 */ 26 _scce_childrenAreEmpty: YES, 27 28 /** 29 If YES, all visible fields are considered non-empty when editing. 30 @type Boolean 31 @default YES 32 */ 33 isEditingAffectsIsEmpty: YES, 34 35 36 _scce_isEditingDidChange: function() { 37 if(this.get('isEditingAffectsIsEmpty')) { 38 this.notifyPropertyChange('isEmpty'); 39 } 40 }.observes('isEditing'), 41 42 /** 43 YES if the field itself is empty. Even if the value is non-empty, the field can be empty due to isVisible. 44 */ 45 isEmpty: function() { 46 // When not visible, it is empty. Period. 47 if (!this.get('isVisible')) { 48 return YES; 49 } 50 51 // if it is editing and edit mode affects emptiness, it is NOT empty. 52 if (this.get('isEditingAffectsIsEmpty') && this.get('isEditing')) { 53 return NO; 54 } 55 56 // otherwise, it is empty if its value AND all children are empty. 57 return this.get('isValueEmpty') && this.get('_scce_childrenAreEmpty'); 58 }.property('isValueEmpty', 'isVisible', '_scce_childrenAreEmpty', 'isEditingAffectsIsEmpty').cacheable(), 59 60 /** 61 When emptiness changes tell the parent to re-check its own emptiness. 62 */ 63 _scce_isEmptyDidChange: function() { 64 var parentView = this.get('parentView'); 65 66 if (parentView && parentView._scce_emptinessDidChangeFor) { 67 parentView._scce_emptinessDidChangeFor(this); 68 } 69 }.observes('isEmpty'), 70 71 initMixin: function() { 72 this._scce_emptinessDidChangeFor(); 73 }, 74 75 /** 76 Called by fields when their emptiness changes. 77 78 Always triggers (at end of run loop) a relayout of fields. 79 */ 80 _scce_emptinessDidChangeFor: function(child) { 81 this.invokeOnce('_scce_recalculateChildrensEmptiness'); 82 }, 83 84 /** 85 By default, a view will check all of its fields to determine if it is empty. It is only empty if all of its value fields are. 86 */ 87 _scce_recalculateChildrensEmptiness: function() 88 { 89 // in short, we get the value fields, if we come across one that is not empty 90 // we cannot be empty. 91 var views = this.get('childViews'); 92 93 var empty = YES, 94 len = views.length, 95 field; 96 97 for (var i = 0; i < len; i++) 98 { 99 field = views[i]; 100 101 if (!field.get('isEmpty') && field.hasCalculatesEmptiness) { 102 empty = NO; 103 break; 104 } 105 } 106 107 this.setIfChanged('_scce_childrenAreEmpty', empty); 108 } 109 }; 110 111