concrete 0.2.0

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 (128) hide show
  1. data/CHANGELOG +32 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +87 -0
  4. data/concrete/basic_inline_editor.js +73 -0
  5. data/concrete/clipboard.js +72 -0
  6. data/concrete/concrete.js +58 -0
  7. data/concrete/constraint_checker.js +297 -0
  8. data/concrete/editor.js +964 -0
  9. data/concrete/element_extension.js +68 -0
  10. data/concrete/external_identifier_provider.js +112 -0
  11. data/concrete/helper.js +63 -0
  12. data/concrete/identifier_provider.js +168 -0
  13. data/concrete/inline_editor.js +55 -0
  14. data/concrete/metamodel_provider.js +171 -0
  15. data/concrete/model_interface.js +429 -0
  16. data/concrete/scroller.js +106 -0
  17. data/concrete/selector.js +302 -0
  18. data/concrete/template_provider.js +141 -0
  19. data/concrete/ui/abstract_dialog.js +80 -0
  20. data/concrete/ui/concrete_ui.js +28 -0
  21. data/concrete/ui/create_module_dialog.js +55 -0
  22. data/concrete/ui/images/close.png +0 -0
  23. data/concrete/ui/images/document-new.png +0 -0
  24. data/concrete/ui/images/document-save.png +0 -0
  25. data/concrete/ui/images/edit-find-replace.png +0 -0
  26. data/concrete/ui/images/emblem-symbolic-link.png +0 -0
  27. data/concrete/ui/images/help-browser.png +0 -0
  28. data/concrete/ui/images/minus_11px.png +0 -0
  29. data/concrete/ui/images/plus_11px.png +0 -0
  30. data/concrete/ui/images/preferences-system.png +0 -0
  31. data/concrete/ui/images/process-stop.png +0 -0
  32. data/concrete/ui/images/system-search.png +0 -0
  33. data/concrete/ui/layout_manager.js +54 -0
  34. data/concrete/ui/module_browser.js +88 -0
  35. data/concrete/ui/module_editor.js +217 -0
  36. data/concrete/ui/open_element_dialog.js +90 -0
  37. data/concrete/ui/preferences_dialog.js +75 -0
  38. data/concrete/ui/proceed_dialog.js +52 -0
  39. data/concrete/ui/search_replace_dialog.js +323 -0
  40. data/concrete/ui/style.css +296 -0
  41. data/concrete/ui/toolbar.js +74 -0
  42. data/concrete/ui/workbench.js +165 -0
  43. data/doc/concrete_developers_guide.html +1054 -0
  44. data/doc/concrete_developers_guide.txt +502 -0
  45. data/doc/concrete_users_guide.html +694 -0
  46. data/doc/concrete_users_guide.txt +223 -0
  47. data/example/formula_editor/example_data/example1.json +11 -0
  48. data/example/formula_editor/formula_editor.html +83 -0
  49. data/example/formula_editor/sqrt_horz.png +0 -0
  50. data/example/formula_editor/sqrt_vert.png +0 -0
  51. data/example/formula_editor/style.css +31 -0
  52. data/example/metamodel_editor/edit.rb +54 -0
  53. data/example/metamodel_editor/example_data/formula_metamodel.json +18 -0
  54. data/example/metamodel_editor/example_data/meta_metamodel.json +22 -0
  55. data/example/metamodel_editor/example_data/statemachine_metamodel.json +32 -0
  56. data/example/metamodel_editor/metamodel_editor.html +120 -0
  57. data/example/metamodel_editor/metamodel_editor2.html +135 -0
  58. data/example/metamodel_editor/metamodel_editor3.html +151 -0
  59. data/example/metamodel_editor/style.css +8 -0
  60. data/example/metamodel_editor/style2.css +19 -0
  61. data/example/metamodel_editor/style3.css +35 -0
  62. data/example/minimal_editor/minimal_editor.html +43 -0
  63. data/example/statemachine_editor/example_data/example1.json +11 -0
  64. data/example/statemachine_editor/state_background.png +0 -0
  65. data/example/statemachine_editor/statemachine_editor0.html +55 -0
  66. data/example/statemachine_editor/statemachine_editor1.html +62 -0
  67. data/example/statemachine_editor/statemachine_editor2.html +103 -0
  68. data/example/statemachine_editor/style0.css +8 -0
  69. data/example/statemachine_editor/style1.css +32 -0
  70. data/example/statemachine_editor/style2.css +43 -0
  71. data/example/themes/cobalt.css +176 -0
  72. data/example/themes/dialog-error.png +0 -0
  73. data/example/themes/dialog-information.png +0 -0
  74. data/example/themes/dialog-warning.png +0 -0
  75. data/example/themes/dots_12px.png +0 -0
  76. data/example/themes/fold_button_dots_when_hidden.css +18 -0
  77. data/example/themes/fold_button_plus_minus.css +21 -0
  78. data/example/themes/fold_button_plus_when_hidden.css +18 -0
  79. data/example/themes/light_blue.css +177 -0
  80. data/example/themes/minus_11px.png +0 -0
  81. data/example/themes/minus_13px.png +0 -0
  82. data/example/themes/minus_9px.png +0 -0
  83. data/example/themes/plus_11px.png +0 -0
  84. data/example/themes/plus_13px.png +0 -0
  85. data/example/themes/plus_9px.png +0 -0
  86. data/example/themes/white.css +177 -0
  87. data/lib/concrete/concrete_syntax_provider.rb +63 -0
  88. data/lib/concrete/config.rb +36 -0
  89. data/lib/concrete/file_cache_map.rb +88 -0
  90. data/lib/concrete/index_builder.rb +108 -0
  91. data/lib/concrete/metamodel/concrete_mmm.rb +45 -0
  92. data/lib/concrete/metamodel/ecore_to_concrete.rb +80 -0
  93. data/lib/concrete/server.rb +92 -0
  94. data/lib/concrete/util/logger.rb +24 -0
  95. data/lib/concrete/util/string_writer.rb +17 -0
  96. data/lib/concrete/working_set.rb +41 -0
  97. data/rakefile +33 -0
  98. data/redist/prototype.js +4320 -0
  99. data/redist/scriptaculous/builder.js +136 -0
  100. data/redist/scriptaculous/controls.js +991 -0
  101. data/redist/scriptaculous/dragdrop.js +975 -0
  102. data/redist/scriptaculous/effects.js +1130 -0
  103. data/redist/scriptaculous/scriptaculous.js +60 -0
  104. data/redist/scriptaculous/slider.js +275 -0
  105. data/redist/scriptaculous/sound.js +55 -0
  106. data/redist/scriptaculous/unittest.js +568 -0
  107. data/test/concrete_test.rb +5 -0
  108. data/test/file_cache_map_test.rb +90 -0
  109. data/test/file_cache_map_test/testdir/fileA +1 -0
  110. data/test/index_builder_test.rb +68 -0
  111. data/test/index_builder_test/ecore_index.js +85 -0
  112. data/test/index_builder_test/ecore_index_expected.js +85 -0
  113. data/test/integration/external_elements_test.html +114 -0
  114. data/test/metamodel_test.rb +40 -0
  115. data/test/metamodel_test/concrete_mmm_expected.js +19 -0
  116. data/test/metamodel_test/concrete_mmm_generated.js +19 -0
  117. data/test/metamodel_test/concrete_mmm_regenerated.js +19 -0
  118. data/test/unit/external_identifier_provider_test.html +138 -0
  119. data/test/unit/identifier_provider_test.html +269 -0
  120. data/test/unit/metamodel_provider_test.html +318 -0
  121. data/test/unit/model_interface_test.html +257 -0
  122. data/test/unit/template_provider_test.html +171 -0
  123. data/test/unit/test.css +90 -0
  124. data/test/working_set_test.rb +54 -0
  125. data/test/working_set_test/file1.txt +0 -0
  126. data/test/working_set_test/file2 +0 -0
  127. data/test/working_set_test/subdir/file3.xml +0 -0
  128. metadata +201 -0
