midas 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.bundle/config +2 -0
- data/.document +5 -0
- data/.gitignore +32 -0
- data/Gemfile +12 -0
- data/LICENSE +20 -0
- data/README.textile +21 -0
- data/Rakefile +84 -0
- data/VERSION +1 -0
- data/features/step_definitions/editor_steps.rb +16 -0
- data/features/step_definitions/web_steps.rb +205 -0
- data/features/support/env.rb +39 -0
- data/features/support/paths.rb +29 -0
- data/features/view_editor.feature +8 -0
- data/generators/midas/midas_generator.rb +1 -0
- data/lib/midas.rb +1 -0
- data/midas.gemspec +131 -0
- data/public/images/midas/toolbars/actions/_background.png +0 -0
- data/public/images/midas/toolbars/actions/_background_radio.png +0 -0
- data/public/images/midas/toolbars/actions/_separator.png +0 -0
- data/public/images/midas/toolbars/actions/extra/prefspane.png +0 -0
- data/public/images/midas/toolbars/actions/extra/todospane.png +0 -0
- data/public/images/midas/toolbars/actions/historypanel.png +0 -0
- data/public/images/midas/toolbars/actions/insertcharacter.png +0 -0
- data/public/images/midas/toolbars/actions/insertlink.png +0 -0
- data/public/images/midas/toolbars/actions/insertmedia.png +0 -0
- data/public/images/midas/toolbars/actions/insertobject.png +0 -0
- data/public/images/midas/toolbars/actions/inserttable.png +0 -0
- data/public/images/midas/toolbars/actions/inspectorpanel.png +0 -0
- data/public/images/midas/toolbars/actions/notespanel.png +0 -0
- data/public/images/midas/toolbars/actions/preview.png +0 -0
- data/public/images/midas/toolbars/actions/redo.png +0 -0
- data/public/images/midas/toolbars/actions/save.png +0 -0
- data/public/images/midas/toolbars/actions/undo.png +0 -0
- data/public/images/midas/toolbars/htmleditor/_background.png +0 -0
- data/public/images/midas/toolbars/htmleditor/_line_separator.png +0 -0
- data/public/images/midas/toolbars/htmleditor/_separator.png +0 -0
- data/public/images/midas/toolbars/htmleditor/buttons.png +0 -0
- data/public/javascripts/midas/config.js +181 -0
- data/public/javascripts/midas/dialog.js +9 -0
- data/public/javascripts/midas/midas.js +307 -0
- data/public/javascripts/midas/native_extensions.js +43 -0
- data/public/javascripts/midas/palette.js +108 -0
- data/public/javascripts/midas/region.js +194 -0
- data/public/javascripts/midas/statusbar.js +84 -0
- data/public/javascripts/midas/toolbar.js +255 -0
- data/public/javascripts/prototype.js +6001 -0
- data/public/midas/backcolor.html +97 -0
- data/public/midas/examples/bundled.html +60 -0
- data/public/midas/examples/iframe.html +73 -0
- data/public/midas/examples/index.html +73 -0
- data/public/midas/examples/javascript_archive.js +111 -0
- data/public/midas/forecolor.html +97 -0
- data/public/stylesheets/midas/dialog.css +2 -0
- data/public/stylesheets/midas/midas.css +9 -0
- data/public/stylesheets/midas/palette.css +50 -0
- data/public/stylesheets/midas/region.css +16 -0
- data/public/stylesheets/midas/statusbar.css +17 -0
- data/public/stylesheets/midas/toolbar.css +262 -0
- data/rails/init.rb +1 -0
- data/spec/javascripts/dialog_spec.js +7 -0
- data/spec/javascripts/fixtures/midas_fixture.html +27 -0
- data/spec/javascripts/fixtures/midas_styles.css +14 -0
- data/spec/javascripts/fixtures/native_extensions_fixture.html +5 -0
- data/spec/javascripts/helpers/browser_detection.js +15 -0
- data/spec/javascripts/helpers/event_simulation.js +505 -0
- data/spec/javascripts/helpers/spec_helper.js +125 -0
- data/spec/javascripts/midas_spec.js +284 -0
- data/spec/javascripts/native_extensions_spec.js +39 -0
- data/spec/javascripts/palette_spec.js +59 -0
- data/spec/javascripts/region_spec.js +441 -0
- data/spec/javascripts/statusbar_spec.js +61 -0
- data/spec/javascripts/support/jasmine.yml +82 -0
- data/spec/javascripts/support/jasmine_config.rb +39 -0
- data/spec/javascripts/support/jasmine_runner.rb +19 -0
- data/spec/javascripts/toolbar_spec.js +149 -0
- data/spec/ruby/helpers/spec_helper.rb +9 -0
- data/spec/ruby/midas_spec.rb +9 -0
- data/spec/spec.opts +1 -0
- data/tasks/midas_tasks.rake +105 -0
- metadata +166 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
NodeList.prototype.equals = function(that) {
|
2
|
+
var length = this.length;
|
3
|
+
if (length != that.length) return false;
|
4
|
+
|
5
|
+
var thisItem, thatItem;
|
6
|
+
length = length - 1;
|
7
|
+
for (var i = 0; i <= length; ++i) {
|
8
|
+
thisItem = this[i];
|
9
|
+
thatItem = that.item(i);
|
10
|
+
|
11
|
+
if (thisItem.nodeType != thatItem.nodeType || thisItem.nodeName != thatItem.nodeName) return false;
|
12
|
+
}
|
13
|
+
|
14
|
+
for (var i = 0; i <= length; ++i) {
|
15
|
+
thisItem = this.item(i);
|
16
|
+
thatItem = that.item(i);
|
17
|
+
|
18
|
+
if (thisItem.nodeType == 3) {
|
19
|
+
if (thisItem.innerText != thatItem.innerText) return false;
|
20
|
+
} else if (thisItem.innerHTML && thatItem.innerHTML) {
|
21
|
+
if (thisItem.innerHTML != thatItem.innerHTML) return false;
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
return true;
|
26
|
+
};
|
27
|
+
|
28
|
+
Number.prototype.toHex = function() {
|
29
|
+
var result = this.toString(16).toUpperCase();
|
30
|
+
return result[1] ? result : "0" + result;
|
31
|
+
};
|
32
|
+
|
33
|
+
String.prototype.toHex = function() {
|
34
|
+
return this.replace(/rgb\((\d+)[\s|\,]?\s(\d+)[\s|\,]?\s(\d+)\)/gi,
|
35
|
+
function (a, b, c, d) {
|
36
|
+
return "#" + parseInt(b).toHex() + parseInt(c).toHex() + parseInt(d).toHex();
|
37
|
+
}
|
38
|
+
)
|
39
|
+
};
|
40
|
+
|
41
|
+
window.isTop = function() {
|
42
|
+
return (this == top);
|
43
|
+
};
|
@@ -0,0 +1,108 @@
|
|
1
|
+
if (!Midas) var Midas = {};
|
2
|
+
Midas.Palette = Class.create({
|
3
|
+
version: 0.2,
|
4
|
+
button: null,
|
5
|
+
element: null,
|
6
|
+
setupFunction: null,
|
7
|
+
options: {
|
8
|
+
url: null,
|
9
|
+
configuration: null
|
10
|
+
},
|
11
|
+
|
12
|
+
initialize: function(button, name, toolbar, options) {
|
13
|
+
if (!Midas.version) throw('Midas.Region requires Midas');
|
14
|
+
|
15
|
+
this.button = button;
|
16
|
+
this.name = name;
|
17
|
+
this.toolbar = toolbar;
|
18
|
+
|
19
|
+
this.options = Object.extend(Object.clone(this.options), options);
|
20
|
+
this.options['configuration'] = this.options['configuration'] || Midas.Config;
|
21
|
+
this.config = this.options['configuration'];
|
22
|
+
|
23
|
+
this.build();
|
24
|
+
this.setupObservers();
|
25
|
+
},
|
26
|
+
|
27
|
+
build: function() {
|
28
|
+
this.element = new Element('div', {'class': 'midas-palette', style: 'display:none;'});
|
29
|
+
this.toolbar.element.appendChild(this.element);
|
30
|
+
},
|
31
|
+
|
32
|
+
setupObservers: function() {
|
33
|
+
Event.observe(window, 'resize', this.position.bind(this));
|
34
|
+
Event.observe(this.element, 'mousedown', function(event) {
|
35
|
+
event.stop();
|
36
|
+
});
|
37
|
+
Event.observe(this.button, 'click', function() {
|
38
|
+
if (!this.element) return;
|
39
|
+
if (this.visible()) this.hide();
|
40
|
+
else this.show();
|
41
|
+
}.bind(this));
|
42
|
+
},
|
43
|
+
|
44
|
+
show: function() {
|
45
|
+
if (!this.loaded) {
|
46
|
+
this.load(this.show.bind(this));
|
47
|
+
return;
|
48
|
+
}
|
49
|
+
|
50
|
+
this.position();
|
51
|
+
this.element.show();
|
52
|
+
},
|
53
|
+
|
54
|
+
hide: function() {
|
55
|
+
this.element.hide();
|
56
|
+
},
|
57
|
+
|
58
|
+
position: function() {
|
59
|
+
if (!this.element) return;
|
60
|
+
|
61
|
+
var keepVisible = this.visible();
|
62
|
+
this.element.setStyle({top: 0, left: 0, display: 'block', visibility: 'hidden'});
|
63
|
+
var position = this.button.cumulativeOffset();
|
64
|
+
var dimensions = this.element.getDimensions();
|
65
|
+
if (position.left + dimensions.width > document.viewport.getWidth()) {
|
66
|
+
position.left = position.left - dimensions.width + this.button.getWidth();
|
67
|
+
}
|
68
|
+
|
69
|
+
this.element.setStyle({
|
70
|
+
top: (position.top + this.button.getHeight()) + 'px',
|
71
|
+
left: position.left + 'px',
|
72
|
+
display: keepVisible ? 'block' : 'none',
|
73
|
+
visibility: 'visible'
|
74
|
+
});
|
75
|
+
},
|
76
|
+
|
77
|
+
visible: function() {
|
78
|
+
return (!this.element || this.element.getStyle('display') == 'block');
|
79
|
+
},
|
80
|
+
|
81
|
+
load: function(callback) {
|
82
|
+
new Ajax.Request(this.options.url, {
|
83
|
+
method: 'get',
|
84
|
+
onSuccess: function(transport) {
|
85
|
+
this.loaded = true;
|
86
|
+
this.element.innerHTML = transport.responseText;
|
87
|
+
transport.responseText.evalScripts();
|
88
|
+
|
89
|
+
this.setupFunction = window['setup_' + this.name];
|
90
|
+
if (this.setupFunction) this.setupFunction.call(this);
|
91
|
+
|
92
|
+
if (callback) callback();
|
93
|
+
}.bind(this),
|
94
|
+
onFailure: function() {
|
95
|
+
alert('unable to get the palette contents');
|
96
|
+
}
|
97
|
+
});
|
98
|
+
},
|
99
|
+
|
100
|
+
execute: function(action, options, event) {
|
101
|
+
Midas.fire('button', {action: this.name, event: event, toolbar: this.toolbar, options: options});
|
102
|
+
},
|
103
|
+
|
104
|
+
destroy: function() {
|
105
|
+
if (this.element) this.element.remove();
|
106
|
+
this.element = null;
|
107
|
+
}
|
108
|
+
});
|
@@ -0,0 +1,194 @@
|
|
1
|
+
if (!Midas) var Midas = {};
|
2
|
+
Midas.Region = Class.create({
|
3
|
+
version: 0.2,
|
4
|
+
name: null,
|
5
|
+
options: {
|
6
|
+
configuration: null,
|
7
|
+
contentWindow: window,
|
8
|
+
inline: false
|
9
|
+
},
|
10
|
+
|
11
|
+
initialize: function(element, options, name) {
|
12
|
+
if (!Midas.version) throw('Midas.Region requires Midas');
|
13
|
+
if (!Midas.agentIsCapable()) throw('Midas.Region requires a browser that has contentEditable features');
|
14
|
+
|
15
|
+
this.element = $(element);
|
16
|
+
if (!this.element) throw('Unable to locate the element "' + element + '"');
|
17
|
+
this.sibling = this.element.previousSibling;
|
18
|
+
|
19
|
+
this.options = Object.extend(Object.clone(this.options), options);
|
20
|
+
this.options['configuration'] = this.options['configuration'] || Midas.Config;
|
21
|
+
this.config = this.options['configuration'];
|
22
|
+
this.name = this.element.getAttribute('id') || name;
|
23
|
+
this.doc = this.options['contentWindow'].document;
|
24
|
+
this.selections = [];
|
25
|
+
|
26
|
+
this.makeEditable();
|
27
|
+
this.setupObservers();
|
28
|
+
},
|
29
|
+
|
30
|
+
makeEditable: function() {
|
31
|
+
this.element.addClassName('midas-region');
|
32
|
+
|
33
|
+
if (this.element.innerHTML.replace(/^\s+|\s+$/g, "") == '' && Prototype.Browser.Gecko) {
|
34
|
+
this.setContents(' ')
|
35
|
+
}
|
36
|
+
|
37
|
+
if (this.options['inline']) {
|
38
|
+
this.element.setStyle({height: 'auto', minHeight: '20px'});
|
39
|
+
} else {
|
40
|
+
this.element.setStyle({overflow: 'auto'});
|
41
|
+
this.element.setStyle({maxWidth: this.element.getWidth() + 'px'});
|
42
|
+
}
|
43
|
+
this.element.contentEditable = true;
|
44
|
+
|
45
|
+
this.doc.execCommand('styleWithCSS', false, false);
|
46
|
+
//this.doc.execCommand('enableInlineTableEditing', false, false);
|
47
|
+
},
|
48
|
+
|
49
|
+
setupObservers: function() {
|
50
|
+
this.element.observe('focus', function(event) {
|
51
|
+
Midas.fire('region', {region: this, name: this.name, event: event});
|
52
|
+
if (this.getContents() == ' ' && Prototype.Browser.Gecko) this.setContents(' ');
|
53
|
+
}.bind(this));
|
54
|
+
|
55
|
+
this.element.observe('click', function(event) {
|
56
|
+
Midas.fire('region', {region: this, name: this.name, event: event});
|
57
|
+
if (this.getContents() == ' ' && Prototype.Browser.Gecko) this.setContents(' ');
|
58
|
+
}.bind(this));
|
59
|
+
this.element.observe('mouseup', function(event) {
|
60
|
+
Midas.fire('region:update', {region: this, name: this.name, event: event});
|
61
|
+
}.bind(this));
|
62
|
+
|
63
|
+
this.element.observe('keyup', function(event) {
|
64
|
+
Midas.fire('region:update', {region: this, name: this.name, event: event, changed: true});
|
65
|
+
}.bind(this));
|
66
|
+
this.element.observe('keypress', function(event) {
|
67
|
+
Midas.fire('region:update', {region: this, name: this.name, event: event});
|
68
|
+
|
69
|
+
switch(event.keyCode) {
|
70
|
+
case 9: // tab
|
71
|
+
this.selections.each(function(selection) {
|
72
|
+
var container = selection.commonAncestorContainer;
|
73
|
+
if (container.nodeType == 3) container = container.parentNode;
|
74
|
+
if (container.tagName == 'LI' || container.up('li')) {
|
75
|
+
event.stop();
|
76
|
+
this.handleAction('indent');
|
77
|
+
}
|
78
|
+
}.bind(this));
|
79
|
+
break;
|
80
|
+
case 13: // enter
|
81
|
+
break;
|
82
|
+
}
|
83
|
+
|
84
|
+
}.bind(this));
|
85
|
+
|
86
|
+
// selection tracking
|
87
|
+
this.element.observe('keyup', function() {
|
88
|
+
this.updateSelections();
|
89
|
+
}.bind(this));
|
90
|
+
this.element.observe('mousedown', function() {
|
91
|
+
this.selecting = true;
|
92
|
+
}.bind(this));
|
93
|
+
Event.observe(document, 'mouseup', function() {
|
94
|
+
if (!this.selecting) return;
|
95
|
+
this.selecting = false;
|
96
|
+
this.updateSelections();
|
97
|
+
}.bind(this));
|
98
|
+
},
|
99
|
+
|
100
|
+
setContents: function(content) {
|
101
|
+
this.element.innerHTML = content;
|
102
|
+
},
|
103
|
+
|
104
|
+
getContents: function() {
|
105
|
+
return this.element.innerHTML.replace(/^\s+|\s+$/g, "");
|
106
|
+
},
|
107
|
+
|
108
|
+
updateSelections: function() {
|
109
|
+
var selection = window.getSelection();
|
110
|
+
this.selections = [];
|
111
|
+
|
112
|
+
for (var i = 0; i <= selection.rangeCount - 1; ++i) {
|
113
|
+
var range = selection.getRangeAt(i);
|
114
|
+
|
115
|
+
if (range.commonAncestorContainer == this.element ||
|
116
|
+
Element.descendantOf(range.commonAncestorContainer, this.element)) {
|
117
|
+
this.selections.push(range);
|
118
|
+
}
|
119
|
+
}
|
120
|
+
},
|
121
|
+
|
122
|
+
execCommand: function(action, argument) {
|
123
|
+
argument = typeof(argument) == 'undefined' ? null : argument;
|
124
|
+
|
125
|
+
var supported = this.doc.execCommand('styleWithCSS', false, false);
|
126
|
+
var handled;
|
127
|
+
try {
|
128
|
+
handled = this.doc.execCommand(action, false, argument);
|
129
|
+
} catch(e) {
|
130
|
+
Midas.trace(e);
|
131
|
+
|
132
|
+
// Gecko does some interesting things when it fails on indent
|
133
|
+
if (action == 'indent') {
|
134
|
+
var sibling = this.element.previousSibling;
|
135
|
+
if (sibling != this.sibling) sibling.remove();
|
136
|
+
}
|
137
|
+
handled = true;
|
138
|
+
}
|
139
|
+
if (!handled && supported) throw('Unknown action "' + action + '"');
|
140
|
+
},
|
141
|
+
|
142
|
+
serialize: function() {
|
143
|
+
return {name: this.name, content: this.getContents()}
|
144
|
+
},
|
145
|
+
|
146
|
+
destroy: function() {
|
147
|
+
this.element.contentEditable = 'false';
|
148
|
+
this.element.blur();
|
149
|
+
this.element.removeClassName('midas-region');
|
150
|
+
},
|
151
|
+
|
152
|
+
handleAction: function(action, event, toolbar, options) {
|
153
|
+
options = options || {};
|
154
|
+
|
155
|
+
if (this.config['behaviors'][action]) {
|
156
|
+
var behaviors = this.config['behaviors'][action];
|
157
|
+
|
158
|
+
for (var behavior in behaviors) {
|
159
|
+
if (Object.isFunction(this.handle[behavior])) {
|
160
|
+
this.handle[behavior].apply(this, Object.isArray(behaviors[behavior]) ? behaviors[behavior] : [behaviors[behavior]]);
|
161
|
+
|
162
|
+
var sel = window.getSelection();
|
163
|
+
this.selections.each(function(selection) {
|
164
|
+
sel.removeRange(selection);
|
165
|
+
sel.addRange(selection);
|
166
|
+
})
|
167
|
+
} else {
|
168
|
+
throw('Unknown behavior method "' + behavior + '"');
|
169
|
+
}
|
170
|
+
}
|
171
|
+
} else {
|
172
|
+
switch (action) {
|
173
|
+
case 'removeformatting':
|
174
|
+
this.handle['insertHTML'].call(this, function() {
|
175
|
+
return (this.selections[0]) ? this.selections[0].cloneContents().textContent : '';
|
176
|
+
});
|
177
|
+
break;
|
178
|
+
default: this.execCommand(action, options['value']);
|
179
|
+
}
|
180
|
+
}
|
181
|
+
},
|
182
|
+
|
183
|
+
handle: {
|
184
|
+
|
185
|
+
insertHTML: function(callback) {
|
186
|
+
this.execCommand('insertHTML', callback.call(this))
|
187
|
+
},
|
188
|
+
|
189
|
+
execCommand: function(action, argument) {
|
190
|
+
this.execCommand(action, argument);
|
191
|
+
}
|
192
|
+
|
193
|
+
}
|
194
|
+
});
|
@@ -0,0 +1,84 @@
|
|
1
|
+
if (!Midas) var Midas = {};
|
2
|
+
Midas.Statusbar = Class.create({
|
3
|
+
version: 0.2,
|
4
|
+
options: {
|
5
|
+
appendTo: null,
|
6
|
+
configuration: null,
|
7
|
+
contentWindow: window,
|
8
|
+
panels: ['Path']
|
9
|
+
},
|
10
|
+
|
11
|
+
initialize: function(options) {
|
12
|
+
if (!Midas.version) throw('Midas.Statusbar requires Midas');
|
13
|
+
|
14
|
+
this.options = Object.extend(Object.clone(this.options), options);
|
15
|
+
this.options['configuration'] = this.options['configuration'] || Midas.Config;
|
16
|
+
this.config = this.options['configuration'];
|
17
|
+
|
18
|
+
this.build();
|
19
|
+
},
|
20
|
+
|
21
|
+
build: function() {
|
22
|
+
this.element = new Element('div', {'class': 'midas-statusbar'});
|
23
|
+
|
24
|
+
this.options.panels.each(function(method) {
|
25
|
+
this.element.innerHTML += this['insert' + method].call(this);
|
26
|
+
}.bind(this));
|
27
|
+
|
28
|
+
($(this.options['appendTo']) || document.body).appendChild(this.element);
|
29
|
+
},
|
30
|
+
|
31
|
+
update: function(region, event) {
|
32
|
+
setTimeout(function() {
|
33
|
+
this.element.innerHTML = '';
|
34
|
+
this.options.panels.each(function(method) {
|
35
|
+
this['insert' + method].call(this, region, event);
|
36
|
+
}.bind(this));
|
37
|
+
}.bind(this), 1);
|
38
|
+
},
|
39
|
+
|
40
|
+
getHeight: function() {
|
41
|
+
return ($(this.options['appendTo']) || this.element).getHeight();
|
42
|
+
},
|
43
|
+
|
44
|
+
destroy: function() {
|
45
|
+
this.element.remove();
|
46
|
+
},
|
47
|
+
|
48
|
+
insertPath: function(region, event) {
|
49
|
+
if (!event) return '<span><strong>Path:</strong></span>';
|
50
|
+
|
51
|
+
var selection = this.options['contentWindow'].getSelection();
|
52
|
+
if (!selection.rangeCount) return;
|
53
|
+
var range = selection.getRangeAt(0);
|
54
|
+
|
55
|
+
var path = '';
|
56
|
+
var node = range.commonAncestorContainer;
|
57
|
+
node = node.nodeType == 3 ? Element.up(node) : node;
|
58
|
+
this.path = [];
|
59
|
+
|
60
|
+
if (node != region.element && Element.descendantOf(node, region.element)) {
|
61
|
+
this.path = Element.ancestors(node);
|
62
|
+
|
63
|
+
var length = this.path.length - 1;
|
64
|
+
for (var i = 0; i <= length; ++i) {
|
65
|
+
if (this.path[i] == region.element) break;
|
66
|
+
path = '<a>' + this.path[i].tagName.toLowerCase() + '</a> » ' + path;
|
67
|
+
}
|
68
|
+
path += '<a>' + node.tagName.toLowerCase() + '</a>';
|
69
|
+
this.path[-1] = node;
|
70
|
+
}
|
71
|
+
|
72
|
+
var element = new Element('span').update('<strong>Path:</strong> ' + path);
|
73
|
+
element.observe('click', function(event) {
|
74
|
+
var index = Element.nextSiblings(Event.element(event)).length;
|
75
|
+
var selection = window.getSelection();
|
76
|
+
var range = document.createRange();
|
77
|
+
range.selectNode(this.path[index - 1]);
|
78
|
+
selection.removeAllRanges();
|
79
|
+
selection.addRange(range);
|
80
|
+
}.bindAsEventListener(this));
|
81
|
+
|
82
|
+
this.element.appendChild(element);
|
83
|
+
}
|
84
|
+
});
|
@@ -0,0 +1,255 @@
|
|
1
|
+
if (!Midas) var Midas = {};
|
2
|
+
Midas.Toolbar = Class.create({
|
3
|
+
version: 0.2,
|
4
|
+
contexts: [],
|
5
|
+
buttons: {},
|
6
|
+
palettes: [],
|
7
|
+
options: {
|
8
|
+
appendTo: null,
|
9
|
+
contentWindow: window,
|
10
|
+
configuration: null
|
11
|
+
},
|
12
|
+
|
13
|
+
initialize: function(options) {
|
14
|
+
if (!Midas.version) throw('Midas.Toolbar requires Midas');
|
15
|
+
this.palettes = [];
|
16
|
+
|
17
|
+
this.options = Object.extend(Object.clone(this.options), options);
|
18
|
+
this.options['configuration'] = this.options['configuration'] || Midas.Config;
|
19
|
+
this.config = this.options['configuration'];
|
20
|
+
|
21
|
+
this.build();
|
22
|
+
this.setupObservers();
|
23
|
+
},
|
24
|
+
|
25
|
+
build: function() {
|
26
|
+
this.element = new Element('div', {id: this.options['id'] || this.generateId()}).addClassName('midas-toolbar');
|
27
|
+
|
28
|
+
if (this.config['toolbars']) {
|
29
|
+
for (var toolbar in this.config['toolbars']) {
|
30
|
+
var element = new Element('div').addClassName('midas-' + toolbar + 'bar');
|
31
|
+
var buttons = this.config['toolbars'][toolbar];
|
32
|
+
for (var button in buttons) {
|
33
|
+
element.appendChild(this.makeButton(button, buttons[button]));
|
34
|
+
}
|
35
|
+
this.element.appendChild(element);
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
($(this.options['appendTo']) || document.body).appendChild(this.element);
|
40
|
+
},
|
41
|
+
|
42
|
+
setupObservers: function() {
|
43
|
+
Event.observe(this.element, 'mousedown', function(e) {
|
44
|
+
e.stop();
|
45
|
+
}.bind(this));
|
46
|
+
Event.observe(document, 'mouseup', function(e) {
|
47
|
+
var element = Event.element(e);
|
48
|
+
this.palettes.each(function(palette) {
|
49
|
+
if (element != palette.element || element.descendantOf(palette.element)) palette.hide();
|
50
|
+
}.bind(this));
|
51
|
+
}.bind(this))
|
52
|
+
},
|
53
|
+
|
54
|
+
generateId: function() {
|
55
|
+
if (this.id) return this.id;
|
56
|
+
|
57
|
+
var id = null;
|
58
|
+
var element = '';
|
59
|
+
while (element !== null) {
|
60
|
+
id = 'midas_toolbar' + parseInt(Math.random() * 10000);
|
61
|
+
element = $(id);
|
62
|
+
}
|
63
|
+
|
64
|
+
this.id = id;
|
65
|
+
return id;
|
66
|
+
},
|
67
|
+
|
68
|
+
makeButton: function(action, buttonSpec) {
|
69
|
+
var element;
|
70
|
+
if (Object.isArray(buttonSpec)) {
|
71
|
+
var types = buttonSpec.without(buttonSpec[0]).without(buttonSpec[1]);
|
72
|
+
|
73
|
+
element = new Element('div', {title: buttonSpec[1] ? buttonSpec[1] : buttonSpec[0], 'class': 'midas-button'});
|
74
|
+
element.update('<em>' + buttonSpec[0] + '</em>');
|
75
|
+
element.addClassName('midas-button-' + action.replace('_', '-'));
|
76
|
+
element.observe('click', function(event) {
|
77
|
+
event.stop();
|
78
|
+
element.blur();
|
79
|
+
Midas.fire('button', {
|
80
|
+
action: action,
|
81
|
+
event: event,
|
82
|
+
toolbar: this
|
83
|
+
});
|
84
|
+
}.bind(this));
|
85
|
+
|
86
|
+
types.each(function(buttonType) {
|
87
|
+
var type = buttonType[0];
|
88
|
+
var mixed = buttonType[1];
|
89
|
+
switch(type) {
|
90
|
+
case 'toggle':
|
91
|
+
element.observe('click', function() {
|
92
|
+
element.toggleClassName('pressed');
|
93
|
+
});
|
94
|
+
break;
|
95
|
+
case 'context':
|
96
|
+
this.contexts.push({element: element, callback: mixed || action});
|
97
|
+
break;
|
98
|
+
case 'mode':
|
99
|
+
element.observe('click', function() {
|
100
|
+
Midas.fire('mode', {mode: mixed || action, toolbar: this});
|
101
|
+
}.bind(this));
|
102
|
+
break;
|
103
|
+
case 'dialog':
|
104
|
+
if (!mixed) throw('Button "' + action + '" is missing arguments');
|
105
|
+
element.observe('click', function() {
|
106
|
+
var url = Object.isFunction(mixed) ? mixed.apply(this, [action]) : mixed;
|
107
|
+
alert('this would open a dialog with the url: ' + url);
|
108
|
+
}.bind(this));
|
109
|
+
break;
|
110
|
+
case 'panel':
|
111
|
+
if (!mixed) throw('Button "' + action + '" is missing arguments');
|
112
|
+
element.observe('click', function() {
|
113
|
+
var url = Object.isFunction(mixed) ? mixed.apply(this, [action]) : mixed;
|
114
|
+
alert('this would open a panel with the url: ' + url);
|
115
|
+
}.bind(this));
|
116
|
+
break;
|
117
|
+
case 'palette':
|
118
|
+
if (!mixed) throw('Button "' + action + '" is missing arguments');
|
119
|
+
this.palettes.push(new Midas.Palette(element, action, this, {url: Object.isFunction(mixed) ? mixed.apply(this, [action]) : mixed}));
|
120
|
+
break;
|
121
|
+
case 'select':
|
122
|
+
if (!mixed) throw('Button "' + action + '" is missing arguments');
|
123
|
+
element.observe('click', function() {
|
124
|
+
var contents = Object.isFunction(mixed) ? mixed.apply(this, [action]) : mixed;
|
125
|
+
alert('this would open a place a pulldown near the button with the contents: ' + contents.join(','));
|
126
|
+
}.bind(this));
|
127
|
+
break;
|
128
|
+
default:
|
129
|
+
throw('Unknown button type "' + type + '" for the "' + action + '" button');
|
130
|
+
}
|
131
|
+
}.bind(this));
|
132
|
+
this.buttons[action] = {element: element, spec: buttonSpec};
|
133
|
+
} else if (Object.isString(buttonSpec)) {
|
134
|
+
element = this.makeSeparator(buttonSpec);
|
135
|
+
} else {
|
136
|
+
element = this.makeButtonGroup(buttonSpec);
|
137
|
+
}
|
138
|
+
return element;
|
139
|
+
},
|
140
|
+
|
141
|
+
makeButtonGroup: function(group) {
|
142
|
+
var element = new Element('div').addClassName('midas-group');
|
143
|
+
for (var button in group) {
|
144
|
+
element.appendChild(this.makeButton(button, group[button]));
|
145
|
+
}
|
146
|
+
return element;
|
147
|
+
},
|
148
|
+
|
149
|
+
makeSeparator: function(button) {
|
150
|
+
return new Element('span').addClassName('midas-' + (button == '*' ? 'flex-separator' : button == '-' ? 'line-separator' : 'separator'));
|
151
|
+
},
|
152
|
+
|
153
|
+
setActiveButtons: function(regions, activeRegion) {
|
154
|
+
var selection = this.options['contentWindow'].getSelection();
|
155
|
+
if (!selection.rangeCount) return;
|
156
|
+
|
157
|
+
var range = selection.getRangeAt(0);
|
158
|
+
|
159
|
+
var node = range.commonAncestorContainer;
|
160
|
+
node = node.nodeType == 3 ? Element.up(node) : node;
|
161
|
+
if (!node) return;
|
162
|
+
|
163
|
+
var length = this.contexts.length;
|
164
|
+
for (var i = 0; i < length; ++i) {
|
165
|
+
var context = this.contexts[i];
|
166
|
+
|
167
|
+
var callback;
|
168
|
+
if (typeof(context['callback']) == 'function') {
|
169
|
+
callback = context['callback'];
|
170
|
+
} else {
|
171
|
+
callback = Midas.Toolbar.contexts[context['callback']];
|
172
|
+
}
|
173
|
+
|
174
|
+
if (typeof(callback) == 'function') {
|
175
|
+
if (callback.call(this, node, activeRegion)) {
|
176
|
+
context['element'].addClassName('active');
|
177
|
+
} else {
|
178
|
+
context['element'].removeClassName('active');
|
179
|
+
}
|
180
|
+
}
|
181
|
+
}
|
182
|
+
},
|
183
|
+
|
184
|
+
unsetActiveButtons: function() {
|
185
|
+
this.element.select('.active').each(function(button) {
|
186
|
+
button.removeClassName('active');
|
187
|
+
});
|
188
|
+
},
|
189
|
+
|
190
|
+
getHeight: function() {
|
191
|
+
return ($(this.options['appendTo']) || this.element).getHeight();
|
192
|
+
},
|
193
|
+
|
194
|
+
destroy: function() {
|
195
|
+
this.palettes.each(function(palette) {
|
196
|
+
if (palette.destroy) palette.destroy();
|
197
|
+
});
|
198
|
+
this.palettes = [];
|
199
|
+
if (this.element) this.element.remove();
|
200
|
+
if (this.element) this.element = null;
|
201
|
+
}
|
202
|
+
});
|
203
|
+
|
204
|
+
// Midas.Toolbar static methods
|
205
|
+
Object.extend(Midas.Toolbar, {
|
206
|
+
contexts: {
|
207
|
+
backcolor: function(node) {
|
208
|
+
this.buttons['backcolor']['element'].setStyle('background-color:' + node.getStyle('background-color'));
|
209
|
+
},
|
210
|
+
forecolor: function(node) {
|
211
|
+
this.buttons['forecolor']['element'].setStyle('background-color:' + node.getStyle('color'));
|
212
|
+
},
|
213
|
+
bold: function(node) {
|
214
|
+
var weight = Element.getStyle(node, 'font-weight');
|
215
|
+
return weight == 'bold' || weight > 400;
|
216
|
+
},
|
217
|
+
italic: function(node) {
|
218
|
+
return Element.getStyle(node, 'font-style') == 'italic' || node.nodeName == 'I' || node.up('i') || node.nodeName == 'EM' || node.up('em');
|
219
|
+
},
|
220
|
+
strikethrough: function(node) {
|
221
|
+
return Element.getStyle(node, 'text-decoration') == 'line-through' || node.nodeName == 'STRIKE' || node.up('strike');
|
222
|
+
},
|
223
|
+
underline: function(node) {
|
224
|
+
return Element.getStyle(node, 'text-decoration') == 'underline' || node.nodeName == 'U' || node.up('u');
|
225
|
+
},
|
226
|
+
subscript: function(node) {
|
227
|
+
return node.nodeName == 'SUB' || node.up('sub');
|
228
|
+
},
|
229
|
+
superscript: function(node) {
|
230
|
+
return node.nodeName == 'SUP' || node.up('sup');
|
231
|
+
},
|
232
|
+
justifyleft: function(node) {
|
233
|
+
return (Element.getStyle(node, 'text-align') || '').indexOf('left') > -1;
|
234
|
+
},
|
235
|
+
justifycenter: function(node) {
|
236
|
+
return (Element.getStyle(node, 'text-align') || '').indexOf('center') > -1;
|
237
|
+
},
|
238
|
+
justifyright: function(node) {
|
239
|
+
return (Element.getStyle(node, 'text-align') || '').indexOf('right') > -1;
|
240
|
+
},
|
241
|
+
justifyfull: function(node) {
|
242
|
+
return (Element.getStyle(node, 'text-align') || '').indexOf('justify') > -1;
|
243
|
+
},
|
244
|
+
insertorderedlist: function(node, region) {
|
245
|
+
if (node.nodeName == 'OL') return true;
|
246
|
+
var ol = Element.up(node, 'ol');
|
247
|
+
return (ol) ? ol.descendantOf(region.element) : false;
|
248
|
+
},
|
249
|
+
insertunorderedlist: function(node, region) {
|
250
|
+
if (node.nodeName == 'ul') return true;
|
251
|
+
var ul = Element.up(node, 'ul');
|
252
|
+
return (ul) ? ul.descendantOf(region.element) : false;
|
253
|
+
}
|
254
|
+
}
|
255
|
+
});
|