sproutcore 0.9.2 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. data/History.txt +33 -0
  2. data/Manifest.txt +3 -14
  3. data/clients/sc_docs/controllers/docs.js +1 -0
  4. data/clients/sc_docs/core.js +1 -1
  5. data/clients/sc_test_runner/controllers/runner.js +5 -0
  6. data/clients/sc_test_runner/core.js +1 -1
  7. data/frameworks/sproutcore/english.lproj/core.css +41 -0
  8. data/frameworks/sproutcore/english.lproj/theme.css +20 -0
  9. data/frameworks/sproutcore/foundation/animator.js +11 -2
  10. data/frameworks/sproutcore/foundation/date.js +2 -2
  11. data/frameworks/sproutcore/foundation/object.js +2 -2
  12. data/frameworks/sproutcore/foundation/server.js +4 -3
  13. data/frameworks/sproutcore/foundation/set.js +1 -1
  14. data/frameworks/sproutcore/foundation/unittest.js +12 -9
  15. data/frameworks/sproutcore/lib/collection_view.rb +1 -0
  16. data/frameworks/sproutcore/lib/core_views.rb +4 -0
  17. data/frameworks/sproutcore/mixins/editable.js +144 -0
  18. data/frameworks/sproutcore/mixins/inline_editor_delegate.js +72 -0
  19. data/frameworks/sproutcore/mixins/observable.js +45 -16
  20. data/frameworks/sproutcore/mixins/scrollable.js +0 -1
  21. data/frameworks/sproutcore/tests/controllers/controller.rhtml +12 -12
  22. data/frameworks/sproutcore/tests/controllers/object.rhtml +2 -2
  23. data/frameworks/sproutcore/views/collection/collection.js +122 -68
  24. data/frameworks/sproutcore/views/collection/source_list.js +5 -0
  25. data/frameworks/sproutcore/views/field/text_field.js +7 -1
  26. data/frameworks/sproutcore/views/inline_text_field.js +397 -0
  27. data/frameworks/sproutcore/views/label.js +78 -68
  28. data/frameworks/sproutcore/views/list_item.js +184 -31
  29. data/frameworks/sproutcore/views/view.js +41 -9
  30. data/generators/client/templates/core.js +1 -1
  31. data/generators/client/templates/english.lproj/body.css +74 -0
  32. data/generators/framework/templates/core.js +1 -1
  33. data/lib/sproutcore/version.rb +1 -1
  34. metadata +5 -16
  35. data/clients/view_builder/builders/builder.js +0 -339
  36. data/clients/view_builder/builders/button.js +0 -81
  37. data/clients/view_builder/controllers/document.js +0 -21
  38. data/clients/view_builder/core.js +0 -19
  39. data/clients/view_builder/english.lproj/body.css +0 -77
  40. data/clients/view_builder/english.lproj/body.rhtml +0 -41
  41. data/clients/view_builder/english.lproj/controls.css +0 -0
  42. data/clients/view_builder/english.lproj/strings.js +0 -14
  43. data/clients/view_builder/main.js +0 -38
  44. data/clients/view_builder/tests/controllers/document.rhtml +0 -20
  45. data/clients/view_builder/tests/views/builder.rhtml +0 -20
  46. data/clients/view_builder/views/builder.js +0 -23
  47. data/frameworks/sproutcore/english.lproj/inline_text_editor.css +0 -21
  48. data/frameworks/sproutcore/views/inline_text_editor.js +0 -96
@@ -5,6 +5,7 @@
5
5
  require('Core') ;
6
6
  require('views/view') ;
7
7
  require('mixins/control');
8
+ require('mixins/inline_editor_delegate');
8
9
 
9
10
  SC.LIST_ITEM_ACTION_CANCEL = 'sc-list-item-cancel-action';
10
11
  SC.LIST_ITEM_ACTION_REFRESH = 'sc-list-item-cancel-refresh';
@@ -16,10 +17,12 @@ SC.LIST_ITEM_ACTION_EJECT = 'sc-list-item-cancel-eject';
16
17
 
17
18
  @extends SC.View
18
19
  @extends SC.Control
19
- @author AuthorName
20
- @version 0.1
20
+ @extends SC.InlineEditorDelegate
21
+ @extends SC.Editable
22
+ @author Charles Jolley
23
+ @since SproutCore 1.0
21
24
  */
