concrete 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ });