midas 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. data/.bundle/config +2 -0
  2. data/.document +5 -0
  3. data/.gitignore +32 -0
  4. data/Gemfile +12 -0
  5. data/LICENSE +20 -0
  6. data/README.textile +21 -0
  7. data/Rakefile +84 -0
  8. data/VERSION +1 -0
  9. data/features/step_definitions/editor_steps.rb +16 -0
  10. data/features/step_definitions/web_steps.rb +205 -0
  11. data/features/support/env.rb +39 -0
  12. data/features/support/paths.rb +29 -0
  13. data/features/view_editor.feature +8 -0
  14. data/generators/midas/midas_generator.rb +1 -0
  15. data/lib/midas.rb +1 -0
  16. data/midas.gemspec +131 -0
  17. data/public/images/midas/toolbars/actions/_background.png +0 -0
  18. data/public/images/midas/toolbars/actions/_background_radio.png +0 -0
  19. data/public/images/midas/toolbars/actions/_separator.png +0 -0
  20. data/public/images/midas/toolbars/actions/extra/prefspane.png +0 -0
  21. data/public/images/midas/toolbars/actions/extra/todospane.png +0 -0
  22. data/public/images/midas/toolbars/actions/historypanel.png +0 -0
  23. data/public/images/midas/toolbars/actions/insertcharacter.png +0 -0
  24. data/public/images/midas/toolbars/actions/insertlink.png +0 -0
  25. data/public/images/midas/toolbars/actions/insertmedia.png +0 -0
  26. data/public/images/midas/toolbars/actions/insertobject.png +0 -0
  27. data/public/images/midas/toolbars/actions/inserttable.png +0 -0
  28. data/public/images/midas/toolbars/actions/inspectorpanel.png +0 -0
  29. data/public/images/midas/toolbars/actions/notespanel.png +0 -0
  30. data/public/images/midas/toolbars/actions/preview.png +0 -0
  31. data/public/images/midas/toolbars/actions/redo.png +0 -0
  32. data/public/images/midas/toolbars/actions/save.png +0 -0
  33. data/public/images/midas/toolbars/actions/undo.png +0 -0
  34. data/public/images/midas/toolbars/htmleditor/_background.png +0 -0
  35. data/public/images/midas/toolbars/htmleditor/_line_separator.png +0 -0
  36. data/public/images/midas/toolbars/htmleditor/_separator.png +0 -0
  37. data/public/images/midas/toolbars/htmleditor/buttons.png +0 -0
  38. data/public/javascripts/midas/config.js +181 -0
  39. data/public/javascripts/midas/dialog.js +9 -0
  40. data/public/javascripts/midas/midas.js +307 -0
  41. data/public/javascripts/midas/native_extensions.js +43 -0
  42. data/public/javascripts/midas/palette.js +108 -0
  43. data/public/javascripts/midas/region.js +194 -0
  44. data/public/javascripts/midas/statusbar.js +84 -0
  45. data/public/javascripts/midas/toolbar.js +255 -0
  46. data/public/javascripts/prototype.js +6001 -0
  47. data/public/midas/backcolor.html +97 -0
  48. data/public/midas/examples/bundled.html +60 -0
  49. data/public/midas/examples/iframe.html +73 -0
  50. data/public/midas/examples/index.html +73 -0
  51. data/public/midas/examples/javascript_archive.js +111 -0
  52. data/public/midas/forecolor.html +97 -0
  53. data/public/stylesheets/midas/dialog.css +2 -0
  54. data/public/stylesheets/midas/midas.css +9 -0
  55. data/public/stylesheets/midas/palette.css +50 -0
  56. data/public/stylesheets/midas/region.css +16 -0
  57. data/public/stylesheets/midas/statusbar.css +17 -0
  58. data/public/stylesheets/midas/toolbar.css +262 -0
  59. data/rails/init.rb +1 -0
  60. data/spec/javascripts/dialog_spec.js +7 -0
  61. data/spec/javascripts/fixtures/midas_fixture.html +27 -0
  62. data/spec/javascripts/fixtures/midas_styles.css +14 -0
  63. data/spec/javascripts/fixtures/native_extensions_fixture.html +5 -0
  64. data/spec/javascripts/helpers/browser_detection.js +15 -0
  65. data/spec/javascripts/helpers/event_simulation.js +505 -0
  66. data/spec/javascripts/helpers/spec_helper.js +125 -0
  67. data/spec/javascripts/midas_spec.js +284 -0
  68. data/spec/javascripts/native_extensions_spec.js +39 -0
  69. data/spec/javascripts/palette_spec.js +59 -0
  70. data/spec/javascripts/region_spec.js +441 -0
  71. data/spec/javascripts/statusbar_spec.js +61 -0
  72. data/spec/javascripts/support/jasmine.yml +82 -0
  73. data/spec/javascripts/support/jasmine_config.rb +39 -0
  74. data/spec/javascripts/support/jasmine_runner.rb +19 -0
  75. data/spec/javascripts/toolbar_spec.js +149 -0
  76. data/spec/ruby/helpers/spec_helper.rb +9 -0
  77. data/spec/ruby/midas_spec.rb +9 -0
  78. data/spec/spec.opts +1 -0
  79. data/tasks/midas_tasks.rake +105 -0
  80. 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('&nbsp;')
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() == '&nbsp;' && Prototype.Browser.Gecko) this.setContents('&nbsp;');
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() == '&nbsp;' && Prototype.Browser.Gecko) this.setContents('&nbsp;');
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> &raquo; ' + 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
+ });