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,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
+