concrete 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +32 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +87 -0
- data/concrete/basic_inline_editor.js +73 -0
- data/concrete/clipboard.js +72 -0
- data/concrete/concrete.js +58 -0
- data/concrete/constraint_checker.js +297 -0
- data/concrete/editor.js +964 -0
- data/concrete/element_extension.js +68 -0
- data/concrete/external_identifier_provider.js +112 -0
- data/concrete/helper.js +63 -0
- data/concrete/identifier_provider.js +168 -0
- data/concrete/inline_editor.js +55 -0
- data/concrete/metamodel_provider.js +171 -0
- data/concrete/model_interface.js +429 -0
- data/concrete/scroller.js +106 -0
- data/concrete/selector.js +302 -0
- data/concrete/template_provider.js +141 -0
- data/concrete/ui/abstract_dialog.js +80 -0
- data/concrete/ui/concrete_ui.js +28 -0
- data/concrete/ui/create_module_dialog.js +55 -0
- data/concrete/ui/images/close.png +0 -0
- data/concrete/ui/images/document-new.png +0 -0
- data/concrete/ui/images/document-save.png +0 -0
- data/concrete/ui/images/edit-find-replace.png +0 -0
- data/concrete/ui/images/emblem-symbolic-link.png +0 -0
- data/concrete/ui/images/help-browser.png +0 -0
- data/concrete/ui/images/minus_11px.png +0 -0
- data/concrete/ui/images/plus_11px.png +0 -0
- data/concrete/ui/images/preferences-system.png +0 -0
- data/concrete/ui/images/process-stop.png +0 -0
- data/concrete/ui/images/system-search.png +0 -0
- data/concrete/ui/layout_manager.js +54 -0
- data/concrete/ui/module_browser.js +88 -0
- data/concrete/ui/module_editor.js +217 -0
- data/concrete/ui/open_element_dialog.js +90 -0
- data/concrete/ui/preferences_dialog.js +75 -0
- data/concrete/ui/proceed_dialog.js +52 -0
- data/concrete/ui/search_replace_dialog.js +323 -0
- data/concrete/ui/style.css +296 -0
- data/concrete/ui/toolbar.js +74 -0
- data/concrete/ui/workbench.js +165 -0
- data/doc/concrete_developers_guide.html +1054 -0
- data/doc/concrete_developers_guide.txt +502 -0
- data/doc/concrete_users_guide.html +694 -0
- data/doc/concrete_users_guide.txt +223 -0
- data/example/formula_editor/example_data/example1.json +11 -0
- data/example/formula_editor/formula_editor.html +83 -0
- data/example/formula_editor/sqrt_horz.png +0 -0
- data/example/formula_editor/sqrt_vert.png +0 -0
- data/example/formula_editor/style.css +31 -0
- data/example/metamodel_editor/edit.rb +54 -0
- data/example/metamodel_editor/example_data/formula_metamodel.json +18 -0
- data/example/metamodel_editor/example_data/meta_metamodel.json +22 -0
- data/example/metamodel_editor/example_data/statemachine_metamodel.json +32 -0
- data/example/metamodel_editor/metamodel_editor.html +120 -0
- data/example/metamodel_editor/metamodel_editor2.html +135 -0
- data/example/metamodel_editor/metamodel_editor3.html +151 -0
- data/example/metamodel_editor/style.css +8 -0
- data/example/metamodel_editor/style2.css +19 -0
- data/example/metamodel_editor/style3.css +35 -0
- data/example/minimal_editor/minimal_editor.html +43 -0
- data/example/statemachine_editor/example_data/example1.json +11 -0
- data/example/statemachine_editor/state_background.png +0 -0
- data/example/statemachine_editor/statemachine_editor0.html +55 -0
- data/example/statemachine_editor/statemachine_editor1.html +62 -0
- data/example/statemachine_editor/statemachine_editor2.html +103 -0
- data/example/statemachine_editor/style0.css +8 -0
- data/example/statemachine_editor/style1.css +32 -0
- data/example/statemachine_editor/style2.css +43 -0
- data/example/themes/cobalt.css +176 -0
- data/example/themes/dialog-error.png +0 -0
- data/example/themes/dialog-information.png +0 -0
- data/example/themes/dialog-warning.png +0 -0
- data/example/themes/dots_12px.png +0 -0
- data/example/themes/fold_button_dots_when_hidden.css +18 -0
- data/example/themes/fold_button_plus_minus.css +21 -0
- data/example/themes/fold_button_plus_when_hidden.css +18 -0
- data/example/themes/light_blue.css +177 -0
- data/example/themes/minus_11px.png +0 -0
- data/example/themes/minus_13px.png +0 -0
- data/example/themes/minus_9px.png +0 -0
- data/example/themes/plus_11px.png +0 -0
- data/example/themes/plus_13px.png +0 -0
- data/example/themes/plus_9px.png +0 -0
- data/example/themes/white.css +177 -0
- data/lib/concrete/concrete_syntax_provider.rb +63 -0
- data/lib/concrete/config.rb +36 -0
- data/lib/concrete/file_cache_map.rb +88 -0
- data/lib/concrete/index_builder.rb +108 -0
- data/lib/concrete/metamodel/concrete_mmm.rb +45 -0
- data/lib/concrete/metamodel/ecore_to_concrete.rb +80 -0
- data/lib/concrete/server.rb +92 -0
- data/lib/concrete/util/logger.rb +24 -0
- data/lib/concrete/util/string_writer.rb +17 -0
- data/lib/concrete/working_set.rb +41 -0
- data/rakefile +33 -0
- data/redist/prototype.js +4320 -0
- data/redist/scriptaculous/builder.js +136 -0
- data/redist/scriptaculous/controls.js +991 -0
- data/redist/scriptaculous/dragdrop.js +975 -0
- data/redist/scriptaculous/effects.js +1130 -0
- data/redist/scriptaculous/scriptaculous.js +60 -0
- data/redist/scriptaculous/slider.js +275 -0
- data/redist/scriptaculous/sound.js +55 -0
- data/redist/scriptaculous/unittest.js +568 -0
- data/test/concrete_test.rb +5 -0
- data/test/file_cache_map_test.rb +90 -0
- data/test/file_cache_map_test/testdir/fileA +1 -0
- data/test/index_builder_test.rb +68 -0
- data/test/index_builder_test/ecore_index.js +85 -0
- data/test/index_builder_test/ecore_index_expected.js +85 -0
- data/test/integration/external_elements_test.html +114 -0
- data/test/metamodel_test.rb +40 -0
- data/test/metamodel_test/concrete_mmm_expected.js +19 -0
- data/test/metamodel_test/concrete_mmm_generated.js +19 -0
- data/test/metamodel_test/concrete_mmm_regenerated.js +19 -0
- data/test/unit/external_identifier_provider_test.html +138 -0
- data/test/unit/identifier_provider_test.html +269 -0
- data/test/unit/metamodel_provider_test.html +318 -0
- data/test/unit/model_interface_test.html +257 -0
- data/test/unit/template_provider_test.html +171 -0
- data/test/unit/test.css +90 -0
- data/test/working_set_test.rb +54 -0
- data/test/working_set_test/file1.txt +0 -0
- data/test/working_set_test/file2 +0 -0
- data/test/working_set_test/subdir/file3.xml +0 -0
- metadata +201 -0
data/CHANGELOG
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
=0.1.0 (February 3rd, 2010)
|
2
|
+
|
3
|
+
* First public release
|
4
|
+
|
5
|
+
=0.2.0 (June 14th, 2010)
|
6
|
+
|
7
|
+
* Added higher level "workbench" editor support
|
8
|
+
* Added Ruby/RGen backend support
|
9
|
+
* Added model info hovers which share the popup window used for error descriptions
|
10
|
+
* Added fold button support
|
11
|
+
* Added read only mode of editor widget
|
12
|
+
* Added float datatype support
|
13
|
+
* Added support for table based element layouts
|
14
|
+
* Added metamodel editor examples showing box based and table based layouts
|
15
|
+
* Added support for feature sort function and element icons in TemplateProvider
|
16
|
+
* Added external identifier support
|
17
|
+
* Added "always hide" strategy for features
|
18
|
+
* Added selector option "cursorEdgeOnly", fixed non-edge-only mode
|
19
|
+
* Added "white" theme
|
20
|
+
* Added helper for iterating through model elements in depth first order
|
21
|
+
* Added button to open Concrete Users Guide
|
22
|
+
* The empty element/value placeholder shows the feature name
|
23
|
+
* Constraint checks run asynchronously in the background
|
24
|
+
* Several performance improvements for larger models
|
25
|
+
* Elements can be created in collapsed state
|
26
|
+
* Concrete can be distributed as a Ruby gem
|
27
|
+
* Fixed scrolling to selected element to also work in scroll containers
|
28
|
+
* Fixed error hovers to be only shown for focused editor
|
29
|
+
* Fixed Scriptaculous bugs: do not scroll page while moving in autocompletion box, do not hide autocompletion box when scrollbar is operated
|
30
|
+
* Fixed marker (cursor) positioning for editor nodes with offset
|
31
|
+
* Fixed exception in case fold control nodes are not present
|
32
|
+
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Martin Thiede
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
= Concrete Model Editor
|
2
|
+
|
3
|
+
Concrete is a lightweight, web-based model editor which can be configured for different DSLs (Domain Specific Languages).
|
4
|
+
|
5
|
+
The basic idea is to make use of the browser's document object model (DOM) to represent models and it's rendering engine to create the graphical represenation. DSLs are defined by metamodels (abstract syntax) and DOM templates combined with CSS (concrete syntax).
|
6
|
+
|
7
|
+
Concrete is a Javascript widget based on Prototype and Scriptaculous. It exchanges model data in JSON format and can be integrated easily using Ajax.
|
8
|
+
|
9
|
+
Besides the pure editor widget Concrete now also contains additional support for building larger editors. There is Javascript code implementing a simple "workbench" with an index view and edit view and search and replace functionality. In addition, there is Ruby code implementing the server side counterpart for loading and storing data with a local file system. As an example, check out the separate "mmedit" project on github.
|
10
|
+
|
11
|
+
Note that Concrete can also be used with non-Ruby backends.
|
12
|
+
|
13
|
+
Concrete has been tested sucessfully with Firefox and Chrome.
|
14
|
+
|
15
|
+
|
16
|
+
== Screencast
|
17
|
+
|
18
|
+
See Concrete in action by watching the screencast[http://vimeo.com/9164866].
|
19
|
+
|
20
|
+
|
21
|
+
== Download
|
22
|
+
|
23
|
+
You can get the latest version from github.
|
24
|
+
|
25
|
+
|
26
|
+
== Installation
|
27
|
+
|
28
|
+
Concrete is not a standalone editor but needs to be integrated into a (web) application. For more information how to do this, see the developers guide and the examples included.
|
29
|
+
|
30
|
+
Ruby users can install Concrete as a gem:
|
31
|
+
|
32
|
+
> gem install concrete
|
33
|
+
|
34
|
+
The Ruby based functionality of Concrete requires the "rgen" modelling framework and the little "andand" gem which should be installed automatically. Otherwise it can be installed using:
|
35
|
+
|
36
|
+
> gem install andand rgen
|
37
|
+
|
38
|
+
RGen is also available on github.
|
39
|
+
|
40
|
+
The Ruby part of Concrete should work with any Ruby version starting with Ruby 1.8.6. The Ruby installers can be downloaded from:
|
41
|
+
|
42
|
+
http://ruby-lang.org
|
43
|
+
|
44
|
+
|
45
|
+
== Documentation
|
46
|
+
|
47
|
+
There is a Users Guide and a Developers Guide in the doc folder. The Users Guide explains the usage of the editor independant of any specific DSL. The Developers Guide describes how to create own DSLs and how to integrate Concrete.
|
48
|
+
|
49
|
+
|
50
|
+
== Examples
|
51
|
+
|
52
|
+
Currently, the examples folder contains:
|
53
|
+
* A metamodel editor which can be used to create and edit Concrete metamodels
|
54
|
+
* A simple formula editor which shows some more math like graphical representation
|
55
|
+
* A simple statechart editor, featuring graphical representation of states
|
56
|
+
|
57
|
+
To use the examples, just open the HTML file in your browser (currently it should be Firefox). You can start from scratch or load a given model in JSON format. To do so just paste it into the clipboard box visible on each example page, then focus the editor and paste again using Ctrl-V. To store an example model, select it in the editor using Ctrl-A, copy it to the internal clipboard using Ctrl-C and then copy the JSON from the clipboard area to where you need it.
|
58
|
+
|
59
|
+
The metamodel editor comes in 3 flavours demonstrating text based, table based and box based layouts.
|
60
|
+
|
61
|
+
In the metamodel example, there is also a little Ruby program which shows server interaction with Ajax. You can run the edit.rb with a JSON file as argument and it will fire up the browser, load the model, let you edit it and write it back to the file when you click the "Save" button. There is a folder "example_data" which contains the plain metamodels of the examples, they are basically the same as what is embedded in the respective example HTML files.
|
62
|
+
|
63
|
+
To edit the statemachine example metamodel, you would do:
|
64
|
+
|
65
|
+
example/metamodel_editor> ruby edit.rb example_data/statemachine_metamodel.json
|
66
|
+
|
67
|
+
If you want to try the respective editor with the changed metamodel, you have to put it into the HTML file. It will not load the metamodel from the "example_data" folder.
|
68
|
+
|
69
|
+
A much more sophisticated example metamodel editor is "mmedit" which is available as a separate gem on github.
|
70
|
+
|
71
|
+
|
72
|
+
== License
|
73
|
+
|
74
|
+
Concrete is licensed under the terms of the MIT license, see the included MIT-LICENSE file.
|
75
|
+
|
76
|
+
|
77
|
+
== Credits
|
78
|
+
|
79
|
+
Concrete is based on the great Prototype Javascript framework which makes Javascript feel much more like Ruby:
|
80
|
+
http://www.prototypejs.org
|
81
|
+
|
82
|
+
For auto completion and some visual effects, as well as the unit test framework, thanks go to Scriptaculous:
|
83
|
+
http://script.aculo.us
|
84
|
+
|
85
|
+
Almost all of the icons are taken from the Tango icon theme:
|
86
|
+
http://tango.freedesktop.org
|
87
|
+
|
@@ -0,0 +1,73 @@
|
|
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.BasicInlineEditor = Class.create({
|
8
|
+
|
9
|
+
show: function(element, initialText, partial, completionOptions) {
|
10
|
+
if (this.element) {
|
11
|
+
// edit still in progress, cancel
|
12
|
+
this.hide();
|
13
|
+
}
|
14
|
+
this.element = element;
|
15
|
+
if (element.parentNode.tagName.toUpperCase() == "TBODY") {
|
16
|
+
cols = element.up("table").select("tr").max(function(r) { return r.childElements().select(function(c) {return c.tagName.toUpperCase() == "TD";}).size(); });
|
17
|
+
Element.insert(element, { after:
|
18
|
+
"<tr><td colspan='"+cols+"'>" +
|
19
|
+
"<div class='ct_inline_editor' style='display: inline'>" +
|
20
|
+
"<input type='text' size='"+initialText.length+"' value='"+initialText+"'/>" +
|
21
|
+
"<div style='display: none; position: absolute; border: 1px solid grey; background-color: white;'></div>" +
|
22
|
+
"</div>" +
|
23
|
+
"</td></tr>"
|
24
|
+
});
|
25
|
+
input = element.next().down().down().down();
|
26
|
+
}
|
27
|
+
else {
|
28
|
+
Element.insert(element, { after:
|
29
|
+
"<div class='ct_inline_editor' style='display: inline'>" +
|
30
|
+
"<input type='text' size='"+initialText.length+"' value='"+initialText+"'/>" +
|
31
|
+
"<div style='display: none; position: absolute; border: 1px solid grey; background-color: white;'></div>" +
|
32
|
+
"</div>"
|
33
|
+
});
|
34
|
+
input = element.next().down();
|
35
|
+
}
|
36
|
+
element.hide();
|
37
|
+
this.input = input
|
38
|
+
input.select();
|
39
|
+
this._interval = window.setInterval(function() {
|
40
|
+
// set size one larger than the text, otherwise the first char is shifted out left when new chars are added at the right
|
41
|
+
input.size = input.value.length + 1;
|
42
|
+
}, 50);
|
43
|
+
new Autocompleter.Local(input, input.next(), completionOptions, {partialSearch: partial, fullSearch: partial, minChars: 0, partialChars: 0, onShow:
|
44
|
+
function(element, update){
|
45
|
+
if(!update.style.position || update.style.position=='absolute') {
|
46
|
+
update.style.position = 'absolute';
|
47
|
+
Position.clone(element, update, {
|
48
|
+
setHeight: false,
|
49
|
+
setWidth: false, // in contrast to the original built-in default, do not set width
|
50
|
+
offsetTop: element.offsetHeight
|
51
|
+
});
|
52
|
+
}
|
53
|
+
Effect.Appear(update,{duration:0.15});
|
54
|
+
}
|
55
|
+
})
|
56
|
+
},
|
57
|
+
|
58
|
+
getText: function() {
|
59
|
+
return this.input.value;
|
60
|
+
},
|
61
|
+
|
62
|
+
setError: function() {
|
63
|
+
this.element.next().addClassName("ct_error");
|
64
|
+
},
|
65
|
+
|
66
|
+
hide: function() {
|
67
|
+
if (this._interval) window.clearInterval(this._interval);
|
68
|
+
this.element.show();
|
69
|
+
this.element.next().remove();
|
70
|
+
this.element = undefined;
|
71
|
+
}
|
72
|
+
|
73
|
+
})
|
@@ -0,0 +1,72 @@
|
|
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.Clipboard = Class.create({
|
8
|
+
|
9
|
+
// if a storageElement is specified, the data is stored as textContent of this element
|
10
|
+
initialize: function(storageElement) {
|
11
|
+
this.element = storageElement;
|
12
|
+
this.kind = undefined;
|
13
|
+
},
|
14
|
+
|
15
|
+
read: function() {
|
16
|
+
var data = this._readRaw();
|
17
|
+
if (data && Object.isString(data) && data.isJSON()) {
|
18
|
+
return data.evalJSON();
|
19
|
+
}
|
20
|
+
else {
|
21
|
+
return data;
|
22
|
+
}
|
23
|
+
},
|
24
|
+
|
25
|
+
write: function(data) {
|
26
|
+
if (data instanceof Object) {
|
27
|
+
this._writeRaw(Concrete.Helper.prettyPrintJSON(Object.toJSON(data)));
|
28
|
+
}
|
29
|
+
else {
|
30
|
+
this._writeRaw(data);
|
31
|
+
}
|
32
|
+
},
|
33
|
+
|
34
|
+
containsElement: function() {
|
35
|
+
var data = this._readRaw();
|
36
|
+
return data && Object.isString(data) && data.isJSON();
|
37
|
+
},
|
38
|
+
|
39
|
+
containsValue: function() {
|
40
|
+
var data = this._readRaw();
|
41
|
+
return data && Object.isString(data) && !data.isJSON();
|
42
|
+
},
|
43
|
+
|
44
|
+
_readRaw: function() {
|
45
|
+
if (this.element) {
|
46
|
+
if (this.element.tagName == "TEXTAREA") {
|
47
|
+
return this.element.value;
|
48
|
+
}
|
49
|
+
else {
|
50
|
+
return this.element.innerHTML;
|
51
|
+
}
|
52
|
+
}
|
53
|
+
else {
|
54
|
+
return this.data;
|
55
|
+
}
|
56
|
+
},
|
57
|
+
|
58
|
+
_writeRaw: function(data) {
|
59
|
+
if (this.element) {
|
60
|
+
if (this.element.tagName == "TEXTAREA") {
|
61
|
+
this.element.value = data;
|
62
|
+
}
|
63
|
+
else {
|
64
|
+
this.element.innerHTML = data;
|
65
|
+
}
|
66
|
+
}
|
67
|
+
else {
|
68
|
+
this.data = data;
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
});
|
@@ -0,0 +1,58 @@
|
|
1
|
+
// Concrete Model Editor
|
2
|
+
//
|
3
|
+
// Copyright (c) 2010 Martin Thiede
|
4
|
+
//
|
5
|
+
// Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
// a copy of this software and associated documentation files (the
|
7
|
+
// "Software"), to deal in the Software without restriction, including
|
8
|
+
// without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
// distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
// permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
// the following conditions:
|
12
|
+
//
|
13
|
+
// The above copyright notice and this permission notice shall be
|
14
|
+
// included in all copies or substantial portions of the Software.
|
15
|
+
//
|
16
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
|
24
|
+
var Concrete = {
|
25
|
+
Version: '0.1.0',
|
26
|
+
require: function(r) {
|
27
|
+
document.write('<script type="text/javascript" src="'+r+'"><\/script>');
|
28
|
+
},
|
29
|
+
load: function() {
|
30
|
+
// TODO: check prototype and scriptaculous version
|
31
|
+
var js = /concrete\.js(\?.*)?$/;
|
32
|
+
$$('head script[src]').findAll(function(s) {
|
33
|
+
return s.src.match(js);
|
34
|
+
}).each(function(s) {
|
35
|
+
var path = s.src.replace(js, '');
|
36
|
+
[
|
37
|
+
'editor',
|
38
|
+
'selector',
|
39
|
+
'scroller',
|
40
|
+
'element_extension',
|
41
|
+
'basic_inline_editor',
|
42
|
+
'inline_editor',
|
43
|
+
'template_provider',
|
44
|
+
'model_interface',
|
45
|
+
'metamodel_provider',
|
46
|
+
'identifier_provider',
|
47
|
+
'external_identifier_provider',
|
48
|
+
'constraint_checker',
|
49
|
+
'clipboard',
|
50
|
+
'helper'
|
51
|
+
].each( function(include) {
|
52
|
+
Concrete.require(path + include + '.js');
|
53
|
+
});
|
54
|
+
});
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
Concrete.load();
|
@@ -0,0 +1,297 @@
|
|
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.ConstraintChecker = Class.create({
|
8
|
+
|
9
|
+
// Options:
|
10
|
+
// externalIdentifierProvider: external identifier provider, default: none
|
11
|
+
// externalModule: name of the current module within external index, default: none
|
12
|
+
// allowDuplicates: classes of which instances with same indentifier may exist, default: none
|
13
|
+
// automaticChecking: if set to false, do not run constraint checks when model changes, default: true
|
14
|
+
//
|
15
|
+
initialize: function(rootClasses, identifierProvider, options) {
|
16
|
+
this.options = options || {};
|
17
|
+
this.options.allowDuplicates = this.options.allowDuplicates || [];
|
18
|
+
this.rootClasses = rootClasses;
|
19
|
+
this.identifierProvider = identifierProvider;
|
20
|
+
this.externalIdentifierProvider = this.options.externalIdentifierProvider;
|
21
|
+
this.featureConstraints = {};
|
22
|
+
this._automaticChecking = (this.options.automaticChecking == undefined) ? true : this.options.automaticChecking;
|
23
|
+
},
|
24
|
+
|
25
|
+
// model root must be set for the checker to work
|
26
|
+
setModelRoot: function(modelRoot) {
|
27
|
+
this.modelRoot = modelRoot;
|
28
|
+
},
|
29
|
+
|
30
|
+
addConstraint: function(constraint) {
|
31
|
+
if (constraint instanceof Concrete.ConstraintChecker.FeatureValueConstraint) {
|
32
|
+
this.featureConstraints[constraint.class()] = this.featureConstraints[constraint.class()] || {}
|
33
|
+
this.featureConstraints[constraint.class()][constraint.feature()] = this.featureConstraints[constraint.class()][constraint.feature()] || [];
|
34
|
+
this.featureConstraints[constraint.class()][constraint.feature()].push(constraint);
|
35
|
+
}
|
36
|
+
},
|
37
|
+
|
38
|
+
// ModelChangeListener Interface
|
39
|
+
|
40
|
+
elementAdded: function(element) {
|
41
|
+
},
|
42
|
+
|
43
|
+
elementChanged: function(element, feature) {
|
44
|
+
},
|
45
|
+
|
46
|
+
elementRemoved: function(element) {
|
47
|
+
},
|
48
|
+
|
49
|
+
rootChanged: function(root) {
|
50
|
+
},
|
51
|
+
|
52
|
+
commitChanges: function() {
|
53
|
+
if (this._automaticChecking) {
|
54
|
+
this.updateAllProblems();
|
55
|
+
}
|
56
|
+
},
|
57
|
+
|
58
|
+
// ModelChangeListener End
|
59
|
+
|
60
|
+
isValidInstance: function(type, element) {
|
61
|
+
this._allowedTypes = this._allowedTypes || {};
|
62
|
+
this._allowedTypes[type.name] = this._allowedTypes[type.name] || type.allSubTypes().concat(type);
|
63
|
+
return this._allowedTypes[type.name].include(element.mmClass);
|
64
|
+
},
|
65
|
+
|
66
|
+
isValidValue: function(mmFeature, value) {
|
67
|
+
var opts = this.attributeOptions(mmFeature);
|
68
|
+
return (!(opts instanceof RegExp) || opts.test(value)) &&
|
69
|
+
((opts instanceof RegExp) || opts == undefined || opts.include(value));
|
70
|
+
},
|
71
|
+
|
72
|
+
attributeOptions: function(mmFeature) {
|
73
|
+
if (mmFeature.type.isEnum()) {
|
74
|
+
// enum
|
75
|
+
return mmFeature.type.literals;
|
76
|
+
}
|
77
|
+
else if (mmFeature.type.isBoolean()) {
|
78
|
+
return ["true", "false"];
|
79
|
+
}
|
80
|
+
else if (mmFeature.type.isInteger()) {
|
81
|
+
return /^(-?[1-9]\d*|0)$/;
|
82
|
+
}
|
83
|
+
else if (mmFeature.type.isFloat()) {
|
84
|
+
return /^(-?[1-9]\d*|0)(\.\d+)?$/;
|
85
|
+
}
|
86
|
+
else {
|
87
|
+
return undefined;
|
88
|
+
}
|
89
|
+
},
|
90
|
+
|
91
|
+
elementOptions: function(slot) {
|
92
|
+
if (slot.hasClassName("ct_root")) {
|
93
|
+
return this.rootClasses.reject(function(t){return t.abstract}).collect(function(c){ return c.name});
|
94
|
+
}
|
95
|
+
else {
|
96
|
+
var type = slot.mmFeature().type;
|
97
|
+
return type.allSubTypes().concat(type).reject(function(t){return t.abstract}).collect(function(t){ return t.name});
|
98
|
+
}
|
99
|
+
},
|
100
|
+
|
101
|
+
// enable and disable automatic checking
|
102
|
+
setAutomaticChecking: function(checking) {
|
103
|
+
this._automaticChecking = checking;
|
104
|
+
},
|
105
|
+
|
106
|
+
// run all constraint checks and update the error annotations
|
107
|
+
// this method must be called explicitly if automatic checking is disabled
|
108
|
+
//
|
109
|
+
updateAllProblems: function() {
|
110
|
+
var element = this.modelRoot.childElements().first();
|
111
|
+
// in case the first element is an empty element, skip to next
|
112
|
+
if (element && !element.isElement()) element = element.next();
|
113
|
+
if (!element) return;
|
114
|
+
var stack = [];
|
115
|
+
if (this._intervalTimer) window.clearInterval(this._intervalTimer);
|
116
|
+
this._intervalTimer = window.setInterval(function() {
|
117
|
+
var i;
|
118
|
+
for (i=0; i<100; i++) {
|
119
|
+
this._updateElementProblems(element);
|
120
|
+
element = Concrete.ModelInterface.Helper.nextElement(element, stack);
|
121
|
+
if (!element) {
|
122
|
+
window.clearInterval(this._intervalTimer);
|
123
|
+
this._intervalTimer = undefined;
|
124
|
+
break;
|
125
|
+
}
|
126
|
+
}
|
127
|
+
}.bind(this), 0.01);
|
128
|
+
},
|
129
|
+
|
130
|
+
// private
|
131
|
+
|
132
|
+
_updateElementProblems: function(element) {
|
133
|
+
if (!element || !element.isElement()) return [];
|
134
|
+
this._removeErrors(element);
|
135
|
+
this._checkElement(element).each(function(p) { this._addError(element, p); }, this);
|
136
|
+
element.features.each(function(f) {
|
137
|
+
this._removeErrors(f);
|
138
|
+
this._checkFeature(element, f).each(function(p) { this._addError(f, p); }, this);
|
139
|
+
}, this);
|
140
|
+
},
|
141
|
+
|
142
|
+
_checkElement: function(element) {
|
143
|
+
var problems = [];
|
144
|
+
if (element.parentNode.hasClassName("ct_root")) {
|
145
|
+
if (!this.rootClasses.include(element.mmClass)) {
|
146
|
+
problems.push("element of class '"+element.mmClass.name+"' not allowed");
|
147
|
+
}
|
148
|
+
}
|
149
|
+
else {
|
150
|
+
if (!this.isValidInstance(element.mmFeature("ct_containment").type, element)) {
|
151
|
+
problems.push("element of class '"+element.mmClass.name+"' not allowed");
|
152
|
+
}
|
153
|
+
}
|
154
|
+
if (element.mmClass.abstract) {
|
155
|
+
problems.push("class '"+element.mmClass.name+"' is abstract");
|
156
|
+
}
|
157
|
+
if (!this.options.allowDuplicates.include(element.mmClass)) {
|
158
|
+
var ident = this.identifierProvider.getIdentifier(element);
|
159
|
+
if (this.identifierProvider.getElement(ident) instanceof Array) {
|
160
|
+
problems.push("duplicate identifier '"+ident+"'");
|
161
|
+
}
|
162
|
+
else if (this.externalIdentifierProvider) {
|
163
|
+
var ei = this.externalIdentifierProvider.getElementInfo(ident, {ignoreModule: this.options.externalModule});
|
164
|
+
if (ei) {
|
165
|
+
var loc = Object.isString(ei.module) ? ei.module : "external module";
|
166
|
+
problems.push("duplicate identifier '"+ident+"', also defined in "+loc);
|
167
|
+
}
|
168
|
+
}
|
169
|
+
}
|
170
|
+
return problems;
|
171
|
+
},
|
172
|
+
|
173
|
+
_checkFeature: function(element, feature) {
|
174
|
+
var problems = [];
|
175
|
+
var mmf = feature.mmFeature;
|
176
|
+
var children = feature.slot.childElements().select(function(c) { return !c.hasClassName("ct_empty"); });
|
177
|
+
var featureConstraints = this._featureConstraints(element, feature);
|
178
|
+
if (mmf.upperLimit > -1 && children.size() > mmf.upperLimit) {
|
179
|
+
if (mmf.upperLimit == 1) {
|
180
|
+
if (mmf.isContainment()) {
|
181
|
+
problems.push("only one element may be specified as '"+mmf.name+"'");
|
182
|
+
}
|
183
|
+
else {
|
184
|
+
problems.push("only one value may be specified as '"+mmf.name+"'");
|
185
|
+
}
|
186
|
+
}
|
187
|
+
problems.push("above upper limit of '"+mmf.upperLimit+"'");
|
188
|
+
}
|
189
|
+
if (mmf.lowerLimit > 0 && children.size() < mmf.lowerLimit) {
|
190
|
+
if (mmf.lowerLimit == 1) {
|
191
|
+
problems.push("'"+mmf.name+"' must be specified");
|
192
|
+
}
|
193
|
+
else {
|
194
|
+
problems.push("below lower limit of '"+mmf.upperLimit+"'");
|
195
|
+
}
|
196
|
+
}
|
197
|
+
if (mmf.isContainment()) {
|
198
|
+
// correct element type is checked for each element
|
199
|
+
children.each(function(c) {
|
200
|
+
this._checkFeatureConstraints(featureConstraints, element, c, problems);
|
201
|
+
}, this);
|
202
|
+
}
|
203
|
+
else if (mmf.isReference()) {
|
204
|
+
children.each(function(c) {
|
205
|
+
var targets = this.identifierProvider.getElement(c.value);
|
206
|
+
if (!(targets instanceof Array)) targets = [targets].compact();
|
207
|
+
if (this.externalIdentifierProvider) {
|
208
|
+
var ei = this.externalIdentifierProvider.getElementInfo(c.value, {ignoreModule: this.options.externalModule});
|
209
|
+
if (ei) {
|
210
|
+
// here we add a type instead of an element
|
211
|
+
targets = targets.concat(ei.type);
|
212
|
+
}
|
213
|
+
}
|
214
|
+
if (targets.size() == 0) {
|
215
|
+
problems.push("can not resolve reference");
|
216
|
+
}
|
217
|
+
else if (targets.size() > 1) {
|
218
|
+
problems.push("multiple targets for reference");
|
219
|
+
}
|
220
|
+
else {
|
221
|
+
var type = targets[0].mmClass ? targets[0].mmClass : targets[0];
|
222
|
+
if (!(mmf.type.allSubTypes().concat(mmf.type).include(type))) {
|
223
|
+
problems.push("reference to class '"+type.name+"' not allowed");
|
224
|
+
}
|
225
|
+
else if (targets[0].mmClass) {
|
226
|
+
// if target is an element, i.e. is local
|
227
|
+
this._checkFeatureConstraints(featureConstraints, element, targets[0], problems);
|
228
|
+
}
|
229
|
+
}
|
230
|
+
}, this);
|
231
|
+
}
|
232
|
+
else {
|
233
|
+
children.each(function(c) {
|
234
|
+
if (!this.isValidValue(mmf, c.value)) {
|
235
|
+
problems.push("value not allowed");
|
236
|
+
}
|
237
|
+
else {
|
238
|
+
this._checkFeatureConstraints(featureConstraints, element, c.value, problems);
|
239
|
+
}
|
240
|
+
}, this);
|
241
|
+
}
|
242
|
+
return problems.uniq();
|
243
|
+
},
|
244
|
+
|
245
|
+
_featureConstraints: function(element, feature) {
|
246
|
+
var byFeature = this.featureConstraints[element.mmClass.name]
|
247
|
+
return (byFeature && byFeature[feature.mmFeature.name]) || [];
|
248
|
+
},
|
249
|
+
|
250
|
+
_checkFeatureConstraints: function(constraints, element, value, problems) {
|
251
|
+
constraints.each(function(c) {
|
252
|
+
if (!c.check(element, value)) {
|
253
|
+
problems.push(c.message(element));
|
254
|
+
}
|
255
|
+
});
|
256
|
+
},
|
257
|
+
|
258
|
+
_addError: function(node, text) {
|
259
|
+
if (!node._errorDescriptions) {
|
260
|
+
node._errorDescriptions = [];
|
261
|
+
node.addClassName("ct_error");
|
262
|
+
}
|
263
|
+
var desc = Concrete.Helper.createDOMNode("div", {class: "ct_error_description", style: "display: none"}, text);
|
264
|
+
node.appendChild(desc);
|
265
|
+
node._errorDescriptions.push(desc);
|
266
|
+
},
|
267
|
+
|
268
|
+
_removeErrors: function(node) {
|
269
|
+
if (node._errorDescriptions) {
|
270
|
+
node._errorDescriptions.each(function(d) {
|
271
|
+
d.remove();
|
272
|
+
});
|
273
|
+
node.removeClassName("ct_error");
|
274
|
+
node._errorDescriptions = undefined;
|
275
|
+
}
|
276
|
+
}
|
277
|
+
|
278
|
+
});
|
279
|
+
|
280
|
+
Concrete.ConstraintChecker.FeatureValueConstraint = Class.create({
|
281
|
+
initialize: function(options) {
|
282
|
+
this.options = options;
|
283
|
+
},
|
284
|
+
class: function() {
|
285
|
+
return this.options.class;
|
286
|
+
},
|
287
|
+
feature: function() {
|
288
|
+
return this.options.feature;
|
289
|
+
},
|
290
|
+
check: function(element, value) {
|
291
|
+
return this.options.checker(element, value);
|
292
|
+
},
|
293
|
+
message: function(element) {
|
294
|
+
var msg = this.options.message;
|
295
|
+
return Object.isFunction(msg) ? msg(element) : msg;
|
296
|
+
}
|
297
|
+
});
|