@@ -0,0 +1,106 @@
1
+ // Concrete Model Editor
2
+ //
3
+ // Copyright (c) 2010 Martin Thiede
4
+ //
5
+ // Concrete is freely distributable under the terms of an MIT-style license.
6
+
7
+ Concrete.Scroller = {};
8
+
9
+ // scrolls the window or the first scrollable container by the smallest possible offset
10
+ // which meximizes the visible part of element e
11
+ // +direction+ can be on of "horizontal", "vertical" or "both"
12
+ Concrete.Scroller.scrollTo = function(e, direction) {
13
+ if (!["horizontal", "vertical", "both"].include(direction)) throw "invalid direction";
14
+
15
+ var isScrollable = function(f) {
16
+ return (f.getDimensions().width > f.up().getDimensions().width ||
17
+ f.getDimensions().height > f.up().getDimensions().height);
18
+ };
19
+
20
+ var findScrollable = function(f) {
21
+ if (f.tagName.toUpperCase() == "BODY") return undefined;
22
+ if (isScrollable(f)) {
23
+ return f;
24
+ }
25
+ else {
26
+ return findScrollable(f.up());
27
+ }
28
+ };
29
+
30
+ var maxScroll = function(negOffset, posOffset) {
31
+ if ((negOffset*-1) < posOffset) {
32
+ return negOffset*-1;
33
+ }
34
+ else {
35
+ return posOffset;
36
+ }
37
+ }
38
+
39
+ var scrollable = findScrollable(e);
40
+
41
+ if (scrollable) {
42
+ var scrollContainer = scrollable.up();
43
+ var coff = scrollContainer.cumulativeOffset();
44
+ var cdim = scrollContainer.getDimensions();
45
+ var eoff = e.cumulativeOffset();
46
+ var edim = e.getDimensions();
47
+
48
+ var leftBorderDist = eoff.left - coff.left - scrollContainer.scrollLeft;
49
+ var rightBorderDist = coff.left + cdim.width - (eoff.left + edim.width) + scrollContainer.scrollLeft;
50
+ var topBorderDist = eoff.top - coff.top - scrollContainer.scrollTop;
51
+ var bottomBorderDist = coff.top + cdim.height - (eoff.top + edim.height) + scrollContainer.scrollTop;
52
+
53
+ if (direction == "horizontal" || direction == "both") {
54
+ if (leftBorderDist < 0 && rightBorderDist > 0) {
55
+ scrollContainer.scrollLeft = scrollContainer.scrollLeft - maxScroll(leftBorderDist, rightBorderDist);
56
+ }
57
+ else if (rightBorderDist < 0 && leftBorderDist > 0) {
58
+ scrollContainer.scrollLeft = scrollContainer.scrollLeft + maxScroll(rightBorderDist, leftBorderDist);
59
+ }
60
+ }
61
+
62
+ if (direction == "vertical" || direction == "both") {
63
+ if (topBorderDist < 0 && bottomBorderDist > 0) {
64
+ scrollContainer.scrollTop = scrollContainer.scrollTop - maxScroll(topBorderDist, bottomBorderDist);
65
+ }
66
+ else if (bottomBorderDist < 0 && topBorderDist > 0) {
67
+ scrollContainer.scrollTop = scrollContainer.scrollTop + maxScroll(bottomBorderDist, topBorderDist);
68
+ }
69
+ }
70
+
71
+ }
72
+ else {
73
+ var vpLeft = document.viewport.getScrollOffsets().left;
74
+ var vpRight = document.viewport.getScrollOffsets().left + window.innerWidth;
75
+ var vpTop = document.viewport.getScrollOffsets().top;
76
+ var vpBottom = document.viewport.getScrollOffsets().top + window.innerHeight;
77
+
78
+ var leftBorderDist = e.left()-vpLeft;
79
+ var rightBorderDist = vpRight-e.right();
80
+ var topBorderDist = e.top()-vpTop;
81
+ var bottomBorderDist = vpBottom-e.bottom();
82
+
83
+ var scrollPosX = vpLeft;
84
+ var scrollPosY = vpTop;
85
+
86
+ if (direction == "horizontal" || direction == "both") {
87
+ if (leftBorderDist < 0 && rightBorderDist > 0) {
88
+ scrollPosX = vpLeft - maxScroll(leftBorderDist, rightBorderDist);
89
+ }
90
+ else if (rightBorderDist < 0 && leftBorderDist > 0) {
91
+ scrollPosX = vpLeft + maxScroll(rightBorderDist, leftBorderDist);
92
+ }
93
+ }
94
+
95
+ if (direction == "vertical" || direction == "both") {
96
+ if (topBorderDist < 0 && bottomBorderDist > 0) {
97
+ scrollPosY = vpTop - maxScroll(topBorderDist, bottomBorderDist);
98
+ }
99
+ else if (bottomBorderDist < 0 && topBorderDist > 0) {
100
+ scrollPosY = vpTop + maxScroll(bottomBorderDist, topBorderDist);
101
+ }
102
+ }
103
+
104
+ window.scrollTo(scrollPosX, scrollPosY);
105
+ }
106
+ }
@@ -0,0 +1,302 @@
1
+ // Concrete Model Editor
2
+ //
3
+ // Copyright (c) 2010 Martin Thiede
4
+ //
5
+ // Concrete is freely distributable under the terms of an MIT-style license.
6
+
7
+ Concrete.Selector = Class.create({
8
+
9
+ // Options:
10
+ // cursorEdgeOnly: if set to true snap cursor to element edges, default: true
11
+ //
12
+ initialize: function(options) {
13
+ options = options || {};
14
+ this.cursor = {x: 0, y: 0};
15
+ this._inlineFirst = true;
16
+ this._cursorEdgeOnly = options.cursorEdgeOnly == undefined ? true : options.cursorEdgeOnly;
17
+ this.selected = undefined;
18
+ this.multiSelected = [];
19
+ },
20
+
21
+ setOnChangeFunction: function(f) {
22
+ this.changeActionFunc = f;
23
+ },
24
+
25
+ selectDirect: function(s, multi) {
26
+ var selectable = this.surroundingSelectable(s);
27
+ if (selectable) {
28
+ var last = this.selected;
29
+ this._setSelected(selectable, multi);
30
+ this.changeActionFunc && this.changeActionFunc(last, selectable);
31
+ }
32
+ },
33
+
34
+ surroundingSelectable: function(s) {
35
+ return s.findAncestorOrSelf(Concrete.Selector.SelectableClasses);
36
+ },
37
+
38
+ selectTab: function(dir) {
39
+ var inner = this._findTabSelectables(this.selected)
40
+ if (inner.size() > 0) {
41
+ this.selectDirect(inner.first());
42
+ }
43
+ else {
44
+ var parent = this.selected.findAncestor(Concrete.Selector.SelectableClasses);
45
+ var sameLevel = parent ? this._findTabSelectables(parent) : [];
46
+ var idx = sameLevel.indexOf(this.selected);
47
+ if (idx >= 0) {
48
+ if (dir == "next") idx++;
49
+ if (dir == "prev") idx--;
50
+ if (idx >= sameLevel.size()) idx = 0;
51
+ if (idx < 0) idx = sameLevel.size()-1;
52
+ this.selectDirect(sameLevel[idx]);
53
+ }
54
+ }
55
+ },
56
+
57
+ selectCursor: function(dir, multi) {
58
+ // multi select only works on elements
59
+ if (multi && !this.selected.hasClassName("ct_element")) this._setSelected(this.selected.up(".ct_element"));
60
+
61
+ var inner = this._findCursorSelectables(this.selected);
62
+ var parent = this.selected.findAncestor(Concrete.Selector.SelectableClasses);
63
+ var outer = this._findCursorSelectables(parent ? parent : this.selected.up()).without(this.selected);
64
+
65
+ var candidates = [];
66
+ if (!multi && this._shouldGoInside(dir) && inner.size() > 0) {
67
+ if (this._inlineFirst) {
68
+ var inline = inner.select(function(s){ return this._isCursorInline(dir, s)}, this);
69
+ }
70
+ else {
71
+ var inline = [];
72
+ }
73
+ candidates = (inline.size() > 0 ? inline : inner);
74
+ }
75
+ else if (outer.size() > 0) {
76
+ if (this._inlineFirst) {
77
+ var inline = outer.select(function(s){ return this._isSelectedElementInline(dir, s)}, this).select(function(s){ return this._isOuterInDirection(dir, s)}, this);
78
+ }
79
+ else {
80
+ var inline = [];
81
+ }
82
+ candidates = (inline.size() > 0 ? inline : outer.select(function(s){ return this._isOuterInDirection(dir, s)}, this));
83
+ }
84
+
85
+ function sortNextInDirection(dir, s) {
86
+ if (dir == "left") {
87
+ return -s.right();
88
+ }
89
+ else if (dir == "right") {
90
+ return s.left();
91
+ }
92
+ else if (dir == "up") {
93
+ return -s.bottom();
94
+ }
95
+ else if (dir == "down") {
96
+ return s.top();
97
+ }
98
+ }
99
+
100
+ var next = undefined;
101
+ if (candidates.size() > 0) {
102
+ next = candidates.sortBy(function(s) { return sortNextInDirection(dir, s) }, this).first();
103
+ var last = this.selected;
104
+ this._setSelected(next, multi);
105
+ this._adjustCursorNext(dir, next);
106
+ this.changeActionFunc && this.changeActionFunc(last, this.selected);
107
+ }
108
+ else if (parent) {
109
+ next = parent;
110
+ var last = this.selected;
111
+ this._setSelected(next, multi);
112
+ this._adjustCursorParent(dir, next);
113
+ this.changeActionFunc && this.changeActionFunc(last, this.selected);
114
+ }
115
+ },
116
+
117
+ getCursorPosition: function() {
118
+ if (this.cursor.element) {
119
+ return {
120
+ x: this.cursor.element.left()+this.cursor.x*Element.getWidth(this.cursor.element),
121
+ y: this.cursor.element.top()+this.cursor.y*Element.getHeight(this.cursor.element) };
122
+ }
123
+ else {
124
+ return {x: 0, y: 0};
125
+ }
126
+ },
127
+
128
+ // Private
129
+
130
+ _findTabSelectables: function(root) {
131
+ return root.findFirstDescendants(Concrete.Selector.TabSelectableClasses, Concrete.Selector.SelectableClasses).select(function(n) {
132
+ return n.visible() && n.ancestors().all(function(a){ return a.visible()})
133
+ })
134
+ },
135
+
136
+ _findCursorSelectables: function(root) {
137
+ return root.findFirstDescendants(Concrete.Selector.CursorSelectableClasses, []).select(function(n) {
138
+ return n.visible() && n.ancestors().all(function(a){ return a.visible()})
139
+ })
140
+ },
141
+
142
+ // returns true if s is inline with the cursor in the give direction
143
+ _isCursorInline: function(dir, s) {
144
+ var result = false;
145
+ var cursor = this.getCursorPosition();
146
+ if (dir == "left" || dir == "right") {
147
+ result = (s.top() <= cursor.y && s.bottom() >= cursor.y);
148
+ }
149
+ else if (dir == "up" || dir == "down") {
150
+ result = (s.left() <= cursor.x && s.right() >= cursor.x);
151
+ }
152
+ return result;
153
+ },
154
+
155
+ // returns true if s is inline (i.e. overlaps) with the currently selected element in the given direction
156
+ _isSelectedElementInline: function(dir, s) {
157
+ var result = false;
158
+ if (dir == "left" || dir == "right") {
159
+ result = (s.top() <= this.selected.bottom() && s.bottom() >= this.selected.top());
160
+ }
161
+ else if (dir == "up" || dir == "down") {
162
+ result = (s.left() <= this.selected.right() && s.right() >= this.selected.left());
163
+ }
164
+ return result;
165
+ },
166
+
167
+ // returns true if s is outside of the current element in direction dir
168
+ _isOuterInDirection: function(dir, s) {
169
+ if (dir == "left") {
170
+ return s.right() <= this.selected.left();
171
+ }
172
+ else if (dir == "right") {
173
+ return s.left() >= this.selected.right();
174
+ }
175
+ else if (dir == "up") {
176
+ return s.bottom() <= this.selected.top();
177
+ }
178
+ else if (dir == "down") {
179
+ return s.top() >= this.selected.bottom();
180
+ }
181
+ },
182
+
183
+ // determine if next selectable should be looked for inside of the current element
184
+ _shouldGoInside: function(dir) {
185
+ var cursor = this.getCursorPosition();
186
+ if (dir == "left") {
187
+ return cursor.x == this.selected.right();
188
+ }
189
+ else if (dir == "right") {
190
+ return cursor.x == this.selected.left();
191
+ }
192
+ else if (dir == "up") {
193
+ return cursor.y == this.selected.bottom();
194
+ }
195
+ else if (dir == "down") {
196
+ return cursor.y == this.selected.top();
197
+ }
198
+ },
199
+
200
+ // adjust the cursor when the next selected is next to the current element
201
+ // this should be used after _setSelected() to override the default adjustment
202
+ _adjustCursorNext: function(dir, s) {
203
+ if (dir == "left") {
204
+ this.cursor.x = 1;
205
+ }
206
+ else if (dir == "right") {
207
+ this.cursor.x = 0;
208
+ }
209
+ else if (dir == "up") {
210
+ this.cursor.y = 1;
211
+ }
212
+ else if (dir == "down") {
213
+ this.cursor.y = 0;
214
+ }
215
+ },
216
+
217
+ // adjust the cursor when the next selected is a parent of the current element
218
+ // this should be used after _setSelected() to override the default adjustment
219
+ _adjustCursorParent: function(dir, s) {
220
+ if (dir == "left") {
221
+ this.cursor.x = 0;
222
+ }
223
+ else if (dir == "right") {
224
+ this.cursor.x = 1;
225
+ }
226
+ else if (dir == "up") {
227
+ this.cursor.y = 0;
228
+ }
229
+ else if (dir == "down") {
230
+ this.cursor.y = 1;
231
+ }
232
+ },
233
+
234
+ // set the currently selected element to s and adjusts the cursor to be on the boundaries of s
235
+ _setSelected: function(s, multi) {
236
+ if (s == this.selected) return;
237
+ this.multiSelected.each(function(e) { e.removeClassName("ct_selected"); });
238
+ if (this.selected) this.selected.removeClassName("ct_selected");
239
+ this.multiSelected = [];
240
+ if (multi && this.selected) {
241
+ this.multiSelectStart = this.multiSelectStart || this.selected;
242
+ var last = this.multiSelectStart;
243
+ last = last.hasClassName("ct_element") ? last : last.up(".ct_element");
244
+ s = s.hasClassName("ct_element") ? s : s.up(".ct_element");
245
+ var lastAncestors = [last].concat(last.ancestors());
246
+ var newAncestors = [s].concat(s.ancestors());
247
+ if (s == last) {
248
+ this.selected = s;
249
+ }
250
+ else if (lastAncestors.include(s)) {
251
+ this.selected = s;
252
+ }
253
+ else if (newAncestors.include(last)) {
254
+ this.selected = last;
255
+ }
256
+ else {
257
+ while (lastAncestors.last() == newAncestors.last()) {
258
+ lastAncestors.pop();
259
+ newAncestors.pop();
260
+ }
261
+ last = lastAncestors.last();
262
+ s = newAncestors.last();
263
+ this.selected = s;
264
+ var siblings = s.up().childElements();
265
+ var lastIndex = siblings.indexOf(last);
266
+ var newIndex = siblings.indexOf(s);
267
+ this.multiSelected = (newIndex > lastIndex) ? siblings.slice(lastIndex, newIndex+1) : siblings.slice(newIndex, lastIndex+1);
268
+ }
269
+ }
270
+ else {
271
+ this.selected = s;
272
+ this.multiSelectStart = undefined;
273
+ }
274
+ this.multiSelected.each(function(e) { e.addClassName("ct_selected"); });
275
+ this.selected.addClassName("ct_selected");
276
+ this._adjustCursor(this.selected);
277
+ },
278
+
279
+ _adjustCursor: function(s) {
280
+ var cur = this.getCursorPosition();
281
+ this.cursor.element = s;
282
+ if (this._cursorEdgeOnly) {
283
+ this.cursor.x = (cur.x < (s.left()+s.right())/2) ? 0 : 1;
284
+ this.cursor.y = (cur.y < (s.top()+s.bottom())/2) ? 0 : 1;
285
+ }
286
+ else {
287
+ var x = cur.x;
288
+ var y = cur.y;
289
+ if (x < s.left()) x = s.left();
290
+ if (x > s.right()) x = s.right();
291
+ if (y < s.top()) y = s.top();
292
+ if (y > s.bottom()) y = s.bottom();
293
+ this.cursor.x = (x - s.left()) / Element.getWidth(s);
294
+ this.cursor.y = (y - s.top()) / Element.getHeight(s);
295
+ }
296
+ }
297
+
298
+ })
299
+
300
+ Concrete.Selector.TabSelectableClasses = ['ct_value', 'ct_element'];
301
+ Concrete.Selector.CursorSelectableClasses = ['ct_value', 'ct_element'];
302
+ Concrete.Selector.SelectableClasses = ['ct_value', 'ct_element'];
@@ -0,0 +1,141 @@
1
+ // Concrete Model Editor
2
+ //
3
+ // Copyright (c) 2010 Martin Thiede
4
+ //
5
+ // Concrete is freely distributable under the terms of an MIT-style license.
6
+
7
+ Concrete.TemplateProvider = Class.create({
8
+
9
+ // +templateRoot+ is the DOM element containing the templates
10
+ //
11
+ // Options:
12
+ // identifierAttribute: name of the feature that holds the identifier, default: none
13
+ // featureSortFunc: function providing values for features used to sort them, default: none
14
+ // alwaysHideFeatures: names of the features which should always be hidden, default: none
15
+ //
16
+ initialize: function(templateRoot, options) {
17
+ this.templateRoot = templateRoot;
18
+ this._templateByClass = {};
19
+ this.options = options || {};
20
+ this.options.alwaysHideFeatures = this.options.alwaysHideFeatures || [];
21
+ },
22
+
23
+ emptyValue: function(feature) {
24
+ var value = new Element("span");
25
+ value.className = "ct_value ct_empty";
26
+ value.innerHTML = "&lt;"+feature.mmFeature.name+"&gt;";
27
+ return value;
28
+ },
29
+
30
+ emptyElement: function(parentNode, feature) {
31
+ if (feature) {
32
+ var placeholderText = "&lt;"+feature.mmFeature.name+"&gt;"
33
+ }
34
+ else {
35
+ var placeholderText = "&lt;root&gt;"
36
+ }
37
+ if (parentNode.tagName.toUpperCase() == "TBODY") {
38
+ cols = parentNode.up("table").select("tr").max(function(r) { return r.childElements().select(function(c) {return c.tagName.toUpperCase() == "TD";}).size(); });
39
+ var element = new Element("tr");
40
+ element.className = "ct_element ct_empty";
41
+ element.features = [];
42
+ // adding child td node via innerHTML doesn't work in Firefox as long as the parent tr node is not child of a table
43
+ var td = new Element("td");
44
+ td.writeAttribute("colspan", cols);
45
+ td.innerHTML = placeholderText;
46
+ element.appendChild(td);
47
+ }
48
+ else {
49
+ var element = new Element("span");
50
+ element.className = "ct_element ct_empty";
51
+ element.innerHTML = placeholderText;
52
+ element.features = [];
53
+ }
54
+ return element;
55
+ },
56
+
57
+ templateByClass: function(clazz) {
58
+ if (this._templateByClass[clazz.name]) return this._templateByClass[clazz.name];
59
+
60
+ var tmpl = this.templateRoot.down(".ctc_"+clazz.name);
61
+ if (tmpl) {
62
+ this._completeDomBasedTemplate(tmpl, clazz);
63
+ }
64
+ else {
65
+ tmpl = this._createGenericTemplate(clazz);
66
+ }
67
+ return this._templateByClass[clazz.name] = tmpl;
68
+ },
69
+
70
+ _createGenericTemplate: function(clazz) {
71
+ this.templateRoot.insert({bottom: "<div class='ct_element ctc_"+clazz.name+"'><div class='hl_header'></div></div>"});
72
+ var tmpl = this.templateRoot.childElements().last();
73
+ tmpl.mmClass = clazz;
74
+ var headDiv = tmpl.down();
75
+ headDiv.insert({bottom: "<span class='ct_fold_button'></span> "});
76
+ headDiv.insert({bottom: "<span class='ct_element_icon'></span> "});
77
+ headDiv.insert({bottom: "<span class='ct_handle ct_class_name'>"+clazz.name+"</span> "});
78
+ var ftmpls = [];
79
+ var features = clazz.allFeatures();
80
+ if (this.options.featureSortFunc) features = features.sortBy(this.options.featureSortFunc);
81
+ features.each(function(f) {
82
+ var ft;
83
+ var hideStrat = this.options.alwaysHideFeatures.include(f.name) ? "ct_always_hide" : "ct_auto_hide";
84
+ if (f.kind == "attribute") {
85
+ if (this.options.identifierAttribute && f.name == this.options.identifierAttribute) {
86
+ headDiv.insert({bottom: "<span class='ct_attribute ctn_"+f.name+" ct_identifier_attribute'><span class='ct_feature_name'>"+f.name+":</span> <span class='ct_slot'></span></span> "});
87
+ }
88
+ else {
89
+ headDiv.insert({bottom: "<span class='ct_attribute ctn_"+f.name+" "+hideStrat+"'><span class='ct_feature_name'>"+f.name+":</span> <span class='ct_slot'></span></span> "});
90
+ }
91
+ ft = headDiv.childElements().last();
92
+ }
93
+ else if (f.kind == "reference") {
94
+ headDiv.insert({bottom: "<span class='ct_reference ctn_"+f.name+" "+hideStrat+"'><span class='ct_feature_name'>"+f.name+":</span> <span class='ct_slot'></span></span> "});
95
+ ft = headDiv.childElements().last();
96
+ }
97
+ else if (f.kind == "containment") {
98
+ tmpl.insert({bottom: "<div class='ct_containment ctn_"+f.name+" "+hideStrat+"'><span class='ct_feature_name'>"+f.name+":</span><div class='ct_slot'></div></div>"});
99
+ ft = tmpl.childElements().last();
100
+ }
101
+ else {
102
+ throw new Error("Unknown feature kind");
103
+ }
104
+ ft.mmFeature = f;
105
+ ftmpls.push(ft);
106
+ }, this);
107
+
108
+ return tmpl;
109
+ },
110
+
111
+ _completeDomBasedTemplate: function(tmpl, clazz) {
112
+ tmpl.mmClass = clazz;
113
+ var allFeatureTemplates = tmpl.select(".ct_attribute").concat(tmpl.select(".ct_reference")).concat(tmpl.select(".ct_containment"));
114
+ clazz.allFeatures().each(function(f) {
115
+ var msg = " template not found for '"+f.name+"' in class '"+clazz.name+"'";
116
+ var ft;
117
+ if (f.isAttribute()) {
118
+ ft = tmpl.down(".ct_attribute.ctn_"+f.name);
119
+ if (!ft) throw new Error("attribute"+msg);
120
+ }
121
+ else if (f.isReference()) {
122
+ ft = tmpl.down(".ct_reference.ctn_"+f.name);
123
+ if (!ft) throw new Error("reference"+msg);
124
+ }
125
+ else if (f.isContainment()) {
126
+ ft = tmpl.down(".ct_containment.ctn_"+f.name);
127
+ if (!ft) throw new Error("containment"+msg);
128
+ }
129
+ else {
130
+ throw new Error("Unknown feature kind");
131
+ }
132
+ if (!ft.down(".ct_slot")) throw new Error("no slot in template for class '"+clazz.name+"' feature '"+f.name+"'");
133
+ ft.mmFeature = f;
134
+ delete allFeatureTemplates[allFeatureTemplates.indexOf(ft)];
135
+ });
136
+ allFeatureTemplates.each(function(ft) {
137
+ throw new Error("Unused feature template '"+ft.className+"' in class '"+clazz.name+"'");
138
+ });
139
+ }
140
+
141
+ });