1 // ==========================================================================
  2 // Project:   SproutCore - JavaScript Application Framework
  3 // Copyright: ©2006-2011 Strobe Inc. and contributors.
  4 //            Portions ©2008-2011 Apple Inc. All rights reserved.
  5 // License:   Licensed under MIT license (see license.js)
  6 // ==========================================================================
  7 
  8 /**
  9   @namespace
 10 
 11   The Editable mixin is a standard protocol used to activate keyboard editing
 12   on views that are editable such as text fields, label views and item views.
 13 
 14   You should apply this mixin, or implement the methods, if you are
 15   designing an item view for a collection and you want to automatically
 16   trigger editing.
 17 
 18   ## Using Editable Views
 19 
 20   To use a view that includes the Editable mixin, you simply call three
 21   methods on the view:
 22 
 23     - To begin editing, call beginEditing().  This will make the view first responder and allow the user to make changes to it.  If the view cannot begin editing for some reason, it will return NO.
 24     - If you want to cancel editing, you should try calling discardEditing().  This will cause the editor to discard its changed value and resign first responder.  Some editors do not support cancelling editing and will return NO.  If this is the case, you may optionally try calling commitEditing() instead to force the view to resign first responder, even though this will commit the changes.
 25     - If you want to end editing, while saving any changes that were made, try calling commitEditing().  This will cause the editor to validate and apply its changed value and resign first responder.  If the editor cannot validate its contents for some reason, it will return NO.  In this case you may optionally try calling discardEditing() instead to force the view to resign first responder, even though this will discard the changes.
 26 
 27   ## Implementing an Editable View
 28 
 29   To implement a new view that is editable, you should implement the three
 30   methods defined below: beginEditing(), discardEditing(), and
 31   commitEditing().  If you already allow editing when your view becomes first
 32   responder and commit your changes when the view loses first responder status
 33   then you can simply apply this mixin and not override any methods.
 34 
 35 
 36   @since SproutCore 1.0
 37 */
 38 SC.Editable = {
 39 
 40   /**
 41     Indicates whether a view is editable or not.  You can optionally
 42     implement the methods in this mixin to disallow editing is isEditable is
 43     NO.
 44 
 45     @type Boolean
 46     @default NO
 47   */
 48   isEditable: NO,
 49 
 50   /**
 51     Indicates whether editing is currently in progress.  The methods you
 52     implement should generally up this property as appropriate when you
 53     begin and end editing.
 54 
 55     @type Boolean
 56     @default NO
 57   */
 58   isEditing: NO,
 59 
 60   /**
 61     Begins editing on the view.
 62 
 63     This method is called by other views when they want you to begin editing.
 64     You should write this method to become first responder, perform any
 65     additional setup needed to begin editing and then return YES.
 66 
 67     If for some reason you do not want to allow editing right now, you can
 68     also return NO.  If your view is already editing, then you should not
 69     restart editing again but just return YES.
 70 
 71     The default implementation checks to see if editing is allowed, then
 72     becomes first responder and updates the isEditing property if appropriate.
 73     Generally you will want to replace this method with your own
 74     implementation and not call the default.
 75 
 76     @returns {Boolean} YES if editing began or is in progress, NO otherwise
 77   */
 78   beginEditing: function() {
 79     if (!this.get('isEditable')) return NO ;
 80     if (this.get('isEditing')) return YES ;
 81 
 82     // begin editing
 83     this.beginPropertyChanges();
 84     this.set('isEditing', YES) ;
 85     this.becomeFirstResponder();
 86     this.endPropertyChanges();
 87 
 88     return YES ;
 89   },
 90 
 91   /**
 92     Ends editing on the view, discarding any changes that were made to the
 93     view value in the meantime.
 94 
 95     This method is called by other views when they want to cancel editing
 96     that began earlier.  When this method is called you should resign first
 97     responder, restore the original value of the view and return YES.
 98 
 99     If your view cannot revert back to its original state before editing began
100     then you can implement this method to simply return NO.  A properly
101     implemented client may try to call commitEditing() instead to force your
102     view to end editing anyway.
103 
104     If this method is called on a view that is not currently editing, you
105     should always just return YES.
106 
107     The default implementation does not support discarding changes and always
108     returns NO.
109 
110     @returns {Boolean} YES if changes were discarded and editing ended.
111   */
112   discardEditing: function() {
113     // if we are not editing, return YES, otherwise NO.
114 
115     return !this.get('isEditing') ;
116   },
117 
118   /**
119     Ends editing on the view, committing any changes that were made to the
120     view value in the meantime.
121 
122     This method is called by other views when they want to end editing,
123     saving any changes that were made to the view in the meantime.  When this
124     method is called you should resign first responder, save the latest
125     value of the view and return YES.
126 
127     If your view cannot save the current state of the view for some reason
128     (for example if validation fails), then you should return NO.  Properly
129     implemented clients may then try to call discardEditing() to force your
130     view to resign first responder anyway.
131 
132     Some views apply changes to their value immediately during an edit instead
133     of waiting for the view to end editing.  If this is the case, you should
134     still implement commitEditing but you simply may not save any value
135     changes.
136 
137     If this method is called on a view that is not currently editing, you
138     should always just return YES.
139 
140     The default implementation sets isEditing to NO, resigns first responder
141     and returns YES.
142 
143     @returns {Boolean} YES if changes were discarded and editing ended.
144   */
145   commitEditing: function() {
146     if (!this.get('isEditing')) return YES;
147     this.set('isEditing', NO) ;
148     this.resignFirstResponder();
149 
150     return YES ;
151   }
152 
153 } ;
154