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,964 @@
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.Editor = Class.create({
8
+
9
+ // Options:
10
+ // readOnlyMode: if set, the model can not be modified via user events, default: false
11
+ // clipboard: if a DOM node is provided it is used as clipboard, default: internal clipboard
12
+ // rootClasses: set of classes which can be instaniated on root level, default: all
13
+ // externalIdentifierProvider:
14
+ // an object providing access to identifiers of objects which are not
15
+ // part of the model being edited in this instance of the editor, default: none
16
+ // constraintChecker:
17
+ // a custom constraint checker, default: none (built in constraint checker)
18
+ // externalModule: name of the external module which represents the module being edited
19
+ // by this instance of the editor (see externalIdentifierProvider)
20
+ // if set, external identifiers from the named module will be ignored, default: none
21
+ // followReferenceSupport:
22
+ // if set to true, this editor will provide the functionality to follow references
23
+ // and to step back and forward in the jump history, default: true
24
+ // onFollowReference:
25
+ // a function which will be called when a reference is invoked
26
+ // it gets two argments, the source reference element and the target element, default: none
27
+ // onFollowExternalReference:
28
+ // a function which will be called when an external reference is invoked,
29
+ // it gets two arguments, the module (provided by the identifier provider) and the identifier,
30
+ // if not defined, external references can not be followed, default: none
31
+ // scrolling: specifies if the current element should scroll into view
32
+ // possible values: none, horizontal, vertical, both, default: both
33
+ // selector: if a selector is provided, use this instead of the internal selector, default: none
34
+ // showInfoPopups:
35
+ // if set to true, show information about element/values in a popup, default: true
36
+ //
37
+ initialize: function(editorRoot, templateProvider, metamodelProvider, identifierProvider, options) {
38
+ options = options || {};
39
+ this.options = options;
40
+ if (options.readOnlyMode == undefined) options.readOnlyMode = false;
41
+ if (options.followReferenceSupport == undefined) options.followReferenceSupport = true;
42
+ if (options.showInfoPopups == undefined) options.showInfoPopups = true;
43
+ options.scrolling = options.scrolling || "both";
44
+ this.editorRoot = editorRoot;
45
+ this._setupRoot();
46
+ this.templateProvider = templateProvider;
47
+ this.metamodelProvider = metamodelProvider;
48
+ this.identifierProvider = identifierProvider;
49
+ this._createInlineEditor();
50
+ this.modelInterface = new Concrete.ModelInterface(this.modelRoot, this.templateProvider, this.metamodelProvider);
51
+ this.modelInterface.addModelChangeListener(this.identifierProvider);
52
+ this.rootClasses = options.rootClasses || this.metamodelProvider.metaclasses;
53
+ this.maxRootElements = -1;
54
+ this.externalIdentifierProvider = options.externalIdentifierProvider;
55
+ this.constraintChecker = options.constraintChecker ||
56
+ new Concrete.ConstraintChecker(this.rootClasses, this.identifierProvider,
57
+ {externalIdentifierProvider: this.externalIdentifierProvider,
58
+ externalModule: options.externalModule});
59
+ this.constraintChecker.setModelRoot(this.modelRoot);
60
+ this.modelInterface.addModelChangeListener(this.constraintChecker);
61
+ this.modelRoot.insert({top: this.templateProvider.emptyElement(this.modelRoot)});
62
+ this.selector = options.selector || new Concrete.Selector();
63
+ this._setupSelector(this.selector);
64
+ this.selector.selectDirect(this.modelRoot.down());
65
+ this.adjustMarker();
66
+ this.jumpStack = [];
67
+ this.clipboard = options.clipboard || new Concrete.Clipboard();
68
+ this.onFollowReference = options.onFollowReference;
69
+ this.onFollowExternalReference = options.onFollowExternalReference;
70
+ this._hasFocus = false;
71
+ },
72
+
73
+ _setupRoot: function() {
74
+ this.editorRoot.insert({top: "<div class='ct_root'></div>"});
75
+ this.modelRoot = this.editorRoot.childElements().first();
76
+ this.editorRoot.insert({bottom: "<div style='position: absolute; left: 0; top: 0' class='ct_cursor'></div>"});
77
+ this.marker = this.editorRoot.childElements().last();
78
+ this.editorRoot.insert({bottom: "<div style='position: fixed; display: none; left: 0; top: 0;' class='ct_message_popup'></div>"});
79
+ this.popup = this.editorRoot.childElements().last();
80
+ },
81
+
82
+ _createInlineEditor: function() {
83
+ var marker = this.marker;
84
+ this.inlineEditor = new Concrete.InlineEditor(function(isActive) {
85
+ if (isActive) {
86
+ marker.hide();
87
+ }
88
+ else {
89
+ marker.show();
90
+ }
91
+ });
92
+ },
93
+
94
+ _setupSelector: function(selector) {
95
+ var editor = this;
96
+ selector.setOnChangeFunction(
97
+ function(oldNode, newNode) {
98
+ if (editor.options.scrolling != "none") Concrete.Scroller.scrollTo(newNode, editor.options.scrolling);
99
+ if (oldNode && newNode != oldNode) {
100
+ var oa = oldNode.ancestors();
101
+ oa.unshift(oldNode);
102
+ var na = newNode.ancestors();
103
+ na.unshift(newNode);
104
+ oa.reverse().each(function(n) {
105
+ if (n != na.pop() && n.isElement()) {
106
+ editor.hideEmptyFeatures(n);
107
+ }
108
+ });
109
+ }
110
+ editor.adjustMarker();
111
+ });
112
+ },
113
+
114
+ focus: function() {
115
+ this._hasFocus = true;
116
+ },
117
+
118
+ handleEvent: function(event) {
119
+ if (event.type == "click" && event.isLeftClick()) {
120
+ if (Event.element(event).ancestors().concat(Event.element(event)).include(this.editorRoot)) {
121
+ this._hasFocus = true;
122
+ this.editorRoot.addClassName("ct_focus");
123
+ }
124
+ else {
125
+ this._hasFocus = false;
126
+ this.editorRoot.removeClassName("ct_focus");
127
+ }
128
+ }
129
+ if (!this._hasFocus) return;
130
+
131
+ if (event.type == "mousemove") {
132
+ this._handleErrorPopups(event);
133
+ if (this.options.showInfoPopups) {
134
+ this._handleInfoPopups(event);
135
+ }
136
+ this._handleRefHighlight(event);
137
+ this.popup.setStyle({left: event.clientX+20, top: event.clientY+20});
138
+ }
139
+ if (this.inlineEditor.isActive) {
140
+ if (event.type == "click" && event.isLeftClick()) {
141
+ this.inlineEditor.cancel()
142
+ this.selector.selectDirect(Event.element(event));
143
+ }
144
+ else if (event.keyCode == 9) { // tab
145
+ this.inlineEditor.finish();
146
+ if (event.shiftKey) {
147
+ this.selector.selectTab("prev");
148
+ }
149
+ else {
150
+ this.selector.selectTab("next");
151
+ }
152
+ event.stop();
153
+ }
154
+ else if (event.keyCode == 13) { // return
155
+ this.inlineEditor.finish();
156
+ event.stop();
157
+ }
158
+ else if (event.keyCode == 27) { // esc
159
+ this.inlineEditor.cancel();
160
+ event.stop();
161
+ }
162
+ }
163
+ else {
164
+ if (event.type == "click" && event.isLeftClick()) {
165
+ if (Event.element(event).hasClassName("ct_fold_button")) {
166
+ this.toggleFoldButton(Event.element(event));
167
+ }
168
+ else if (event.ctrlKey) {
169
+ this.jumpReference(Event.element(event));
170
+ }
171
+ else if (this.selector.selected == this.selector.surroundingSelectable(Event.element(event))) {
172
+ this.runCommand("edit_event");
173
+ }
174
+ else {
175
+ this.selector.selectDirect(Event.element(event), event.shiftKey);
176
+ event.stop();
177
+ }
178
+ }
179
+ else if (event.keyCode == Event.KEY_LEFT && event.ctrlKey) {
180
+ if (event.shiftKey) {
181
+ this.runCommand("collapse_recursive_event")
182
+ }
183
+ else {
184
+ this.runCommand("collapse_event")
185
+ }
186
+ event.stop();
187
+ }
188
+ else if (event.keyCode == Event.KEY_RIGHT && event.ctrlKey) {
189
+ if (event.shiftKey) {
190
+ this.runCommand("expand_recursive_event")
191
+ }
192
+ else {
193
+ this.runCommand("expand_event")
194
+ }
195
+ event.stop();
196
+ }
197
+ else if (event.keyCode == Event.KEY_LEFT && event.altKey) {
198
+ if (this.options.followReferenceSupport) {
199
+ this.runCommand("jump_backward_event");
200
+ event.stop();
201
+ }
202
+ }
203
+ else if (event.keyCode == Event.KEY_RIGHT && event.altKey) {
204
+ if (this.options.followReferenceSupport) {
205
+ this.runCommand("jump_forward_event");
206
+ event.stop();
207
+ }
208
+ }
209
+ else if (event.keyCode == Event.KEY_UP) {
210
+ this.selector.selectCursor("up", event.shiftKey);
211
+ event.stop();
212
+ }
213
+ else if (event.keyCode == Event.KEY_DOWN) {
214
+ this.selector.selectCursor("down", event.shiftKey);
215
+ event.stop();
216
+ }
217
+ else if (event.keyCode == Event.KEY_LEFT) {
218
+ this.selector.selectCursor("left", event.shiftKey);
219
+ event.stop();
220
+ }
221
+ else if (event.keyCode == Event.KEY_RIGHT) {
222
+ this.selector.selectCursor("right", event.shiftKey);
223
+ event.stop();
224
+ }
225
+ else if (event.keyCode == 9) { // tab
226
+ if (event.shiftKey) {
227
+ this.selector.selectTab("prev");
228
+ }
229
+ else {
230
+ this.selector.selectTab("next");
231
+ }
232
+ event.stop();
233
+ }
234
+ else if (event.keyCode == 32 && event.ctrlKey) { // ctrl space
235
+ this.runCommand("edit_event");
236
+ event.stop();
237
+ }
238
+ else if (event.keyCode == 113) { // F2
239
+ this.runCommand("edit_event");
240
+ event.stop();
241
+ }
242
+ else if (event.keyCode == 46) { // Del
243
+ this.runCommand("delete_event");
244
+ event.stop();
245
+ }
246
+ else if (event.shiftKey && event.keyCode == 13) { // shift return
247
+ this.runCommand("show_hidden_event");
248
+ event.stop();
249
+ }
250
+ else if (event.keyCode == 13) { // return
251
+ this.runCommand("insert_event");
252
+ event.stop();
253
+ }
254
+ else if (event.keyCode == 65 && event.ctrlKey) { // ctrl a
255
+ this.selector.selectDirect(this.modelRoot.childElements().first(), false);
256
+ this.selector.selectDirect(this.modelRoot.childElements().last(), true);
257
+ event.stop();
258
+ }
259
+ else if (event.keyCode == 67 && event.ctrlKey) { // ctrl c
260
+ this.runCommand("copy_event");
261
+ event.stop();
262
+ }
263
+ else if (event.keyCode == 86 && event.ctrlKey) { // ctrl v
264
+ this.runCommand("paste_event");
265
+ event.stop();
266
+ }
267
+ else if (event.keyCode == 88 && event.ctrlKey) { // ctrl x
268
+ this.runCommand("cut_event");
269
+ event.stop();
270
+ }
271
+ else if ((event.keyCode >= 65 && event.keyCode <= 90) || // a - z
272
+ (event.keyCode >= 48 && event.keyCode <= 57)) { // 0 - 9
273
+ this.runCommand("edit_event");
274
+ }
275
+ }
276
+ },
277
+
278
+ _handleErrorPopups: function(event) {
279
+ var element = Event.element(event);
280
+ var errorElement = (element.hasClassName("ct_error")) ? element : element.up(".ct_error");
281
+ if (errorElement && (errorElement.up(".ct_editor") == this.editorRoot)) {
282
+ var desc = errorElement.childElements().find(function(e) { return e.hasClassName("ct_error_description")});
283
+ if (desc) {
284
+ this._setPopupMessage("error_desc", "error", desc.innerHTML);
285
+ }
286
+ }
287
+ else {
288
+ this._resetPopupMessage("error_desc");
289
+ }
290
+ },
291
+
292
+ _handleInfoPopups: function(event) {
293
+ var element = Event.element(event);
294
+ this._resetPopupMessage("feature_name");
295
+ this._resetPopupMessage("reference_value");
296
+ this._resetPopupMessage("reference_module");
297
+ if (element.hasClassName("ct_value") && element.up(".ct_editor") == this.editorRoot) {
298
+ this._setPopupMessage("feature_name", "info", "Feature: "+element.mmFeature().name);
299
+ if (element.mmFeature().isReference() && element.value) {
300
+ this._setPopupMessage("reference_value", "info", "Reference to: "+element.value);
301
+ if (this.externalIdentifierProvider) {
302
+ var ei = this.externalIdentifierProvider.getElementInfo(element.value);
303
+ if (ei && ei.module != this.options.externalModule) {
304
+ this._setPopupMessage("reference_module", "info", "In module: "+ei.module);
305
+ }
306
+ }
307
+ }
308
+ }
309
+ },
310
+
311
+ _setPopupMessage: function(ident, kind, content) {
312
+ this._popupMessages = this._popupMessages || {};
313
+ var msg = this._popupMessages[ident];
314
+ var clazz = kind == "error" ? "ct_error_message" : "ct_info_message";
315
+ if (!msg) {
316
+ Element.insert(this.popup, {bottom: "<div class='"+clazz+"'>"+content+"</div>"});
317
+ msg = this.popup.childElements().last();
318
+ }
319
+ else {
320
+ msg.className = clazz;
321
+ msg.innerHTML = content;
322
+ }
323
+ this._popupMessages[ident] = msg;
324
+ this.popup.show();
325
+ },
326
+
327
+ _resetPopupMessage: function(ident) {
328
+ this._popupMessages = this._popupMessages || {};
329
+ var msg = this._popupMessages[ident];
330
+ if (msg) {
331
+ msg.remove();
332
+ this._popupMessages[ident] = undefined;
333
+ }
334
+ if (this.popup.childElements().size() == 0) this.popup.hide();
335
+ },
336
+
337
+ _handleRefHighlight: function(event) {
338
+ var element = Event.element(event);
339
+ if (this.refHighlight) {
340
+ this.refHighlight.source.removeClassName("ct_ref_source");
341
+ if (this.refHighlight.target) this.refHighlight.target.removeClassName("ct_ref_target");
342
+ this.refHighlight = undefined;
343
+ }
344
+ if (event.ctrlKey && element.hasClassName("ct_value") && !element.hasClassName("ct_empty") && element.mmFeature().isReference()) {
345
+ var targets = this.identifierProvider.getElement(element.value);
346
+ if (!(targets instanceof Array)) targets = [targets].compact();
347
+ if (this.externalIdentifierProvider) {
348
+ var ei = this.externalIdentifierProvider.getElementInfo(element.value);
349
+ if (ei) {
350
+ // here we add a type instead of an element
351
+ targets = targets.concat(ei.type);
352
+ }
353
+ }
354
+ if (targets.size() > 0) {
355
+ // highlight the first reference
356
+ element.addClassName("ct_ref_source");
357
+ if (targets[0].mmClass) {
358
+ // if target is an element in this editor
359
+ var target = targets[0];
360
+ target.addClassName("ct_ref_target");
361
+ }
362
+ this.refHighlight = {source: element, target: target};
363
+ }
364
+ }
365
+ },
366
+
367
+ runCommand: function(eventId) {
368
+ var se = this.selector.selected
369
+ var cmd = Concrete.Editor.Commands.select(function(c) {
370
+ return (!this.options.readOnlyMode || c.readOnly) && c.enable && c.enable(se, this) && c.trigger == eventId
371
+ }, this).first();
372
+ if (cmd) {
373
+ cmd.run(se, this);
374
+ }
375
+ },
376
+
377
+ allSelected: function() {
378
+ return this.selector.multiSelected.concat(this.selector.selected).uniq();
379
+ },
380
+
381
+ // assumption all nodes have the same parent
382
+ removeElements: function(nodes) {
383
+ if (nodes.first().siblings().select(function(s){ return s.hasClassName("ct_element")}).size() == nodes.size()-1) {
384
+ nodes.last().insert({after: this.templateProvider.emptyElement(nodes.last().parentNode, nodes.last().feature())});
385
+ }
386
+ if (nodes.last().next()) {
387
+ this.selector.selectDirect(nodes.last().next());
388
+ }
389
+ else {
390
+ this.selector.selectDirect(nodes.first().previous());
391
+ }
392
+ this.modelInterface.removeElement(nodes);
393
+ this.adjustMarker();
394
+ },
395
+
396
+ hideEmptyFeatures: function(n) {
397
+ n.findFirstDescendants(["ct_attribute", "ct_reference", "ct_containment"], ["ct_element"]).each(function(f) {
398
+ if (Concrete.Editor.CommandHelper.canAutoHide(f)) {
399
+ f.hide();
400
+ }
401
+ });
402
+ this.adjustMarker();
403
+ },
404
+
405
+ showHiddenFeatures: function(n) {
406
+ // expand to make fold button state consistent (code below will show all features)
407
+ this.expandElement(n);
408
+ n.findFirstDescendants(["ct_attribute", "ct_reference", "ct_containment"], ["ct_element"]).each(function(f) {
409
+ f.show();
410
+ var slot = f.down(".ct_slot");
411
+ if (slot.childElements().size() == 0) {
412
+ if (f.mmFeature.isContainment()) {
413
+ slot.insert({bottom: this.templateProvider.emptyElement(slot, f)});
414
+ }
415
+ else {
416
+ slot.insert({bottom: this.templateProvider.emptyValue(f)});
417
+ }
418
+ }
419
+ }, this);
420
+ this.adjustMarker();
421
+ },
422
+
423
+ toggleFoldButton: function(fb) {
424
+ if (fb.hasClassName("ct_fold_open")) {
425
+ this.collapseElement(fb.up(".ct_element"));
426
+ }
427
+ else if (fb.hasClassName("ct_fold_closed")) {
428
+ this.expandElement(fb.up(".ct_element"));
429
+ }
430
+ },
431
+
432
+ collapseElement: function(n) {
433
+ n.features.each(function(f) {
434
+ if (f.mmFeature.isContainment()) f.hide();
435
+ });
436
+ if (n.foldButton) {
437
+ n.foldButton.removeClassName("ct_fold_open");
438
+ n.foldButton.addClassName("ct_fold_closed");
439
+ }
440
+ this.adjustMarker();
441
+ },
442
+
443
+ expandElement: function(n) {
444
+ n.features.each(function(f) {
445
+ if (f.mmFeature.isContainment() && !Concrete.Editor.CommandHelper.canAutoHide(f)) {
446
+ f.show();
447
+ }
448
+ });
449
+ if (n.foldButton) {
450
+ n.foldButton.removeClassName("ct_fold_closed");
451
+ n.foldButton.addClassName("ct_fold_open");
452
+ }
453
+ this.adjustMarker();
454
+ },
455
+
456
+ collapseElementRecursive: function(n) {
457
+ n.select(".ct_element").each(function(e) {
458
+ this.collapseElement(e);
459
+ }, this);
460
+ this.collapseElement(n);
461
+ },
462
+
463
+ expandElementRecursive: function(n) {
464
+ n.select(".ct_element").each(function(e) {
465
+ this.expandElement(e);
466
+ }, this);
467
+ this.expandElement(n);
468
+ },
469
+
470
+ // expands the parent elements of an element or attribute/reference value
471
+ expandParentElements: function(n) {
472
+ if (!n.mmClass) {
473
+ // node is a value, expand parents of containing element
474
+ n = n.up(".ct_element");
475
+ }
476
+ var parentElements = n.ancestors().select(function(a) {return a.mmClass;});
477
+ parentElements.each(function(e) {
478
+ if (e.foldButton && e.foldButton.hasClassName("ct_fold_closed")) {
479
+ this.expandElement(e);
480
+ }
481
+ }, this);
482
+ },
483
+
484
+ copyToClipboard: function(nodes, editor) {
485
+ if (nodes.first().hasClassName("ct_value")) {
486
+ // in case of a value, we expect only one node
487
+ this.clipboard.write(nodes.first().value);
488
+ }
489
+ else {
490
+ this.clipboard.write(nodes.collect(function(n) { return this.modelInterface.extractModel(n); }, this));
491
+ }
492
+ },
493
+
494
+ jumpReference: function(n) {
495
+ if (!n.hasClassName("ct_value") || !n.mmFeature().isReference()) return;
496
+ var target = this.identifierProvider.getElement(n.value);
497
+ if (target && !(target instanceof Array)) {
498
+ if (this.onFollowReference) this.onFollowReference(n, target);
499
+ if (this.options.followReferenceSupport) {
500
+ this.jumpStack.push(n);
501
+ this.selector.selectDirect(target);
502
+ }
503
+ }
504
+ else {
505
+ var ei = this.externalIdentifierProvider && this.externalIdentifierProvider.getElementInfo(n.value);
506
+ if (ei && this.onFollowExternalReference) {
507
+ this.onFollowExternalReference(ei.module, n.value);
508
+ }
509
+ }
510
+ },
511
+
512
+ adjustMarker: function() {
513
+ var cur = this.selector.getCursorPosition();
514
+ var poff = this.marker.getOffsetParent().cumulativeOffset();
515
+ this.marker.setStyle({left: cur.x-poff.left, top: cur.y-poff.top});
516
+ },
517
+
518
+ getModel: function() {
519
+ return Concrete.Helper.prettyPrintJSON(
520
+ Object.toJSON(this.modelRoot.childElements().collect(function(n) { return this.modelInterface.extractModel(n)}, this)));
521
+ },
522
+
523
+ setModel: function(json) {
524
+ this.modelInterface.removeElement(this.modelRoot.childElements());
525
+ this.modelInterface.createElement(this.modelRoot, "bottom", json.evalJSON());
526
+ this.selector.selectDirect(this.modelRoot.down());
527
+ }
528
+ })
529
+
530
+ Concrete.Editor.CommandHelper = {
531
+
532
+ referenceOptions: function(type, editor) {
533
+ var idents = editor.modelInterface.elements().
534
+ select(function(e) { return editor.constraintChecker.isValidInstance(type, e);}).
535
+ collect(function(e) { return editor.identifierProvider.getIdentifier(e); });
536
+ if (editor.externalIdentifierProvider) {
537
+ idents = idents.concat(editor.externalIdentifierProvider.getIdentifiers(type));
538
+ }
539
+ return idents.select(function(i) {return i && i.length > 0});
540
+ },
541
+
542
+ canAutoHide: function(feature) {
543
+ return (!feature.hasClassName("ct_error") &&
544
+ (feature.hasClassName("ct_always_hide") ||
545
+ (feature.hasClassName("ct_auto_hide") && (feature.down(".ct_slot").childElements().select(function(e) { return !e.hasClassName("ct_empty"); }).size() == 0))));
546
+ },
547
+
548
+ canAddElement: function(slot, editor) {
549
+ var numElements = slot.childElements().select(function(c){ return c.hasClassName("ct_element")}).size();
550
+ if (slot.hasClassName("ct_root")) {
551
+ return editor.maxRootElements == -1 || numElements < editor.maxRootElements;
552
+ }
553
+ else {
554
+ return slot.mmFeature().upperLimit == -1 || numElements < slot.mmFeature().upperLimit;
555
+ }
556
+ },
557
+
558
+ showAllNonAutoHideFeatures: function(n, editor) {
559
+ n.select(".ct_attribute, .ct_reference, .ct_containment").each(function(f) {
560
+ if (!f.hasClassName("ct_auto_hide") && !f.hasClassName("ct_always_hide")) {
561
+ f.show();
562
+ var slot = f.down(".ct_slot");
563
+ if (slot.childElements().size() == 0) {
564
+ if (f.mmFeature.isContainment()) {
565
+ slot.insert({bottom: editor.templateProvider.emptyElement(slot, f)});
566
+ }
567
+ else {
568
+ slot.insert({bottom: editor.templateProvider.emptyValue(f)});
569
+ }
570
+ }
571
+ }
572
+ });
573
+ },
574
+
575
+ removeValue: function(n, editor) {
576
+ if (n.siblings().select(function(s){ return s.hasClassName("ct_value")}).size() == 0) {
577
+ n.insert({after: editor.templateProvider.emptyValue(n.feature())});
578
+ }
579
+ if (n.next()) {
580
+ editor.selector.selectDirect(n.next());
581
+ }
582
+ else {
583
+ editor.selector.selectDirect(n.previous());
584
+ }
585
+ editor.modelInterface.removeValue(n);
586
+ editor.adjustMarker();
587
+ }
588
+
589
+ }
590
+
591
+ Concrete.Editor.Commands = [
592
+
593
+ {
594
+ name: "Edit Attribute",
595
+ trigger: "edit_event",
596
+ enable: function(n, editor) {
597
+ return n.hasClassName("ct_value") && n.mmFeature().isAttribute();
598
+ },
599
+ run: function(n, editor) {
600
+ editor.inlineEditor.edit(n, {
601
+ init: n.value,
602
+ options: editor.constraintChecker.attributeOptions(n.mmFeature()),
603
+ onSuccess: function(v) {
604
+ if (n.hasClassName("ct_empty")) {
605
+ editor.modelInterface.createValue(n, "after", v);
606
+ editor.selector.selectDirect(n.next());
607
+ n.remove();
608
+ }
609
+ else {
610
+ editor.modelInterface.changeValue(n, v);
611
+ }
612
+ editor.adjustMarker();
613
+ }
614
+ });
615
+ }
616
+ },
617
+
618
+ {
619
+ name: "Add Attribute",
620
+ trigger: "insert_event",
621
+ enable: function(n, editor) {
622
+ return n.hasClassName("ct_value") && !n.hasClassName("ct_empty") && n.mmFeature().isAttribute() &&
623
+ (n.mmFeature().upperLimit == -1 || n.siblings().select(function(s){ return s.hasClassName(".ct_value")}).size()+1 < n.mmFeature().upperLimit);
624
+ },
625
+ run: function(n, editor) {
626
+ n.insert({after: editor.templateProvider.emptyValue(n.feature())});
627
+ var temp = n.next();
628
+ editor.selector.selectDirect(temp);
629
+ editor.inlineEditor.edit(temp, { init: "",
630
+ options: editor.constraintChecker.attributeOptions(n.mmFeature()),
631
+ onSuccess: function(v) {
632
+ temp.remove();
633
+ editor.modelInterface.createValue(n, "after", v);
634
+ editor.selector.selectDirect(n.next());
635
+ },
636
+ onFailure: function() {
637
+ temp.remove();
638
+ editor.selector.selectDirect(n);
639
+ }
640
+ });
641
+ }
642
+ },
643
+
644
+ {
645
+ name: "Remove Attribute",
646
+ trigger: "delete_event",
647
+ enable: function(n, editor) {
648
+ return n.hasClassName("ct_value") && !n.hasClassName("ct_empty") && n.mmFeature().isAttribute();
649
+ },
650
+ run: function(n, editor) {
651
+ Concrete.Editor.CommandHelper.removeValue(n, editor);
652
+ }
653
+ },
654
+
655
+ {
656
+ name: "Edit Reference",
657
+ trigger: "edit_event",
658
+ enable: function(n, editor) {
659
+ return n.hasClassName("ct_value") && n.mmFeature().isReference();
660
+ },
661
+ run: function(n, editor) {
662
+ editor.inlineEditor.edit(n, { init: n.value, partial: true,
663
+ options: Concrete.Editor.CommandHelper.referenceOptions(n.mmFeature().type, editor),
664
+ onSuccess: function(v) {
665
+ if (n.hasClassName("ct_empty")) {
666
+ editor.modelInterface.createValue(n, "after", v);
667
+ editor.selector.selectDirect(n.next());
668
+ n.remove();
669
+ }
670
+ else {
671
+ editor.modelInterface.changeValue(n, v);
672
+ }
673
+ editor.adjustMarker();
674
+ }
675
+ });
676
+ }
677
+ },
678
+
679
+ {
680
+ name: "Add Reference",
681
+ trigger: "insert_event",
682
+ enable: function(n, editor) {
683
+ return n.hasClassName("ct_value") && !n.hasClassName("ct_empty") && n.mmFeature().isReference() &&
684
+ (n.mmFeature().upperLimit == -1 || n.siblings().select(function(s){ return s.hasClassName(".ct_value")}).size()+1 < n.mmFeature().upperLimit);
685
+ },
686
+ run: function(n, editor) {
687
+ n.insert({after: editor.templateProvider.emptyValue(n.feature())});
688
+ var temp = n.next();
689
+ editor.selector.selectDirect(temp);
690
+ editor.inlineEditor.edit(temp, { init: "", partial: true,
691
+ options: Concrete.Editor.CommandHelper.referenceOptions(temp.mmFeature().type, editor),
692
+ onSuccess: function(v) {
693
+ temp.remove();
694
+ editor.modelInterface.createValue(n, "after", v);
695
+ editor.selector.selectDirect(n.next());
696
+ },
697
+ onFailure: function() {
698
+ temp.remove();
699
+ editor.selector.selectDirect(n);
700
+ }
701
+ });
702
+ }
703
+ },
704
+
705
+ {
706
+ name: "Remove Reference",
707
+ trigger: "delete_event",
708
+ enable: function(n, editor) {
709
+ return n.hasClassName("ct_value") && !n.hasClassName("ct_empty") && n.mmFeature().isReference();
710
+ },
711
+ run: function(n, editor) {
712
+ Concrete.Editor.CommandHelper.removeValue(n, editor);
713
+ }
714
+ },
715
+
716
+ {
717
+ name: "Create Element",
718
+ trigger: "edit_event",
719
+ enable: function(n, editor) {
720
+ return n.hasClassName("ct_element") && n.hasClassName("ct_empty");
721
+ },
722
+ run: function(n, editor) {
723
+ editor.inlineEditor.edit(n, { init: "", partial: false,
724
+ options: editor.constraintChecker.elementOptions(n.up()),
725
+ onSuccess: function(v) {
726
+ editor.modelInterface.createElement(n, "after", {_class: v});
727
+ editor.showHiddenFeatures(n.next());
728
+ editor.selector.selectDirect(n.next());
729
+ n.remove();
730
+ editor.adjustMarker();
731
+ }
732
+ });
733
+ }
734
+ },
735
+
736
+ {
737
+ name: "Replace Element",
738
+ trigger: "edit_event",
739
+ enable: function(n, editor) {
740
+ return n.hasClassName("ct_element") && !n.hasClassName("ct_empty");
741
+ },
742
+ run: function(n, editor) {
743
+ var handle = n.findFirstDescendants(["ct_handle"], ["ct_element"]).first() || n;
744
+ editor.inlineEditor.edit(handle, { init: n.mmClass.name,
745
+ options: editor.constraintChecker.elementOptions(n.up()),
746
+ onSuccess: function(v) {
747
+ var data = editor.modelInterface.extractModel(n);
748
+ data._class = v;
749
+ editor.modelInterface.createElement(n, "after", data);
750
+ editor.selector.selectDirect(n.next());
751
+ editor.modelInterface.removeElement(n);
752
+ editor.adjustMarker();
753
+ }
754
+ });
755
+ }
756
+ },
757
+
758
+ {
759
+ name: "Add Element",
760
+ trigger: "insert_event",
761
+ enable: function(n, editor) {
762
+ return n.hasClassName("ct_element") && !n.hasClassName("ct_empty") && Concrete.Editor.CommandHelper.canAddElement(n.up(), editor);
763
+ },
764
+ run: function(n, editor) {
765
+ n.insert({after: editor.templateProvider.emptyElement(n.parentNode, n.feature())})
766
+ var temp = n.next()
767
+ editor.selector.selectDirect(temp)
768
+ editor.inlineEditor.edit(temp, { init: "",
769
+ options: editor.constraintChecker.elementOptions(n.up()),
770
+ onSuccess: function(v) {
771
+ editor.modelInterface.createElement(n, "after", {_class: v});
772
+ editor.selector.selectDirect(n.next());
773
+ editor.showHiddenFeatures(n.next());
774
+ temp.remove();
775
+ editor.adjustMarker();
776
+ },
777
+ onFailure: function(v) {
778
+ temp.remove()
779
+ editor.selector.selectDirect(n)
780
+ }
781
+ });
782
+ }
783
+ },
784
+
785
+ {
786
+ name: "Remove Element",
787
+ trigger: "delete_event",
788
+ enable: function(n, editor) {
789
+ return n.hasClassName("ct_element") && !n.hasClassName("ct_empty");
790
+ },
791
+ run: function(n, editor) {
792
+ editor.removeElements(editor.allSelected());
793
+ }
794
+ },
795
+
796
+ {
797
+ name: "Hide Empty Features",
798
+ trigger: "hide_empty_event",
799
+ readOnly: true,
800
+ enable: function(n, editor) {
801
+ return n.hasClassName("ct_element");
802
+ },
803
+ run: function(n, editor) {
804
+ editor.allSelected().each(function(s) {
805
+ editor.hideEmptyFeatures(s);
806
+ });
807
+ }
808
+ },
809
+
810
+ {
811
+ name: "Show Hidden Features",
812
+ trigger: "show_hidden_event",
813
+ readOnly: true,
814
+ enable: function(n, editor) {
815
+ return true;
816
+ },
817
+ run: function(n, editor) {
818
+ editor.allSelected().each(function(s) {
819
+ editor.showHiddenFeatures(s.findAncestorOrSelf(["ct_element"]));
820
+ });
821
+ }
822
+ },
823
+
824
+ {
825
+ name: "Collapse Element",
826
+ trigger: "collapse_event",
827
+ readOnly: true,
828
+ enable: function(n, editor) {
829
+ return n.hasClassName("ct_element");
830
+ },
831
+ run: function(n, editor) {
832
+ editor.allSelected().each(function(s) {
833
+ editor.collapseElement(s);
834
+ });
835
+ }
836
+ },
837
+
838
+ {
839
+ name: "Expand Element",
840
+ trigger: "expand_event",
841
+ readOnly: true,
842
+ enable: function(n, editor) {
843
+ return n.hasClassName("ct_element");
844
+ },
845
+ run: function(n, editor) {
846
+ editor.allSelected().each(function(s) {
847
+ editor.expandElement(s);
848
+ });
849
+ }
850
+ },
851
+
852
+ {
853
+ name: "Collapse Element Recursive",
854
+ trigger: "collapse_recursive_event",
855
+ readOnly: true,
856
+ enable: function(n, editor) {
857
+ return n.hasClassName("ct_element");
858
+ },
859
+ run: function(n, editor) {
860
+ editor.allSelected().each(function(s) {
861
+ editor.collapseElementRecursive(s);
862
+ });
863
+ }
864
+ },
865
+
866
+ {
867
+ name: "Expand Element Recursive",
868
+ trigger: "expand_recursive_event",
869
+ readOnly: true,
870
+ enable: function(n, editor) {
871
+ return n.hasClassName("ct_element");
872
+ },
873
+ run: function(n, editor) {
874
+ editor.allSelected().each(function(s) {
875
+ editor.expandElementRecursive(s);
876
+ });
877
+ }
878
+ },
879
+
880
+ {
881
+ name: "Copy",
882
+ trigger: "copy_event",
883
+ readOnly: true,
884
+ enable: function(n, editor) {
885
+ return !n.hasClassName("ct_empty");
886
+ },
887
+ run: function(n, editor) {
888
+ editor.copyToClipboard(editor.allSelected());
889
+ }
890
+ },
891
+
892
+ {
893
+ name: "Cut",
894
+ trigger: "cut_event",
895
+ enable: function(n, editor) {
896
+ return !n.hasClassName("ct_empty");
897
+ },
898
+ run: function(n, editor) {
899
+ editor.copyToClipboard(editor.allSelected());
900
+ if (n.hasClassName("ct_value")) {
901
+ Concrete.Editor.CommandHelper.removeValue(n, editor);
902
+ }
903
+ else {
904
+ editor.removeElements(editor.allSelected());
905
+ }
906
+ }
907
+ },
908
+
909
+ {
910
+ name: "Paste",
911
+ trigger: "paste_event",
912
+ enable: function(n, editor) {
913
+ return (n.hasClassName("ct_element") && editor.clipboard.containsElement()) ||
914
+ (n.hasClassName("ct_value") && editor.clipboard.containsValue());
915
+ },
916
+ run: function(n, editor) {
917
+ var data = editor.clipboard.read();
918
+ if (!(data instanceof Array)) data = [ data ];
919
+ if (n.hasClassName("ct_element")) {
920
+ editor.modelInterface.createElement(n, "after", data);
921
+ var created = n.next();
922
+ data.each(function(d) {
923
+ Concrete.Editor.CommandHelper.showAllNonAutoHideFeatures(created, editor);
924
+ created = created.next();
925
+ });
926
+ editor.selector.selectDirect(n.next());
927
+ if (n.hasClassName("ct_empty")) n.remove();
928
+ }
929
+ else {
930
+ editor.modelInterface.createValue(n, "after", data);
931
+ editor.selector.selectDirect(n.next());
932
+ if (n.hasClassName("ct_empty")) n.remove();
933
+ }
934
+ editor.adjustMarker();
935
+ }
936
+ },
937
+
938
+ {
939
+ name: "Jump Reference",
940
+ trigger: "jump_forward_event",
941
+ readOnly: true,
942
+ enable: function(n, editor) {
943
+ return n.hasClassName("ct_value") && n.mmFeature().isReference();
944
+ },
945
+ run: function(n, editor) {
946
+ editor.jumpReference(n);
947
+ }
948
+ },
949
+
950
+ {
951
+ name: "Jump Reference Back",
952
+ trigger: "jump_backward_event",
953
+ readOnly: true,
954
+ enable: function(n, editor) {
955
+ return true;
956
+ },
957
+ run: function(n, editor) {
958
+ if (editor.jumpStack.size() > 0) {
959
+ editor.selector.selectDirect(editor.jumpStack.pop());
960
+ }
961
+ }
962
+ }
963
+
964
+ ]