22
- SC.ListItemView = SC.View.extend(SC.Control,
25
+ SC.ListItemView = SC.View.extend(SC.Control, SC.InlineEditorDelegate,
23
26
  /** @scope SC.ListItemView.prototype */ {
24
27
 
25
28
  /** A ListItemView has an img tag, label, count, optional right button, and
@@ -83,8 +86,13 @@ SC.ListItemView = SC.View.extend(SC.Control,
83
86
  */
84
87
  contentIsBranchKey: null,
85
88
 
89
+ /**
90
+ YES if the item view is currently editing.
91
+ */
92
+ isEditing: NO,
86
93
 
87
94
  contentPropertyDidChange: function() {
95
+ if (this.get('isEditing')) this.discardEditing() ;
88
96
  this.render() ;
89
97
  },
90
98
 
@@ -101,26 +109,26 @@ SC.ListItemView = SC.View.extend(SC.Control,
101
109
  if (this.getDelegateProperty(del, 'hasContentIcon')) {
102
110
  var iconKey = this.getDelegateProperty(del,'contentIconKey') ;
103
111
  var icon = (iconKey && content && content.get) ? content.get(iconKey) : null ;
104
- html.push(this.renderIconHTML(icon));
112
+ html.push(this.renderIconHtml(icon));
105
113
  }
106
114
 
107
115
  // handle label
108
116
  var labelKey = this.getDelegateProperty(del, 'contentValueKey') ;
109
117
  var label = (labelKey && content && content.get) ? content.get(labelKey) : null ;
110
- html.push(this.renderLabelHTML(label));
118
+ html.push(this.renderLabelHtml(label));
111
119
 
112
120
  // handle unread count
113
121
  var countKey = this.getDelegateProperty(del, 'contentUnreadCountKey') ;
114
122
  var count = (countKey && content && content.get) ? content.get(countKey) : null ;
115
123
  if ((count != null) && (count != 0)) {
116
- html.push(this.renderCountHTML(count));
124
+ html.push(this.renderCountHtml(count));
117
125
  }
118
126
 
119
127
  // handle action
120
128
  var actionKey = this.getDelegateProperty(del, 'listItemActionProperty') ;
121
129
  var actionClassName = (actionKey && content && content.get) ? content.get(actionKey) : null ;
122
130
  if (actionClassName) {
123
- html.push(this.renderActionHTML(actionClassName));
131
+ html.push(this.renderActionHtml(actionClassName));
124
132
  }
125
133
  this.setClassName('sc-has-action', actionClassName) ;
126
134
 
@@ -128,7 +136,7 @@ SC.ListItemView = SC.View.extend(SC.Control,
128
136
  if (this.getDelegateProperty(del, 'hasContentBranch')) {
129
137
  var branchKey = this.getDelegateProperty(del, 'contentIsBranchKey');
130
138
  var hasBranch = (branchKey && content && content.get) ? content.get(branchKey) : false ;
131
- html.push(this.renderBranchHTML(hasBranch));
139
+ html.push(this.renderBranchHtml(hasBranch));
132
140
  this.setClassName('sc-has-branch', true) ;
133
141
  } else this.setClassName('sc-has-branch', false) ;
134
142
 
@@ -140,12 +148,13 @@ SC.ListItemView = SC.View.extend(SC.Control,
140
148
  },
141
149
 
142
150
  /**
143
- renderIconHTML generates the html string used to represent the icon for your
144
- list item. override this to return your own custom HTML
151
+ renderIconHtml generates the html string used to represent the icon for
152
+ your list item. override this to return your own custom HTML
153
+
145
154
  @returns {String}
146
155
  @arguments {String} the icon property based on your view's contentIconKey
147
156
  */
148
- renderIconHTML: function(icon){
157
+ renderIconHtml: function(icon){
149
158
  var html = [];
150
159
  // get a class name and url to include if relevant
151
160
  var url = null, className = null ;
@@ -164,12 +173,14 @@ SC.ListItemView = SC.View.extend(SC.Control,
164
173
  },
165
174
 
166
175
  /**
167
- renderLabelHTML generates the html string used to represent the label for your
168
- list item. override this to return your own custom HTML
176
+ renderLabelHtml generates the html string used to represent the label
177
+ for your list item. override this to return your own custom HTML
178
+
169
179
  @returns {String}
170
- @arguments {String} the label property based on your view's contentValueKey
180
+ @arguments {String} the label property based on your view's
181
+ contentValueKey
171
182
  */
172
- renderLabelHTML: function(label){
183
+ renderLabelHtml: function(label){
173
184
  var html = [];
174
185
  html.push('<span class="sc-label">') ;
175
186
  html.push(label || '') ;
@@ -177,13 +188,25 @@ SC.ListItemView = SC.View.extend(SC.Control,
177
188
  return html.join('');
178
189
  },
179
190
 
191
+ /**
192
+ Finds and retrieves the element containing the label. This is used
193
+ for inline editing. If you override renderLabelHtml() you probably
194
+ need to override this as well.
195
+ */
196
+ findLabelElement: function() {
197
+ return this.$class('sc-label') ;
198
+ },
199
+
180
200
  /**
181
- renderCountHTML generates the html string used to represent the count (like unread count)
182
- for your list item. override this to return your own custom HTML
201
+ renderCountHtml generates the html string used to represent the count
202
+ (like unread count) for your list item. override this to return your
203
+ own custom HTML
204
+
183
205
  @returns {String}
184
- @arguments {Integer} the label property based on your view's contentValueKey
206
+ @arguments {Integer} the label property based on your view's
207
+ contentValueKey
185
208
  */
186
- renderCountHTML: function(count) {
209
+ renderCountHtml: function(count) {
187
210
  var html= [];
188
211
  html.push('<span class="sc-count"><span class="inner">') ;
189
212
  html.push(count.toString()) ;
@@ -192,12 +215,14 @@ SC.ListItemView = SC.View.extend(SC.Control,
192
215
  },
193
216
 
194
217
  /**
195
- renderActiontHTML generates the html string used to represent the action item
196
- for your list item. override this to return your own custom HTML
197
- @returns {String}
198
- @arguments {String} the name of the action item.
218
+ renderActionHtml generates the html string used to represent the
219
+ action item for your list item. override this to return your own
220
+ custom HTML
221
+
222
+ @returns {String}
223
+ @param actionClassName {String} the name of the action item.
199
224
  */
200
- renderActionHTML: function(actionClassName){
225
+ renderActionHtml: function(actionClassName){
201
226
  var html = [];
202
227
  html.push('<img src="') ;
203
228
  html.push(static_url('blank.gif')) ;
@@ -206,20 +231,148 @@ SC.ListItemView = SC.View.extend(SC.Control,
206
231
  },
207
232
 
208
233
  /**
209
- renderBranchHTML generates the html string used to represent the branch arrow.
210
- override this to return your own custom HTML
211
- @returns {String}
212
- @arguments {Boolean} whehter the branch is
234
+ renderBranchHtml generates the html string used to represent the
235
+ branch arrow. override this to return your own custom HTML
236
+ @returns {String}
237
+ @arguments {Boolean} whehter the branch is
213
238
  */
214
239
 
215
- renderBranchHTML: function(hasBranch) {
240
+ renderBranchHtml: function(hasBranch) {
216
241
  var html = [];
217
242
  html.push('<span class="sc-branch ');
218
243
  html.push(hasBranch ? 'sc-branch-visible' : 'sc-branch-hidden') ;
219
244
  html.push('">&nbsp;</span>');
220
245
  return html.join('');
221
- }
222
-
246
+ },
247
+
248
+ /**
249
+ Returns true if a click is on the label text itself to enable editing.
250
+
251
+ Note that if you override renderLabelHtml(), you probably need to override
252
+ this as well.
223
253
 
254
+ @param evt {Event} the mouseUp event.
255
+ @returns {Boolean} YES if the mouse was on the content element itself.
256
+ */
257
+ contentHitTest: function(evt) {
258
+
259
+ // if not content value is returned, not much to do.
260
+ var del = this.displayDelegate ;
261
+ var labelKey = this.getDelegateProperty(del, 'contentValueKey') ;
262
+ if (!labelKey) return NO ;
263
+
264
+ // get the element to check for.
265
+ var el = this.findLabelElement() ;
266
+ if (!el) return NO ; // no label to check for.
267
+
268
+ var cur = Event.element(evt) ;
269
+ while(cur && (cur != (this.rootElement)) && (cur != window)) {
270
+ if (cur === el) return YES ;
271
+ cur = cur.parentNode ;
272
+ }
273
+
274
+ return NO;
275
+ },
276
+
277
+ beginEditing: function() {
278
+
279
+ if (this.get('isEditing')) return YES ;
280
+
281
+ var content = this.get('content') ;
282
+ var del = this.displayDelegate ;
283
+ var labelKey = this.getDelegateProperty(del, 'contentValueKey') ;
284
+ var v = (labelKey && content && content.get) ? content.get(labelKey) : null ;
285
+
286
+ var f = this.get('frame') ;
287
+ var el = this.findLabelElement() ;
288
+ if (!el) return NO ;
289
+
290
+ // if the label has a large line height, try to adjust it to something
291
+ // more reasonable so that it looks right when we show the popup editor.
292
+ var oldLineHeight = Element.getStyle(el, 'lineHeight') ;
293
+ var fontSize = parseInt(Element.getStyle(el, 'fontSize'), 0) ;
294
+ var lineHeight = parseInt(oldLineHeight, 0) ;
295
+ var lineHeightShift = 0;
296
+
297
+ if (fontSize && lineHeight) {
298
+ var targetLineHeight = fontSize * 1.5 ;
299
+ if (targetLineHeight < lineHeight) {
300
+ Element.setStyle(el, { lineHeight: '1.5' }) ;
301
+ lineHeightShift = (lineHeight - targetLineHeight) / 2;
302
+ } else oldLineHeight = null ;
303
+ }
304
+
305
+ f.x += el.offsetLeft ;
306
+ f.y += el.offsetTop + lineHeightShift - 2;
307
+ f.height = el.offsetHeight ;
308
+ f.width = (f.width - 30 - el.offsetLeft) ;
309
+ f = this.convertFrameToView(f, null) ;
310
+
311
+ var ret = SC.InlineTextFieldView.beginEditing({
312
+ frame: f,
313
+ exampleElement: el,
314
+ delegate: this,
315
+ value: v
316
+ }) ;
317
+
318
+ // restore old line height for original item if the old line height
319
+ // was saved.
320
+ if (oldLineHeight) Element.setStyle(el, { lineHeight: oldLineHeight }) ;
321
+
322
+ // Done! If this failed, then set editing back to no.
323
+ return ret ;
324
+ },
325
+
326
+ commitEditing: function() {
327
+ if (!this.get('isEditing')) return YES ;
328
+ return SC.InlineTextFieldView.commitEditing();
329
+ },
330
+
331
+ discardEditing: function() {
332
+ if (!this.get('isEditing')) return YES ;
333
+ return SC.InlineTextFieldView.discardEditing();
334
+ },
335
+
336
+ /** @private
337
+ Set editing to true so edits will no longer be allowed.
338
+ */
339
+ inlineEditorWillBeginEditing: function(inlineEditor) {
340
+ this.set('isEditing', YES);
341
+ },
342
+
343
+ /** @private
344
+ Hide the label view while the inline editor covers it.
345
+ */
346
+ inlineEditorDidBeginEditing: function(inlineEditor) {
347
+ var el = this.findLabelElement() ;
348
+ this._oldOpacity = Element.getStyle(el, 'opacity') ;
349
+ Element.setStyle(el, { opacity: 0.0 }) ;
350
+ },
351
+
352
+ /** @private
353
+ Could check with a validator someday...
354
+ */
355
+ inlineEditorShouldEndEditing: function(inlineEditor, finalValue) {
356
+ return YES ;
357
+ },
358
+
359
+ /** @private
360
+ Update the field value and make it visible again.
361
+ */
362
+ inlineEditorDidEndEditing: function(inlineEditor, finalValue) {
363
+ this.set('isEditing', NO) ;
364
+
365
+ var content = this.get('content') ;
366
+ var del = this.displayDelegate ;
367
+ var labelKey = this.getDelegateProperty(del, 'contentValueKey') ;
368
+ if (labelKey && content && content.set) {
369
+ content.set(labelKey, finalValue) ;
370
+ }
371
+
372
+ // force a refresh, otherwise the label will never be visible again
373
+ // b/c its opacity is 0.
374
+ this._lastRenderedHtml = null;
375
+ this.render() ;
376
+ }
224
377
 
225
378
  }) ;
@@ -525,22 +525,16 @@ SC.View = SC.Responder.extend(SC.PathModule, SC.DelegateSupport,
525
525
  }
526
526
 
527
527
  //if (style == 'float') style = 'cssFloat' ;
528
- style = (style == 'float') ? 'cssFloat' : style.camelize() ;
528
+ style = (style === 'float') ? 'cssFloat' : style.camelize() ;
529
529
  var value = element.style[style];
530
530
  if (!value) {
531
531
  value = this._computedStyle ? this._computedStyle[style] : null ;
532
532
  }
533
533
 
534
- switch(style) {
535
- case 'opacity':
534
+ if (style === 'opacity') {
536
535
  value = value ? parseFloat(value) : 1.0;
537
- break ;
538
- case 'auto':
539
- value = null;
540
- break ;
541
- default:
542
- break ;
543
536
  }
537
+ if (value === 'auto') value = null ;
544
538
 
545
539
  return value ;
546
540
  },
@@ -2059,6 +2053,44 @@ SC.View.mixin({
2059
2053
 
2060
2054
  }) ;
2061
2055
 
2056
+ // IE Specfic Overrides
2057
+ console.log('SC.Platform.IE = %@'.fmt(SC.Platform.IE));
2058
+ if (SC.Platform.IE) {
2059
+ SC.View.prototype.getStyle = function(style) {
2060
+ var element = this.rootElement ;
2061
+
2062
+ // collect value
2063
+ style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
2064
+ var value = element.style[style];
2065
+ if (!value && element.currentStyle) value = element.currentStyle[style];
2066
+
2067
+ // handle opacity
2068
+ if (style === 'opacity') {
2069
+ if (value = (this.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/)) {
2070
+ if (value[1]) value = parseFloat(value[1]) / 100;
2071
+ }
2072
+ value = 1.0;
2073
+ }
2074
+
2075
+ // handle auto
2076
+ if (value === 'auto') {
2077
+ switch(style) {
2078
+ case 'width':
2079
+ value = (this.getStyle('display') != 'none') ? (element.offsetWidth + 'px') : null;
2080
+ break ;
2081
+ case 'height':
2082
+ value = (this.getStyle('display') != 'none') ? (element.offsetHeight + 'px') : null;
2083
+ break ;
2084
+ default:
2085
+ value = null ;
2086
+ }
2087
+ }
2088
+
2089
+ return value;
2090
+ };
2091
+ }
2092
+
2093
+
2062
2094
  // this handler goes through the guid to avoid any potential memory leaks
2063
2095
  SC.View._onscroll = function(evt) { $view(this)._onscroll(evt); } ;
2064
2096
 
@@ -14,6 +14,6 @@
14
14
  FIXTURES: [],
15
15
 
16
16
  // Any keys in this array will be instantiated automatically from main.
17
- controllers: [],
17
+ controllers: []
18
18
 
19
19
  }) ;
@@ -73,3 +73,77 @@
73
73
  }
74
74
 
75
75
  /* @end */
76
+
77
+ /* @group For Tutorial */
78
+
79
+ body {
80
+ position: absolute ;
81
+ left: 0;
82
+ right: 0;
83
+ top: 0;
84
+ bottom: 0;
85
+ padding: 0;
86
+ margin: 0;
87
+ overflow: hidden ;
88
+ }
89
+
90
+ .workspace .sidebar {
91
+ position: absolute;
92
+ left: 0;
93
+ top: 0;
94
+ width: 200px;
95
+ bottom: 0;
96
+ }
97
+
98
+ .workspace .sidebar .master_list {
99
+ position: absolute;
100
+ left: 0;
101
+ top: 0;
102
+ right: 0;
103
+ bottom: 0;
104
+ }
105
+
106
+ .workspace .sc-split-divider-view {
107
+ position: absolute;
108
+ left: 200px;
109
+ top: 0;
110
+ bottom: 0;
111
+ }
112
+
113
+ .workspace .detail_view {
114
+ position: absolute;
115
+ left: 205px;
116
+ right: 0;
117
+ top: 0;
118
+ bottom: 0;
119
+ border: none ;
120
+ background-color: #aaa;
121
+ }
122
+
123
+ .workspace .detail_view .card-detail {
124
+ margin-left: auto;
125
+ margin-right: auto;
126
+ top: 100px;
127
+ position: relative;
128
+ border: 4px #888 solid;
129
+ background: #f0f0f0;
130
+ padding-top: 10px;
131
+ padding-bottom: 10px;
132
+ }
133
+
134
+ .workspace .detail_view .card-detail td {
135
+ padding: 0 10px;
136
+ }
137
+
138
+ .workspace .detail_view .card-detail .buttons {
139
+ text-align: right;
140
+ padding-top: 10px;
141
+ }
142
+
143
+ .workspace .detail_view .card-detail input {
144
+ font-size: 14px;
145
+ width: 300px;
146
+ }
147
+
148
+ /* @end */
149
+