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
@@ -0,0 +1,171 @@
|
|
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.MetamodelProvider = Class.create({
|
8
|
+
|
9
|
+
initialize: function(metamodelJson, opts) {
|
10
|
+
this.metamodel = metamodelJson;
|
11
|
+
this.metaclassesByName = {};
|
12
|
+
this.metaclasses = [];
|
13
|
+
this._extendedChecks = (opts == undefined || !(opts.extended_checks == false));
|
14
|
+
this._resolveMetamodel();
|
15
|
+
},
|
16
|
+
|
17
|
+
_resolveMetamodel: function() {
|
18
|
+
var rootClass = {name: "_Root", abstract: true, subTypes: [], superTypes: [], features: []};
|
19
|
+
Object.extend(rootClass, Concrete.MetamodelExtension.Class);
|
20
|
+
|
21
|
+
var datatypesByName = {};
|
22
|
+
this.metamodel.each(function(c) {
|
23
|
+
if (c._class == "Class") {
|
24
|
+
if (!c.name || c.name.length == 0) throw new Error("Class name is empty");
|
25
|
+
if (this.metaclassesByName[c.name]) throw new Error("Class name '"+c.name+"' not unique");
|
26
|
+
if (this._extendedChecks) this._extendedClassChecks(c);
|
27
|
+
Object.extend(c, Concrete.MetamodelExtension.Class);
|
28
|
+
c.subTypes = [];
|
29
|
+
if (c.abstract == undefined) c.abstract = false;
|
30
|
+
this.metaclassesByName[c.name] = c;
|
31
|
+
this.metaclasses.push(c);
|
32
|
+
}
|
33
|
+
else if (c._class == "Datatype" || c._class == "Enum") {
|
34
|
+
if (!c.name || c.name.length == 0) throw new Error("Datatype name is empty");
|
35
|
+
if (datatypesByName[c.name]) throw new Error("Datatype name '"+c.name+"' not unique");
|
36
|
+
if (this._extendedChecks) this._extendedDatatypeChecks(c);
|
37
|
+
Object.extend(c, Concrete.MetamodelExtension.Datatype);
|
38
|
+
datatypesByName[c.name] = c;
|
39
|
+
}
|
40
|
+
else {
|
41
|
+
throw new Error ("specify '_class' property, it must be one of [Datatype, Enum, Class]");
|
42
|
+
}
|
43
|
+
}, this);
|
44
|
+
// default datatype in case it is not present
|
45
|
+
if (!datatypesByName["String"]) {
|
46
|
+
var dt = {name: "String"};
|
47
|
+
Object.extend(dt, Concrete.MetamodelExtension.Datatype);
|
48
|
+
datatypesByName["String"] = dt;
|
49
|
+
}
|
50
|
+
this.metaclasses.each(function(c) {
|
51
|
+
if (c.superTypes) {
|
52
|
+
if (!(c.superTypes instanceof Array)) c.superTypes = [c.superTypes];
|
53
|
+
c.superTypes = [rootClass].concat(c.superTypes.collect(function(t) {
|
54
|
+
var target = this.metaclassesByName[t];
|
55
|
+
if (!target) throw new Error("Can not resolve supertype '"+t+"' in class '"+c.name+"'");
|
56
|
+
return target;
|
57
|
+
}, this));
|
58
|
+
}
|
59
|
+
else {
|
60
|
+
c.superTypes = [rootClass];
|
61
|
+
}
|
62
|
+
c.superTypes.each(function(t) {
|
63
|
+
t.subTypes.push(c);
|
64
|
+
});
|
65
|
+
featureNames = {};
|
66
|
+
c.features = c.features || [];
|
67
|
+
if (!(c.features instanceof Array)) c.features = [c.features];
|
68
|
+
c.features.each(function(f) {
|
69
|
+
if (!f.name || f.name.length == 0) throw new Error("Feature name is empty in class '"+c.name+"'");
|
70
|
+
if (featureNames[f.name]) throw new Error("Feature name '"+f.name+"' not unique in class '"+c.name+"'");
|
71
|
+
if (this._extendedChecks) this._extendedFeatureChecks(f, c);
|
72
|
+
Object.extend(f, Concrete.MetamodelExtension.Feature);
|
73
|
+
f.containingClass = c;
|
74
|
+
featureNames[f.name] = true;
|
75
|
+
f.kind = f.kind || "attribute";
|
76
|
+
if (f.isReference() || f.isContainment()) {
|
77
|
+
if (f.type) {
|
78
|
+
var target = this.metaclassesByName[f.type];
|
79
|
+
if (!target) throw new Error("Can not resolve type '"+f.type+"' in feature '"+f.name+"' class '"+c.name+"'");
|
80
|
+
f.type = target;
|
81
|
+
}
|
82
|
+
else {
|
83
|
+
f.type = rootClass;
|
84
|
+
}
|
85
|
+
if (f.isReference()) {
|
86
|
+
f.upperLimit = f.upperLimit || 1;
|
87
|
+
}
|
88
|
+
else {
|
89
|
+
f.upperLimit = f.upperLimit || -1;
|
90
|
+
}
|
91
|
+
f.lowerLimit = f.lowerLimit || 0;
|
92
|
+
}
|
93
|
+
else {
|
94
|
+
var typename = f.type || "String";
|
95
|
+
f.type = datatypesByName[typename];
|
96
|
+
if (!f.type) throw new Error("Can not resolve datatype '"+typename+"' in feature '"+f.name+"' class '"+c.name+"'");
|
97
|
+
f.upperLimit = f.upperLimit || 1;
|
98
|
+
f.lowerLimit = f.lowerLimit || 0;
|
99
|
+
}
|
100
|
+
}, this);
|
101
|
+
}, this);
|
102
|
+
},
|
103
|
+
|
104
|
+
_extendedDatatypeChecks: function(type) {
|
105
|
+
for (p in type) {
|
106
|
+
if (!["_class", "name", "literals"].include(p)) throw new Error("Unknown property '"+p+"' in type '"+type.name+"'");
|
107
|
+
}
|
108
|
+
if (type._class != "Enum" && type.literals != undefined) {
|
109
|
+
throw new Error("Literals can only be specified for Enums");
|
110
|
+
}
|
111
|
+
if (type.literals != undefined) {
|
112
|
+
if (!(type.literals instanceof Array) || !type.literals.all(function(l) { return Object.isString(l); })) throw new Error("Enum literals must be an Array of Strings in type '"+type.name+"'");
|
113
|
+
}
|
114
|
+
if (type._class == "Datatype") {
|
115
|
+
if (!(["String", "Integer", "Float", "Boolean"].include(type.name))) throw new Error("Plain Datatypes (excluding Enums) must be named one of [String, Integer, Float, Boolean]");
|
116
|
+
}
|
117
|
+
},
|
118
|
+
|
119
|
+
_extendedClassChecks: function(clazz) {
|
120
|
+
for (p in clazz) {
|
121
|
+
if (!["_class", "features", "name", "superTypes", "abstract"].include(p)) throw new Error("Unknown property '"+p+"' in class '"+clazz.name+"'");
|
122
|
+
}
|
123
|
+
if (clazz.abstract != undefined) {
|
124
|
+
if (clazz.abstract != true && clazz.abstract != false) throw new Error("Abstract property must be true or false in class '"+clazz.name+"'");
|
125
|
+
}
|
126
|
+
},
|
127
|
+
_extendedFeatureChecks: function(feat, clazz) {
|
128
|
+
var loc = " in class '"+clazz.name+"', feature '"+feat.name+"'";
|
129
|
+
for (p in feat) {
|
130
|
+
if (!["_class", "name", "kind", "type", "lowerLimit", "upperLimit"].include(p)) throw new Error("Unknown property '"+p+"'" + loc );
|
131
|
+
if (p == "kind") {
|
132
|
+
if (!["attribute", "reference", "containment"].include(feat.kind)) throw new Error("Feature kind must be one of 'attribute', 'reference', 'containment'" + loc);
|
133
|
+
}
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
});
|
138
|
+
|
139
|
+
Concrete.MetamodelExtension = {};
|
140
|
+
|
141
|
+
Concrete.MetamodelExtension.Class = {
|
142
|
+
|
143
|
+
allSubTypes: function() {
|
144
|
+
this.subTypes = this.subTypes || [];
|
145
|
+
return this.subTypes.concat(this.subTypes.collect(function(t) { return t.allSubTypes(); })).flatten().uniq();
|
146
|
+
},
|
147
|
+
|
148
|
+
allSuperTypes: function() {
|
149
|
+
this.superTypes = this.superTypes || [];
|
150
|
+
return this.superTypes.collect(function(t) { return t.allSuperTypes(); }).concat(this.superTypes).flatten().uniq();
|
151
|
+
},
|
152
|
+
|
153
|
+
allFeatures: function() {
|
154
|
+
return this.allSuperTypes().collect(function(t) { return t.features; }).concat(this.features).flatten().uniq();
|
155
|
+
}
|
156
|
+
};
|
157
|
+
|
158
|
+
Concrete.MetamodelExtension.Datatype = {
|
159
|
+
isEnum: function() { return this._class == "Enum"; },
|
160
|
+
isString: function() { return this.name == "String"; },
|
161
|
+
isInteger: function() { return this.name == "Integer"; },
|
162
|
+
isFloat: function() { return this.name == "Float"; },
|
163
|
+
isBoolean: function() { return this.name == "Boolean"; }
|
164
|
+
};
|
165
|
+
|
166
|
+
Concrete.MetamodelExtension.Feature = {
|
167
|
+
isContainment: function() { return this.kind == "containment"; },
|
168
|
+
isReference: function() { return this.kind == "reference"; },
|
169
|
+
isAttribute: function() { return this.kind == "attribute"; }
|
170
|
+
};
|
171
|
+
|
@@ -0,0 +1,429 @@
|
|
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
|
+
// The ModelInterface manages the model represented by DOM elements
|
8
|
+
|
9
|
+
Element.addMethods({
|
10
|
+
// speed optimization in case the class is known this will be faster
|
11
|
+
feature: function(e, clazz) {
|
12
|
+
if (clazz) {
|
13
|
+
var feat = e.findAncestor(clazz);
|
14
|
+
}
|
15
|
+
else {
|
16
|
+
var feat = e.findAncestor(["ct_attribute", "ct_reference", "ct_containment"]);
|
17
|
+
}
|
18
|
+
return feat;
|
19
|
+
},
|
20
|
+
|
21
|
+
mmFeature: function(e, clazz) {
|
22
|
+
return e.feature(clazz).mmFeature;
|
23
|
+
},
|
24
|
+
|
25
|
+
featureValues: function(e, feature) {
|
26
|
+
if (Object.isString(feature)) {
|
27
|
+
if (!e.featuresByName) {
|
28
|
+
e.featuresByName = {};
|
29
|
+
e.features.each(function(f) { e.featuresByName[f.mmFeature.name] = f; });
|
30
|
+
}
|
31
|
+
feature = e.featuresByName[feature];
|
32
|
+
}
|
33
|
+
else if (!feature.mmFeature) {
|
34
|
+
feature = e.features.find(function(f) {return f.mmFeature == feature; });
|
35
|
+
}
|
36
|
+
var values = feature.slot.childElements();
|
37
|
+
if (feature.mmFeature.isContainment()) {
|
38
|
+
if (values.size() > 1) {
|
39
|
+
// optimization: empty placeholder values can not appear amoung other childs
|
40
|
+
return values;
|
41
|
+
}
|
42
|
+
else if (values.size() == 1 && !values[0].hasClassName("ct_empty")) {
|
43
|
+
return [values[0]];
|
44
|
+
}
|
45
|
+
else {
|
46
|
+
return [];
|
47
|
+
}
|
48
|
+
}
|
49
|
+
else {
|
50
|
+
if (values.size() > 1) {
|
51
|
+
// optimization: empty placeholder values can not appear amoung other childs
|
52
|
+
return values.collect(function(c) {return c.value});
|
53
|
+
}
|
54
|
+
else if (values.size() == 1 && !values[0].hasClassName("ct_empty")) {
|
55
|
+
return [values[0].value];
|
56
|
+
}
|
57
|
+
else {
|
58
|
+
return [];
|
59
|
+
}
|
60
|
+
}
|
61
|
+
},
|
62
|
+
|
63
|
+
isElement: function(e) {
|
64
|
+
return e.mmClass != undefined;
|
65
|
+
}
|
66
|
+
})
|
67
|
+
|
68
|
+
Concrete.ModelInterface = Class.create({
|
69
|
+
|
70
|
+
// +modelRoot+ is the DOM element containing the model elements
|
71
|
+
// Options:
|
72
|
+
// displayValueProvider: a function which returns the display value for an attribute or reference,
|
73
|
+
// the function must take two arguments, the original value text and the value node's feature parent
|
74
|
+
// default: none
|
75
|
+
//
|
76
|
+
initialize: function(modelRoot, templateProvider, metamodelProvider, options) {
|
77
|
+
this.modelRoot = modelRoot;
|
78
|
+
this.templateProvider = templateProvider;
|
79
|
+
this.metamodelProvider = metamodelProvider;
|
80
|
+
options = options || {};
|
81
|
+
this._displayValueProvider = options.displayValueProvider;
|
82
|
+
this._modelChangeListeners = [];
|
83
|
+
},
|
84
|
+
|
85
|
+
addModelChangeListener: function(listener) {
|
86
|
+
if (!listener.elementChanged || !(listener.elementChanged instanceof Function) ||
|
87
|
+
!listener.elementAdded || !(listener.elementAdded instanceof Function) ||
|
88
|
+
!listener.elementRemoved || !(listener.elementRemoved instanceof Function))
|
89
|
+
throw new Error ("incomplete listener interface");
|
90
|
+
this._modelChangeListeners.push(listener);
|
91
|
+
},
|
92
|
+
|
93
|
+
setDisplayValueProvider: function(dvp) {
|
94
|
+
this._displayValueProvider = dvp;
|
95
|
+
},
|
96
|
+
|
97
|
+
elements: function() {
|
98
|
+
return this.modelRoot.childElements().collect(function(e) {
|
99
|
+
return this._collectElementsRecursive(e);
|
100
|
+
}, this).flatten();
|
101
|
+
},
|
102
|
+
|
103
|
+
// creates the element or elements described be +model+ before or after the element +target+
|
104
|
+
// or at the bottom of slot +target+
|
105
|
+
createElement: function(target, where, model, options) {
|
106
|
+
if (!(["before", "after", "bottom"].include(where))) throw new Error ("unknown position");
|
107
|
+
if (where == "bottom" && target != this.modelRoot && !target.hasClassName("ct_slot")) throw new Error ("not a slot");
|
108
|
+
if (where != "bottom" && !target.hasClassName("ct_element")) throw new Error ("not an element");
|
109
|
+
if (!(model instanceof Array)) model = [ model ];
|
110
|
+
if (where == "after") model = model.reverse();
|
111
|
+
model.each(function(e) {
|
112
|
+
var inst = this._instantiateTemplateRecursive(e, target, where, options);
|
113
|
+
this._notifyModelChangeListeners("added", inst);
|
114
|
+
}, this);
|
115
|
+
var parent = target.up(".ct_element");
|
116
|
+
var feature = parent && target.up(".ct_containment");
|
117
|
+
parent = parent || this.modelRoot;
|
118
|
+
this._notifyModelChangeListeners("changed", parent, feature);
|
119
|
+
this._notifyModelChangeListeners("commit");
|
120
|
+
if (parent != this.modelRoot) {
|
121
|
+
if (parent.foldButton) parent.foldButton.removeClassName("ct_fold_empty");
|
122
|
+
}
|
123
|
+
},
|
124
|
+
|
125
|
+
moveElement: function(element) {
|
126
|
+
// TODO
|
127
|
+
},
|
128
|
+
|
129
|
+
removeElement: function(elements) {
|
130
|
+
if (!(elements instanceof Array)) elements = [ elements ];
|
131
|
+
elements.each(function(element) {
|
132
|
+
if (!element.hasClassName("ct_element")) throw new Error ("not an element");
|
133
|
+
var parent = element.up(".ct_element");
|
134
|
+
var feature = parent && element.up(".ct_containment");
|
135
|
+
parent = parent || this.modelRoot;
|
136
|
+
element.remove();
|
137
|
+
this._notifyModelChangeListeners("removed", element);
|
138
|
+
this._notifyModelChangeListeners("changed", parent, feature);
|
139
|
+
if (parent != this.modelRoot) {
|
140
|
+
if (parent.foldButton && !this._hasChildElements(parent)) parent.foldButton.addClassName("ct_fold_empty");
|
141
|
+
}
|
142
|
+
}, this);
|
143
|
+
this._notifyModelChangeListeners("commit");
|
144
|
+
},
|
145
|
+
|
146
|
+
createValue: function(target, where, text) {
|
147
|
+
if (!(["before", "after", "bottom"].include(where))) throw new Error ("unknown position");
|
148
|
+
if (where == "bottom" && !target.hasClassName("ct_slot")) throw new Error ("not a slot");
|
149
|
+
if (where != "bottom" && !target.hasClassName("ct_value")) throw new Error ("not a value");
|
150
|
+
text = (text || "").toString();
|
151
|
+
var feature = target.findAncestor(["ct_attribute", "ct_reference"]);
|
152
|
+
var valueNode = Concrete.Helper.createDOMNode('span', {class: 'ct_value'}, this._displayValue(text, feature));
|
153
|
+
valueNode.value = text;
|
154
|
+
var arg = {}; arg[where] = valueNode;
|
155
|
+
target.insert(arg);
|
156
|
+
this._notifyModelChangeListeners("changed", target.up(".ct_element"), feature);
|
157
|
+
this._notifyModelChangeListeners("commit");
|
158
|
+
},
|
159
|
+
|
160
|
+
changeValue: function(value, text) {
|
161
|
+
if (!value.hasClassName("ct_value")) throw new Error("not a value");
|
162
|
+
var feature = value.findAncestor(["ct_attribute", "ct_reference"]);
|
163
|
+
text = text.toString();
|
164
|
+
value.textContent = this._displayValue(text, feature);
|
165
|
+
value.value = text;
|
166
|
+
this._notifyModelChangeListeners("changed", value.up(".ct_element"), feature);
|
167
|
+
this._notifyModelChangeListeners("commit");
|
168
|
+
},
|
169
|
+
|
170
|
+
removeValue: function(value) {
|
171
|
+
if (!value.hasClassName("ct_value")) throw new Error("not a value");
|
172
|
+
var element = value.up(".ct_element");
|
173
|
+
var feature = value.findAncestor(["ct_attribute", "ct_reference"]);
|
174
|
+
value.remove();
|
175
|
+
this._notifyModelChangeListeners("changed", element, feature);
|
176
|
+
this._notifyModelChangeListeners("commit");
|
177
|
+
},
|
178
|
+
|
179
|
+
extractModel: function(element) {
|
180
|
+
var result = {_class: element.mmClass.name}
|
181
|
+
element.features.each(function(f) {
|
182
|
+
var childs = f.slot.childElements().reject(function(v){return v.hasClassName("ct_empty"); });
|
183
|
+
if (childs.size() > 0) {
|
184
|
+
if (f.mmFeature.isContainment()) {
|
185
|
+
var converted = childs.collect(function(v){return this.extractModel(v); }, this);
|
186
|
+
}
|
187
|
+
else if (f.mmFeature.isReference()) {
|
188
|
+
var converted = childs.collect(function(v){return v.value; }, this);
|
189
|
+
}
|
190
|
+
else {
|
191
|
+
var converted = childs.collect(function(v){
|
192
|
+
if (f.mmFeature.type.isInteger()) {
|
193
|
+
return parseInt(v.value);
|
194
|
+
}
|
195
|
+
else if (f.mmFeature.type.isFloat()) {
|
196
|
+
return parseFloat(v.value);
|
197
|
+
}
|
198
|
+
else if (f.mmFeature.type.isBoolean()) {
|
199
|
+
return v.value == "true";
|
200
|
+
}
|
201
|
+
else {
|
202
|
+
return v.value;
|
203
|
+
}
|
204
|
+
});
|
205
|
+
}
|
206
|
+
if (childs.size() == 1) {
|
207
|
+
result[f.mmFeature.name] = converted.first();
|
208
|
+
}
|
209
|
+
else {
|
210
|
+
result[f.mmFeature.name] = converted;
|
211
|
+
}
|
212
|
+
}
|
213
|
+
}, this)
|
214
|
+
return result;
|
215
|
+
},
|
216
|
+
|
217
|
+
// if no +element+ is provided redrawing will start on model root
|
218
|
+
redrawDisplayValues: function(element) {
|
219
|
+
if (!this._displayValueProvider) return;
|
220
|
+
if (element == undefined) {
|
221
|
+
this.modelRoot.childElements().each(function(c) {
|
222
|
+
this.redrawDisplayValues(c);
|
223
|
+
}, this);
|
224
|
+
}
|
225
|
+
else {
|
226
|
+
element.features.each(function(f) {
|
227
|
+
var childs = f.slot.childElements().reject(function(v){return v.hasClassName("ct_empty"); });
|
228
|
+
if (childs.size() > 0) {
|
229
|
+
if (f.mmFeature.isContainment()) {
|
230
|
+
childs.each(function(c) {
|
231
|
+
this.redrawDisplayValues(c);
|
232
|
+
}, this);
|
233
|
+
}
|
234
|
+
else {
|
235
|
+
childs.each(function(c) {
|
236
|
+
c.textContent = this._displayValue(c.value, f);
|
237
|
+
}, this);
|
238
|
+
}
|
239
|
+
}
|
240
|
+
}, this);
|
241
|
+
}
|
242
|
+
},
|
243
|
+
|
244
|
+
// Private
|
245
|
+
|
246
|
+
_hasChildElements: function(element) {
|
247
|
+
return element.features.any(function(f) {
|
248
|
+
return f.mmFeature.isContainment() && f.slot.childElements().any(function(c) {
|
249
|
+
return !c.hasClassName("ct_empty");
|
250
|
+
});
|
251
|
+
});
|
252
|
+
},
|
253
|
+
|
254
|
+
_notifyModelChangeListeners: function(type, element, feature) {
|
255
|
+
if (type == "changed")
|
256
|
+
if (element == this.modelRoot)
|
257
|
+
this._modelChangeListeners.each(function(l) {l.rootChanged(this.modelRoot);}, this);
|
258
|
+
else
|
259
|
+
this._modelChangeListeners.each(function(l) {l.elementChanged(element, feature);});
|
260
|
+
else if (type == "added")
|
261
|
+
this._modelChangeListeners.each(function(l) {l.elementAdded(element);});
|
262
|
+
else if (type == "removed")
|
263
|
+
this._modelChangeListeners.each(function(l) {l.elementRemoved(element);});
|
264
|
+
else if (type == "commit")
|
265
|
+
this._modelChangeListeners.each(function(l) {l.commitChanges();});
|
266
|
+
else
|
267
|
+
throw new Error("unknown type");
|
268
|
+
},
|
269
|
+
|
270
|
+
_collectElementsRecursive: function(element) {
|
271
|
+
var result = [element];
|
272
|
+
element.features.each(function(f) {
|
273
|
+
if (f.mmFeature.isContainment()) {
|
274
|
+
result = result.concat(f.slot.childElements().collect(function(c) {
|
275
|
+
return this._collectElementsRecursive(c);
|
276
|
+
}, this).flatten());
|
277
|
+
}
|
278
|
+
}, this);
|
279
|
+
return result;
|
280
|
+
},
|
281
|
+
|
282
|
+
// inserts a instance of the template representing element into slot
|
283
|
+
// also inserts template instances for all contained elements
|
284
|
+
// this function is optimized to minimize model load time
|
285
|
+
_instantiateTemplateRecursive: function(element, target, where, options) {
|
286
|
+
options = options || {};
|
287
|
+
var clazz = this.metamodelProvider.metaclassesByName[element._class];
|
288
|
+
if (!clazz) return;
|
289
|
+
var tmpl = this.templateProvider.templateByClass(clazz);
|
290
|
+
if (!tmpl.featurePositions) this._addTemplateInfo(tmpl);
|
291
|
+
|
292
|
+
var inst = Concrete.Helper.createDOMNode(tmpl.tagName, {class: tmpl.className, style: tmpl.readAttribute("style")},"");
|
293
|
+
if (where == "bottom") {
|
294
|
+
target.appendChild(inst);
|
295
|
+
}
|
296
|
+
else if (where == "before") {
|
297
|
+
target.parentNode.insertBefore(inst, target);
|
298
|
+
}
|
299
|
+
else if (where == "after") {
|
300
|
+
if (target.next()) {
|
301
|
+
target.parentNode.insertBefore(inst, target.next());
|
302
|
+
}
|
303
|
+
else {
|
304
|
+
target.parentNode.appendChild(inst);
|
305
|
+
}
|
306
|
+
}
|
307
|
+
// set inner HTML only after the new node has been hooked into its parent
|
308
|
+
// (otherwise the browser filters nodes like "tr" which it considers invalid at this place)
|
309
|
+
inst.innerHTML = tmpl.innerHTML;
|
310
|
+
inst.mmClass = tmpl.mmClass;
|
311
|
+
|
312
|
+
inst.features = [];
|
313
|
+
var childs = inst.allChildren();
|
314
|
+
var hasChildElements = false;
|
315
|
+
for (var i=0; i<tmpl.featurePositions.length; i++) {
|
316
|
+
var mmf = tmpl.mmFeatures[i];
|
317
|
+
var f = childs[tmpl.featurePositions[i]];
|
318
|
+
inst.features.push(f);
|
319
|
+
var values = element[mmf.name];
|
320
|
+
if (!(values instanceof Array)) values = [ values ].compact();
|
321
|
+
f.mmFeature = mmf;
|
322
|
+
var slot = childs[tmpl.slotPositions[i]];
|
323
|
+
f.slot = slot;
|
324
|
+
if (values.size() > 0) {
|
325
|
+
if (mmf.isContainment()) {
|
326
|
+
if (options.collapse) f.hide();
|
327
|
+
values.each(function(v) {
|
328
|
+
this._instantiateTemplateRecursive(v, slot, "bottom", options);
|
329
|
+
hasChildElements = true;
|
330
|
+
}, this);
|
331
|
+
}
|
332
|
+
else {
|
333
|
+
values.each(function(v) {
|
334
|
+
var vale = Concrete.Helper.createDOMNode('span', {class: 'ct_value'}, this._displayValue(v.toString(), f));
|
335
|
+
vale.value = v.toString();
|
336
|
+
slot.appendChild(vale);
|
337
|
+
}, this);
|
338
|
+
}
|
339
|
+
}
|
340
|
+
else if (f.hasClassName("ct_auto_hide")) {
|
341
|
+
f.hide();
|
342
|
+
}
|
343
|
+
if (f.hasClassName("ct_always_hide")) {
|
344
|
+
f.hide();
|
345
|
+
}
|
346
|
+
}
|
347
|
+
if (tmpl.foldButtonPosition != undefined) {
|
348
|
+
inst.foldButton = childs[tmpl.foldButtonPosition];
|
349
|
+
if (options.collapse) {
|
350
|
+
inst.foldButton.addClassName("ct_fold_closed");
|
351
|
+
}
|
352
|
+
else {
|
353
|
+
inst.foldButton.addClassName("ct_fold_open");
|
354
|
+
}
|
355
|
+
if (!hasChildElements) inst.foldButton.addClassName("ct_fold_empty");
|
356
|
+
}
|
357
|
+
return inst;
|
358
|
+
},
|
359
|
+
|
360
|
+
_displayValue: function(text, feature) {
|
361
|
+
if (this._displayValueProvider) {
|
362
|
+
return this._displayValueProvider(text, feature);
|
363
|
+
}
|
364
|
+
else {
|
365
|
+
return text;
|
366
|
+
}
|
367
|
+
},
|
368
|
+
|
369
|
+
// add information used to make template instantiation more efficient
|
370
|
+
_addTemplateInfo: function(tmpl) {
|
371
|
+
var allChilds = tmpl.allChildren();
|
372
|
+
var ftmpls = tmpl.select(".ct_attribute").concat(tmpl.select(".ct_reference")).concat(tmpl.select(".ct_containment"));
|
373
|
+
tmpl.featurePositions = ftmpls.collect(function(ft) { return allChilds.indexOf(ft); });
|
374
|
+
tmpl.mmFeatures = ftmpls.collect(function(ft) { return ft.mmFeature; });
|
375
|
+
tmpl.slotPositions = ftmpls.collect(function(ft) { return allChilds.indexOf(ft.down(".ct_slot")); });
|
376
|
+
var foldButton = tmpl.down(".ct_fold_button");
|
377
|
+
tmpl.foldButtonPosition = foldButton && allChilds.indexOf(foldButton);
|
378
|
+
}
|
379
|
+
|
380
|
+
});
|
381
|
+
|
382
|
+
Concrete.ModelInterface.Helper = {
|
383
|
+
|
384
|
+
// returns the next element in depth first search order
|
385
|
+
// or false if the last element in the model has been reached
|
386
|
+
//
|
387
|
+
// as a speed optimization an optional stack can be used which keeps the
|
388
|
+
// parent containers over several invocations of this method
|
389
|
+
// in this case the stack must either be empty or it must be in the state
|
390
|
+
// established by the last call of this method (i.e. it must not be modified)
|
391
|
+
//
|
392
|
+
nextElement: function(element, stack) {
|
393
|
+
var fIndex = 0;
|
394
|
+
while (true) {
|
395
|
+
var feature = element.features[fIndex];
|
396
|
+
var values = feature && element.featureValues(feature.mmFeature.name);
|
397
|
+
if (feature && feature.mmFeature.isContainment() && values.size() > 0 && values[0].isElement()) {
|
398
|
+
// found first child in feature
|
399
|
+
if (stack) {
|
400
|
+
stack.push(element);
|
401
|
+
stack.push(feature);
|
402
|
+
}
|
403
|
+
return values[0];
|
404
|
+
}
|
405
|
+
else if (fIndex < element.features.size()-1) {
|
406
|
+
// next feature
|
407
|
+
fIndex++;
|
408
|
+
}
|
409
|
+
else if (element.next()) {
|
410
|
+
// next element
|
411
|
+
return element.next();
|
412
|
+
}
|
413
|
+
else {
|
414
|
+
var parentFeature = (stack && stack.pop()) || element.up(".ct_containment");
|
415
|
+
if (parentFeature) {
|
416
|
+
// go up to parent
|
417
|
+
var parentElement = (stack && stack.pop()) || parentFeature.up(".ct_element");
|
418
|
+
var fIndex = parentElement.features.indexOf(parentFeature) + 1;
|
419
|
+
element = parentElement;
|
420
|
+
}
|
421
|
+
else {
|
422
|
+
return false;
|
423
|
+
}
|
424
|
+
}
|
425
|
+
}
|
426
|
+
}
|
427
|
+
|
428
|
+
};
|
429
|
+
|