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,90 @@
1
+ Concrete.UI.OpenElementDialog = Class.create(Concrete.UI.AbstractDialog, {
2
+
3
+ initialize: function($super, extIdentProvider, options) {
4
+ var dialogElement = this._createDomElement();
5
+ $super(dialogElement, options);
6
+ this.input = dialogElement.down(".search_box_input");
7
+ this.extIdentProvider = extIdentProvider;
8
+ this.completer = this._createAutoCompleter();
9
+ },
10
+
11
+ _createDomElement: function() {
12
+ if ($('ct_open_element_dialog')) return $('ct_open_element_dialog');
13
+ Element.insert($$('body').first(), { bottom:
14
+ "<div id='ct_open_element_dialog' class='popup_dialog' style='display: none; position: fixed; z-index: 1000'>" +
15
+ "<div class='shadow'></div>" +
16
+ "<div class='dialog_box'>" +
17
+ "<div class='title_bar'>" +
18
+ "Open Element" +
19
+ "<a class='close_button'></a>" +
20
+ "</div>" +
21
+ "<div class='container'>" +
22
+ "<p class='label'>Element name</p>" +
23
+ "<input class='text_input search_box_input' type='text' spellcheck='false'/>" +
24
+ "<div class='search_box_list_container'>" +
25
+ "<div class='search_box_list auto_complete_dropdown' style='position: relative'>" +
26
+ "</div> " +
27
+ "</div> " +
28
+ "<div style='text-align: center; margin: 10px'>" +
29
+ "<input class='button_input proceed_button' type='button' value='Open' />" +
30
+ "</div>" +
31
+ "</div> " +
32
+ "</div> " +
33
+ "</div>"
34
+ });
35
+ return $('ct_open_element_dialog');
36
+ },
37
+
38
+ _createAutoCompleter: function() {
39
+ var eip = this.extIdentProvider;
40
+ var completer = new Autocompleter.Local(
41
+ this.input,
42
+ this.dialogElement.down(".search_box_list"),
43
+ [],
44
+ { partialSearch: true, fullSearch: true, minChars: 0, partialChars: 0, choices: 100,
45
+ selector: function(completer) {
46
+ var result = [];
47
+ var entry = completer.getToken();
48
+
49
+ for (var i = 0; i < completer.options.array.length &&
50
+ result.length < completer.options.choices ; i++) {
51
+
52
+ var ei = completer.options.array[i];
53
+ var identifier = ei.identifier;
54
+ var foundPos = completer.options.ignoreCase ?
55
+ identifier.toLowerCase().indexOf(entry.toLowerCase()) :
56
+ identifier.indexOf(entry);
57
+
58
+ if (foundPos != -1) {
59
+ result.push("<li><span class='search_box_identifier'>"+
60
+ identifier.substr(0, foundPos) +
61
+ "<strong>" + identifier.substr(foundPos, entry.length) + "</strong>" +
62
+ identifier.substr(foundPos + entry.length) +
63
+ "</span><span class='informal search_box_class_name'> ["+ei.type+"]</span><br />"+
64
+ "<span class='search_box_module_name'> "+ei.module+"</span>"+
65
+ "</li>");
66
+ }
67
+ }
68
+ return "<ul>" + result.join('') + "</ul>";
69
+ }});
70
+ return completer;
71
+ },
72
+
73
+ _proceed: function() {
74
+ if (this.options.onOpenReference) {
75
+ var value = this.input.value;
76
+ var splitIndex = value.indexOf(" ");
77
+ var ident = value.substr(0, splitIndex);
78
+ var module = value.substr(splitIndex+1, value.length-splitIndex-1);
79
+ this.options.onOpenReference(module, ident);
80
+ }
81
+ },
82
+
83
+ open: function($super, eis) {
84
+ $super();
85
+ this.input.value = "";
86
+ this.completer.options.array = eis;
87
+ },
88
+
89
+ });
90
+
@@ -0,0 +1,75 @@
1
+ Concrete.UI.PreferencesDialog = Class.create(Concrete.UI.AbstractDialog, {
2
+
3
+ initialize: function($super, options) {
4
+ options = options || {};
5
+ var dialogElement = this._createDomElement();
6
+ $super(dialogElement, options);
7
+ this.syntaxInput = dialogElement.down(".syntax_input");
8
+ },
9
+
10
+ _createDomElement: function() {
11
+ if ($('ct_preferences_dialog')) return $('ct_preferences_dialog');
12
+ Element.insert($$('body').first(), { bottom:
13
+ "<div id='ct_preferences_dialog' class='popup_dialog' style='display: none; position: fixed; z-index: 1000'>" +
14
+ "<div class='shadow'></div>" +
15
+ "<div class='dialog_box'>" +
16
+ "<div class='title_bar'>Preferences" +
17
+ "<a class='close_button'></a>" +
18
+ "</div>" +
19
+ "<div class='container'>" +
20
+ "<p class='label'>Concrete Syntax</p>" +
21
+ "<select class='dropdown_input syntax_input'>" +
22
+ "</select>" +
23
+ "<p>Save your work and press 'reload' to make changes visible.</p>" +
24
+ "<div style='text-align: center; margin: 10px'>" +
25
+ "<input class='button_input proceed_button' type='button' value='OK' />" +
26
+ "<input class='button_input cancel_button' type='button' value='Cancel' />" +
27
+ "</div>" +
28
+ "</div>" +
29
+ "</div>" +
30
+ "</div>" });
31
+ return $('ct_preferences_dialog');
32
+ },
33
+
34
+ _proceed: function() {
35
+ new Ajax.Request("/setConcreteSyntax", {
36
+ method: 'get',
37
+ parameters: {"ident": this.syntaxInput.value},
38
+ onSuccess: function(transport) {
39
+ },
40
+ onFailure: function() {
41
+ }
42
+ });
43
+ },
44
+
45
+ _buttonPressed: function(element) {
46
+ if (element == this.dialogElement.down(".cancel_button")) {
47
+ this.close();
48
+ }
49
+ },
50
+
51
+ open: function($super, options) {
52
+ $super();
53
+ new Ajax.Request("/concreteSyntaxes", {
54
+ method: 'get',
55
+ onSuccess: function(transport) {
56
+ if (transport.responseText.isJSON()) {
57
+ var synDesc = transport.responseText.evalJSON();
58
+ Element.replace(this.syntaxInput,
59
+ "<select class='dropdown_input syntax_input'>" +
60
+ synDesc.syntaxes.collect(function(s) {
61
+ var selected = (s.ident == synDesc.selected) ? "selected" : "";
62
+ return "<option "+selected+" value=\""+s.ident+"\">"+s.name+"</option>";
63
+ }).join("") +
64
+ "</select>"
65
+ );
66
+ this.syntaxInput = this.dialogElement.down(".syntax_input");
67
+ }
68
+ }.bind(this),
69
+ });
70
+ }
71
+
72
+ });
73
+
74
+
75
+
@@ -0,0 +1,52 @@
1
+ Concrete.UI.ProceedDialog = Class.create(Concrete.UI.AbstractDialog, {
2
+
3
+ initialize: function($super, options) {
4
+ options = options || {};
5
+ this.title = options.title || "Proceed?";
6
+ this.message = options.message || "Proceed?";
7
+ this.proceedButtonText = options.proceedButtonText || "Proceed";
8
+ var dialogElement = this._createDomElement();
9
+ $super(dialogElement, options);
10
+ },
11
+
12
+ _createDomElement: function() {
13
+ if ($('ct_proceed_dialog')) return $('ct_proceed_dialog');
14
+ Element.insert($$('body').first(), { bottom:
15
+ "<div id='ct_proceed_dialog' class='popup_dialog' style='display: none; position: fixed; z-index: 1000'>" +
16
+ "<div class='shadow'></div>" +
17
+ "<div class='dialog_box'>" +
18
+ "<div class='title_bar'>" + this.title +
19
+ "<a class='close_button'></a>" +
20
+ "</div>" +
21
+ "<div class='container'>" +
22
+ "<p class='label'>"+this.message+"</p>" +
23
+ "<div style='text-align: center; margin: 10px'>" +
24
+ "<input class='button_input proceed_button' type='button' value='"+this.proceedButtonText+"' />" +
25
+ "<input class='button_input cancel_button' type='button' value='Cancel' />" +
26
+ "</div>" +
27
+ "</div>" +
28
+ "</div>" +
29
+ "</div>" });
30
+ return $('ct_proceed_dialog');
31
+ },
32
+
33
+ _proceed: function() {
34
+ if (this._onProceed) {
35
+ this._onProceed();
36
+ }
37
+ },
38
+
39
+ _buttonPressed: function(element) {
40
+ if (element == this.dialogElement.down(".cancel_button")) {
41
+ this.close();
42
+ }
43
+ },
44
+
45
+ open: function($super, options) {
46
+ $super();
47
+ this._onProceed = options.onProceed;
48
+ },
49
+
50
+ });
51
+
52
+
@@ -0,0 +1,323 @@
1
+ Concrete.UI.SearchReplaceDialog = Class.create(Concrete.UI.AbstractDialog, {
2
+
3
+ initialize: function($super, options) {
4
+ var dialogElement = this._createDomElement();
5
+ $super(dialogElement, options);
6
+
7
+ this.propertyInput = dialogElement.down(".property_input");
8
+ Element.insert(this.propertyInput, { after:
9
+ "<div style='position: relative; margin: 1px'><div class='auto_complete_dropdown' style='display: none; width: 100%; max-height: 100px; overflow-y: auto'></div></div>"
10
+ });
11
+ this.propertyOptions = [];
12
+ new Autocompleter.Local(this.propertyInput, this.propertyInput.next().down(), this.propertyOptions, {
13
+ partialSearch: true, fullSearch: true, minChars: 0, partialChars: 0, choices: 100 })
14
+
15
+ this.searchInput = dialogElement.down(".search_input");
16
+ this.replaceInput = dialogElement.down(".replace_input");
17
+ this.findButton = dialogElement.down(".find_button");
18
+ this.replaceButton = dialogElement.down(".replace_button");
19
+ this.replaceFindButton = dialogElement.down(".replace_find_button");
20
+ this.replaceAllButton = dialogElement.down(".replace_all_button");
21
+ this.regexpBox = dialogElement.down(".regular_expression_box");
22
+ this.statusOutput = dialogElement.down(".status_output");
23
+ this.lastNodeFound = undefined;
24
+ },
25
+
26
+ _createDomElement: function() {
27
+ if ($('ct_search_replace_dialog')) return $('ct_search_replace_dialog');
28
+ Element.insert($$('body').first(), { bottom:
29
+ "<div id='ct_search_replace_dialog' class='popup_dialog' style='display: none; position: fixed; z-index: 1000'>" +
30
+ "<div class='shadow'></div>" +
31
+ "<div class='dialog_box'>" +
32
+ "<div class='title_bar'>" +
33
+ "Find and replace" +
34
+ "<a class='close_button'></a>" +
35
+ "</div>" +
36
+ "<div class='container'>" +
37
+ "<p class='label'>For property</p>" +
38
+ "<input class='text_input property_input' type='text' spellcheck='false'/>" +
39
+ "<div style='float: right; margin-top: 10px'>Regular Expression: <input class='checkbox_input regular_expression_box' type='checkbox' /></div>" +
40
+ "<p class='label'>Find value</p>" +
41
+ "<input class='text_input search_input' type='text' spellcheck='false'/>" +
42
+ "<p class='label'>Replace with</p>" +
43
+ "<input class='text_input replace_input' type='text' spellcheck='false'/>" +
44
+ "<div style='width: 100%; margin: 5px 0 5px 0' class='status_output' >&nbsp;</div>" +
45
+ "<div style='text-align: center; margin: 10px'>" +
46
+ "<input class='button_input find_button' type='button' value='Find' />" +
47
+ "<input class='button_input replace_button' type='button' value='Replace' />" +
48
+ "<input class='button_input replace_find_button' type='button' value='Replace/Find' />" +
49
+ "<input class='button_input replace_all_button' type='button' value='Replace All' />" +
50
+ "</div>" +
51
+ "</div>" +
52
+ "</div>" +
53
+ "</div>" });
54
+ return $('ct_search_replace_dialog');
55
+ },
56
+
57
+ _buttonPressed: function(element) {
58
+ var featureDesc = this.propertyInput.value.strip();
59
+ if (/^(\w*|\*|\w+#\*|\w+#\w+)$/.match(featureDesc)) {
60
+ if (featureDesc.include("#")) {
61
+ var className = featureDesc.split("#")[0];
62
+ var featureName = featureDesc.split("#")[1];
63
+ }
64
+ else {
65
+ var featureName = featureDesc;
66
+ }
67
+ }
68
+ else {
69
+ this._setStatus("Invalid feature description: specify <feature name>, <class name>#<feature name>, <class name>#*, * or leave empty");
70
+ return;
71
+ }
72
+ var searchPattern = this.searchInput.value;
73
+ if (this.regexpBox.checked) {
74
+ try {
75
+ searchPattern = new RegExp(searchPattern);
76
+ }
77
+ catch(e) {
78
+ this._setStatus("Invalid regular expression");
79
+ return;
80
+ }
81
+ }
82
+ var replaceText = this.replaceInput.value;
83
+
84
+ if (element == this.findButton) {
85
+ this._findCommand(className, featureName, searchPattern);
86
+ }
87
+ else if (element == this.replaceButton) {
88
+ this._replaceCommand(searchPattern, replaceText);
89
+ }
90
+ else if (element == this.replaceFindButton) {
91
+ this._replaceCommand(searchPattern, replaceText);
92
+ this._findCommand(className, featureName, searchPattern);
93
+ }
94
+ else if (element == this.replaceAllButton) {
95
+ this._replaceAllCommand(className, featureName, searchPattern, replaceText);
96
+ }
97
+ },
98
+
99
+ _findCommand: function(className, featureName, searchPattern) {
100
+ this._setStatus("Searching...");
101
+ this._defer(function() {
102
+ if (this._selectNextMatch(className, featureName, searchPattern)) {
103
+ this._setStatus("Found next occurance");
104
+ }
105
+ else {
106
+ this._setStatus("Pattern not found");
107
+ }
108
+ });
109
+ },
110
+
111
+ _replaceCommand: function(searchPattern, replaceText) {
112
+ if (this.lastNodeFound && this.lastSearchPattern.toString() == searchPattern.toString() && this.editor.selector.selected == this.lastNodeFound) {
113
+ this._setStatus("Replacing...");
114
+ this._defer(function() {
115
+ if (this._replaceMatch(this.lastNodeFound, searchPattern, replaceText)) {
116
+ this._setStatus("Replaced 1 occurance");
117
+ }
118
+ else {
119
+ this._setStatus("Nothing replaced");
120
+ }
121
+ });
122
+ }
123
+ else {
124
+ this._setStatus("Use find before replace");
125
+ }
126
+ },
127
+
128
+ _replaceAllCommand: function(className, featureName, searchPattern, replaceText) {
129
+ this._setStatus("Replacing...");
130
+ this._defer(function() {
131
+ var numReplaced = this._replaceAll(className, featureName, searchPattern, replaceText);
132
+ this._setStatus("Replaced "+numReplaced+" occurances");
133
+ });
134
+ },
135
+
136
+ // this is a special defer which is necessary, since Prototype's defer doesn't seem to yield
137
+ // the current process in a way that UI redrawing can take place
138
+ _defer: function(func) {
139
+ func = func.bind(this);
140
+ window.setTimeout(function() {
141
+ window.setTimeout(func, 0);
142
+ }, 0);
143
+ },
144
+
145
+ _selectNextMatch: function(className, featureName, searchPattern) {
146
+ var startLoc = this._findLocation(this.editor.selector.selected);
147
+ if (!startLoc) return false;
148
+ var nextLoc = this.findNext(startLoc[0], startLoc[1], startLoc[2], className, featureName, searchPattern);
149
+ if (nextLoc) {
150
+ var node = nextLoc[0].features[nextLoc[1]].slot.childElements()[nextLoc[2]];
151
+ this.editor.expandParentElements(node);
152
+ if (node.ancestors().any(function(a) {return !a.visible();})) {
153
+ this.editor.showHiddenFeatures(node.up(".ct_element"));
154
+ }
155
+ this.editor.selector.selectDirect(node);
156
+ this.lastNodeFound = node;
157
+ this.lastSearchPattern = searchPattern;
158
+ }
159
+ return (nextLoc != false);
160
+ },
161
+
162
+ _replaceAll: function(className, featureName, searchPattern, replaceText) {
163
+ var startLoc = this._findLocation(this.editor.selector.selected);
164
+ if (!startLoc) return 0;
165
+ var firstMatch = this.findNext(startLoc[0], startLoc[1], startLoc[2], className, featureName, searchPattern);
166
+ var nextMatch = firstMatch;
167
+ var matches = [];
168
+ while (nextMatch) {
169
+ matches.push(nextMatch);
170
+ nextMatch = this.findNext(nextMatch[0], nextMatch[1], nextMatch[2], className, featureName, searchPattern);
171
+ if (nextMatch[0] == firstMatch[0] && nextMatch[1] == firstMatch[1] && nextMatch[2] == firstMatch[2]) {
172
+ break;
173
+ }
174
+ }
175
+ var numReplaced = 0;
176
+ matches.each(function(match) {
177
+ var node = match[0].features[match[1]].slot.childElements()[match[2]];
178
+ if (this._replaceMatch(node, searchPattern, replaceText)) {
179
+ numReplaced++;
180
+ }
181
+ }, this);
182
+ return numReplaced;
183
+ },
184
+
185
+ _replaceMatch: function(node, searchPattern, replaceText) {
186
+ if (Object.isString(searchPattern) && (searchPattern.empty() || searchPattern.strip() == "*")) {
187
+ if (replaceText.empty()) {
188
+ //this.editor.modelInterface.removeValue(node);
189
+ }
190
+ else {
191
+ var newValue = replaceText;
192
+ }
193
+ }
194
+ else {
195
+ var newValue = node.value.replace(searchPattern, replaceText);
196
+ }
197
+ if (newValue) {
198
+ this.editor.modelInterface.changeValue(node, newValue);
199
+ return true;
200
+ }
201
+ else {
202
+ return false;
203
+ }
204
+ },
205
+
206
+ // finds the next value starting from +element+, feature index +fIndex+, value index +vIndex+
207
+ // when the last model element has been checked, the search will continue with the first element
208
+ // the search will stop after the starting point has been reached and checked
209
+ //
210
+ findNext: function(element, fIndex, vIndex, className, featureName, searchPattern) {
211
+ var startElement = element;
212
+ var startFIndex = fIndex;
213
+ var startVIndex = vIndex;
214
+ var containmentStack = [];
215
+ var wrapAround = 0;
216
+ while (true) {
217
+ var feature = element.features[fIndex];
218
+ var values = feature && element.featureValues(feature.mmFeature.name);
219
+ if (feature && (vIndex < values.size()-1)) {
220
+ // next value
221
+ vIndex++;
222
+ if (feature.mmFeature.isContainment()) {
223
+ // go down to child element
224
+ containmentStack.push(element);
225
+ containmentStack.push(feature);
226
+ element = values[vIndex];
227
+ fIndex = 0;
228
+ vIndex = -1;
229
+ }
230
+ else {
231
+ // found a value
232
+ if ((!className || className == "*" || element.mmClass.name == className) &&
233
+ (!featureName || featureName == "*" || feature.mmFeature.name == featureName) &&
234
+ (!searchPattern ||
235
+ (Object.isString(searchPattern) && (searchPattern == "*" || (""+values[vIndex]).include(searchPattern))) ||
236
+ (!Object.isString(searchPattern) && searchPattern.match(""+values[vIndex])))) {
237
+ return [element, fIndex, vIndex];
238
+ }
239
+ }
240
+ }
241
+ else if (fIndex < element.features.size()-1) {
242
+ // next feature
243
+ fIndex++;
244
+ vIndex = -1;
245
+ }
246
+ else {
247
+ var parentFeature = containmentStack.pop() || element.up(".ct_containment");
248
+ if (parentFeature) {
249
+ // go up to parent
250
+ var parentElement = containmentStack.pop() || parentFeature.up(".ct_element");
251
+ fIndex = parentElement.features.indexOf(parentFeature);
252
+ vIndex = parentFeature.slot.childElements().indexOf(element);
253
+ element = parentElement;
254
+ }
255
+ else if (element.next()) {
256
+ // next on root level
257
+ element = element.next();
258
+ fIndex = 0;
259
+ vIndex = -1;
260
+ }
261
+ else {
262
+ // first on root level, wrap around
263
+ element = element.up(".ct_root").childElements().first();
264
+ fIndex = 0;
265
+ vIndex = -1;
266
+ wrapAround++;
267
+ }
268
+ }
269
+ if ((element == startElement && fIndex == startFIndex && vIndex == startVIndex) || wrapAround > 1) {
270
+ // reached starting point, or wrapped around more than once (safety check to avoid endless loop)
271
+ return false;
272
+ }
273
+ }
274
+ },
275
+
276
+ _setStatus: function(text) {
277
+ this.statusOutput.textContent = text;
278
+ },
279
+
280
+ _findLocation: function(node) {
281
+ if (node.mmClass) {
282
+ return [node, 0, -1];
283
+ }
284
+ else if (node.hasClassName("ct_empty")) {
285
+ var feature = node.findAncestor(["ct_containment", "ct_reference", "ct_attribute"]);
286
+ if (feature) {
287
+ var element = feature.up(".ct_element");
288
+ return [element, element.features.indexOf(feature), -1];
289
+ }
290
+ else {
291
+ // empty element on root level
292
+ return false;
293
+ }
294
+ }
295
+ else {
296
+ var feature = node.findAncestor(["ct_containment", "ct_reference", "ct_attribute"]);
297
+ var element = feature.up(".ct_element");
298
+ return [element, element.features.indexOf(feature), feature.slot.childElements().indexOf(node)];
299
+ }
300
+ },
301
+
302
+ open: function($super, editor) {
303
+ $super();
304
+ this.editor = editor;
305
+ this.propertyOptions.clear();
306
+ this.lastNodeFound = undefined;
307
+ this.statusOutput.innerHTML = "&nbsp;";
308
+ var featureNames = [];
309
+ this.options.metamodelProvider.metaclasses.each(function(c) {
310
+ this.propertyOptions.push(c.name+"#*");
311
+ c.allFeatures().each(function(f) {
312
+ featureNames.push(f.name);
313
+ this.propertyOptions.push(c.name+"#"+f.name);
314
+ }, this);
315
+ }, this);
316
+ featureNames.uniq().reverse().each(function(fn) {
317
+ this.propertyOptions.unshift(fn);
318
+ }, this);
319
+ }
320
+
321
+ });
322
+
323
+