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,125 @@
1
+ // Load up the fixtures
2
+ jasmine.include('/__spec__/fixtures/fixtures.js', true);
3
+
4
+
5
+ /**
6
+ * Logs the given string to the current logger.
7
+ *
8
+ * @param {String} string to log
9
+ * @static
10
+ */
11
+ jasmine.log = function(string) {
12
+ var env = jasmine.getEnv();
13
+ env.reporter.log(string);
14
+ };
15
+
16
+ /**
17
+ * Loads a given fixture file into the jasmine_content div.
18
+ *
19
+ * @param {String} filename of the fixture you want to load (minus the .html)
20
+ * @static
21
+ */
22
+ jasmine.loadFixture = function(filename) {
23
+ if (!jasmine.fixtures[filename]) throw('Unable to load that fixture.');
24
+ document.getElementById('jasmine_content').innerHTML = jasmine.fixtures[filename];
25
+ // might want to eval the string here as well
26
+ };
27
+
28
+ /**
29
+ * Loads a given css fixture file into the document.
30
+ *
31
+ * @param {String} filename of the css you want to load (minus the .css)
32
+ * @static
33
+ */
34
+ jasmine.loadCSS = function(filename) {
35
+ if (!jasmine.css[filename]) throw('Unable to load that css.');
36
+ if (document.getElementById('css_' + filename)) return;
37
+
38
+ var style_node = document.createElement('div');
39
+ style_node.setAttribute('id', 'css_' + filename);
40
+ style_node.innerHTML = '<style>' + jasmine.css[filename] + '</style>';
41
+ document.body.appendChild(style_node);
42
+ };
43
+
44
+ /**
45
+ * Unloads a given css fixture file from the document.
46
+ *
47
+ * @param {String} filename of the css you want to load (minus the .css)
48
+ * @static
49
+ */
50
+ jasmine.unloadCSS = function(filename) {
51
+ var element = document.getElementById('css_' + filename);
52
+ if (!element) throw('That css cannot be unloaded -- not yet loaded');
53
+
54
+ document.body.removeChild(element);
55
+ };
56
+
57
+
58
+
59
+
60
+
61
+
62
+
63
+
64
+
65
+ // stub functionality
66
+ jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
67
+ var results = spec.results();
68
+ var status = results.passed() ? 'passed' : 'failed';
69
+ var style = '';
70
+ if (results.skipped) {
71
+ status = 'skipped';
72
+ }
73
+ if (results.failedCount == 0 && results.passedCount == 0 && !results.skipped) {
74
+ status = 'stubbed';
75
+ style = "background-color: #FFA200; border: 1px solid #000000";
76
+ }
77
+ var specDiv = this.createDom('div', { className: 'spec ' + status, style: style },
78
+ this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
79
+ this.createDom('a', {
80
+ className: 'description',
81
+ href: '?spec=' + encodeURIComponent(spec.getFullName()),
82
+ title: spec.getFullName()
83
+ }, spec.description));
84
+
85
+
86
+ var resultItems = results.getItems();
87
+ var messagesDiv = this.createDom('div', { className: 'messages' });
88
+ for (var i = 0; i < resultItems.length; i++) {
89
+ var result = resultItems[i];
90
+ if (result.passed && !result.passed()) {
91
+ messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
92
+
93
+ if (result.trace.stack) {
94
+ messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
95
+ }
96
+ }
97
+ }
98
+
99
+ if (messagesDiv.childNodes.length > 0) {
100
+ specDiv.appendChild(messagesDiv);
101
+ }
102
+
103
+ this.suiteDivs[spec.suite.getFullName()].appendChild(specDiv);
104
+ };
105
+
106
+ var stub = function(desc, func) {
107
+ return jasmine.getEnv().stub(desc, func);
108
+ };
109
+
110
+ jasmine.Env.prototype.stub = function(description, func) {
111
+ var spec = new jasmine.Spec(this, this.currentSuite, description);
112
+ this.currentSuite.add(spec);
113
+ this.currentSpec = spec;
114
+
115
+ if (func) {
116
+ spec.stub(func);
117
+ }
118
+
119
+ return spec;
120
+ };
121
+
122
+ jasmine.Spec.prototype.stub = function (e) {
123
+ var expectationResult = new jasmine.MessageResult('stubbed');
124
+ this.results_.addResult(expectationResult);
125
+ };
@@ -0,0 +1,284 @@
1
+ describe('Midas', function () {
2
+
3
+ beforeEach(function() {
4
+ jasmine.loadFixture('midas_fixture');
5
+ jasmine.loadCSS('midas_styles');
6
+ });
7
+
8
+ afterEach(function () {
9
+ try {
10
+ this.midas.destroy();
11
+ this.midas = null;
12
+ } catch(e) {}
13
+ jasmine.unloadCSS('midas_styles');
14
+ });
15
+
16
+ it('should accept options in the constructor', function() {
17
+ this.midas = new Midas({classname: 'not-editable'});
18
+
19
+ expect($('region1').contentEditable).not.toEqual('true');
20
+ expect($('region3').contentEditable).toEqual('true');
21
+ });
22
+
23
+ it('should use the default configuration', function() {
24
+ this.midas = new Midas();
25
+
26
+ expect(this.midas.options.configuration).toEqual(Midas.Config);
27
+ });
28
+
29
+ it('should allow the configuration to be provided in the options', function() {
30
+ var config = { actions: {}, buttons: {} };
31
+ this.midas = new Midas({configuration: config});
32
+
33
+ expect(this.midas.options.configuration).toEqual(config);
34
+ });
35
+
36
+ it("should pass it's configuration along to the toolbar and regions", function() {
37
+ var config = { actions: {}, buttons: {} };
38
+ this.midas = new Midas({configuration: config});
39
+
40
+ expect(this.midas.toolbar.options.configuration).toEqual(config);
41
+ expect(this.midas.regions[0].options.configuration).toEqual(config);
42
+ });
43
+
44
+ it('should only instantiate if a browser has contentEditable features', function() {
45
+ spyOn(Midas, 'agentIsCapable').andCallFake(function() {
46
+ return false;
47
+ });
48
+ try { this.midas = new Midas(); } catch(e) {
49
+ expect(e.toString()).toEqual('Midas requires a browser that has contentEditable features');
50
+ }
51
+
52
+ expect(this.midas).toBeUndefined();
53
+ });
54
+
55
+ it('should only instantiate if the window is top (when using an iframe)', function() {
56
+ var spy1 = spyOn(Midas, 'trace');
57
+ var spy2 = spyOn(window, 'isTop').andCallFake(function() {
58
+ return false;
59
+ });
60
+
61
+ this.midas = new Midas({useIframe: true});
62
+
63
+ expect(spy1.callCount).toEqual(1);
64
+ expect(spy2.callCount).toEqual(1);
65
+ });
66
+
67
+ it('should make all regions with the editable class editable', function() {
68
+ this.midas = new Midas();
69
+
70
+ expect($('region1').contentEditable).toEqual('true');
71
+ expect($('region2').contentEditable).toEqual('true');
72
+ expect($('region3').contentEditable).not.toEqual('true'); // will default to 'inherit' if not specified
73
+ });
74
+
75
+ it('should assign all editable regions to member variables', function() {
76
+ this.midas = new Midas();
77
+
78
+ expect(this.midas.regions.length).toEqual($$('.editable').length);
79
+ expect(this.midas.regionElements).toContain($('region1'));
80
+ expect(this.midas.regionElements).toContain($('region2'));
81
+ });
82
+
83
+ it('should destroy', function() {
84
+ this.midas = new Midas();
85
+ this.midas.destroy();
86
+
87
+ expect(this.midas.toolbar).toEqual(null);
88
+ expect(this.midas.regions).toEqual([]);
89
+ });
90
+
91
+ describe('static methods', function () {
92
+
93
+ it('should track instances of itself', function() {
94
+ var midas1 = new Midas();
95
+ var midas2 = new Midas();
96
+ var midas3 = new Midas();
97
+
98
+ expect(Midas.instances.length).toEqual(3);
99
+
100
+ midas2.destroy();
101
+ expect(Midas.instances.length).toEqual(2);
102
+
103
+ midas1.destroy();
104
+ expect(Midas.instances.length).toEqual(1);
105
+
106
+ midas3.destroy();
107
+ expect(Midas.instances.length).toEqual(0);
108
+
109
+ midas1 = null;
110
+ midas2 = null;
111
+ midas3 = null;
112
+ });
113
+
114
+ describe('for detecting the browser', function () {
115
+
116
+ // I'm not really sure how to test these.. most of the other tests will
117
+ // be broken if these two fail in a given browser, because most of the
118
+ // features require a level of support in the browser.
119
+
120
+ it('should return that it knows what browser is being used', function() {
121
+ expect(Midas.agent()).not.toEqual(false);
122
+ });
123
+
124
+ it('should detect if the browser is capible of editing', function() {
125
+ expect(Midas.agentIsCapable()).toEqual(true);
126
+ });
127
+
128
+ });
129
+
130
+ });
131
+
132
+ describe('when saving', function () {
133
+
134
+ beforeEach(function() {
135
+ this.spy = spyOn(Ajax, 'Request').andCallFake(function() {
136
+ jasmine.log('>> Mock Ajax.Request called with ' + arguments.length + ' arguments...');
137
+ });
138
+ });
139
+
140
+ it('should call serialize on the regions', function () {
141
+ this.midas = new Midas();
142
+ spyOn(this.midas.regions[1], 'serialize').andCallFake(function() {
143
+ return {name: 'banana', content: 'juice'};
144
+ });
145
+ this.midas.save();
146
+
147
+ expect(this.midas.regions[1].serialize).wasCalled();
148
+ });
149
+
150
+ describe('using put (updating)', function() {
151
+
152
+ it('should generate an ajax request', function () {
153
+ this.midas = new Midas({
154
+ saveUrl: '/peanuts',
155
+ saveMethod: 'put'
156
+ });
157
+ this.midas.save();
158
+
159
+ var args = Ajax.Request.argsForCall[0];
160
+ expect(args[0]).toEqual('/peanuts');
161
+ expect(args[1]['method']).toEqual('put');
162
+ expect(args[1]['parameters']['_method']).toEqual('put');
163
+ expect(args[1]['parameters']['region1']).toEqual('region1');
164
+ expect(args[1]['parameters']['region2']).toEqual('region2');
165
+ expect(args[1]['parameters']['region4']).toEqual($('region4').innerHTML.replace(/^\s+|\s+$/g, ""));
166
+ });
167
+
168
+ });
169
+
170
+ describe('using post (creating)', function() {
171
+
172
+ it('should generate an ajax request', function () {
173
+ this.midas = new Midas({
174
+ saveUrl: '/oranges',
175
+ saveMethod: 'post'
176
+ });
177
+ this.midas.save();
178
+
179
+ var args = Ajax.Request.argsForCall[0];
180
+ expect(args[0]).toEqual('/oranges');
181
+ expect(args[1]['method']).toEqual('post');
182
+ expect(args[1]['parameters']['_method']).toBeFalsy();
183
+ expect(args[1]['parameters']['region1']).toEqual('region1');
184
+ expect(args[1]['parameters']['region2']).toEqual('region2');
185
+ expect(args[1]['parameters']['region4']).toEqual($('region4').innerHTML.replace(/^\s+|\s+$/g, ""));
186
+ });
187
+
188
+ });
189
+
190
+ });
191
+
192
+ describe('events that are observed', function () {
193
+
194
+ it('should understand context and highlight buttons', function() {
195
+ this.midas = new Midas();
196
+ this.midas.regions[0].focused = true;
197
+
198
+ var span = $('div6').down('span');
199
+ jasmine.simulate.selection(span);
200
+
201
+ Event.fire(document, 'midas:region:update', {region: this.midas.regions[0], name: 'name', event: {}});
202
+ expect(this.midas.toolbar.element.down('.midas-button-bold').hasClassName('active')).toEqual(true);
203
+
204
+ var em = $('div3').down('em');
205
+ jasmine.simulate.selection(em);
206
+
207
+ Event.fire(document, 'midas:region:update', {region: this.midas.regions[0], name: 'name', event: {}});
208
+
209
+ expect(this.midas.toolbar.element.down('.midas-button-bold').hasClassName('active')).toEqual(false);
210
+ expect(this.midas.toolbar.element.down('.midas-button-italic').hasClassName('active')).toEqual(true);
211
+ });
212
+
213
+ it('should know which region has focus', function() {
214
+
215
+ // focus() doesn't seem to work well in ci.. works fine in browser though.
216
+ // I tried runs, waits, and changing the element that was being observed /
217
+ // fired on, but no luck...using click instead of focus for now.
218
+
219
+ this.midas = new Midas();
220
+
221
+ jasmine.simulate.click(this.midas.regions[1].element);
222
+ expect(this.midas.activeRegion.name).toEqual(this.midas.regions[1].name);
223
+
224
+ jasmine.simulate.click(this.midas.regions[0].element);
225
+ expect(this.midas.activeRegion.name).toEqual(this.midas.regions[0].name);
226
+ });
227
+
228
+ it('should handle and pass any button clicks to the active region', function() {
229
+ this.midas = new Midas();
230
+
231
+ this.midas.activeRegion = this.midas.regions[0];
232
+
233
+ var spy1 = spyOn(this.midas.activeRegion, 'handleAction');
234
+ var spy2 = spyOn(this.midas, 'handleAction');
235
+
236
+ jasmine.simulate.click($$('.midas-button-insertorderedlist')[0]);
237
+ expect(spy1.callCount).toEqual(1);
238
+ expect(spy2.callCount).toEqual(1);
239
+ });
240
+
241
+ it('should handle switching modes', function() {
242
+ this.midas = new Midas();
243
+ this.midas.activeRegion = this.midas.regions[0];
244
+
245
+ var spy = spyOn(this.midas, 'handleMode').andCallThrough();
246
+
247
+ jasmine.simulate.click($$('.midas-button-preview')[0]);
248
+ expect(spy.callCount).toEqual(1);
249
+ });
250
+
251
+ });
252
+
253
+ describe('when using an iframe', function() {
254
+
255
+ it('should create an iframe', function() {
256
+ this.midas = new Midas({useIframe: 'about:blank'});
257
+ spyOn(this.midas, 'initializeRegions');
258
+ spyOn(this.midas, 'finalizeInterface');
259
+ expect($$('.midas-iframe-window').length).toEqual(1);
260
+ });
261
+
262
+ it('should communicate which contentWindow the toolbar should use', function() {
263
+
264
+ // need to figure out a better way to test this...
265
+ // if the expectation runs before the iframe loads 'about:blank' we
266
+ // get hosed... and to avoid making a long wait here, we've just
267
+ // added a bit that only checks if midas.toolbar is not undefined
268
+
269
+ runs(function() {
270
+ this.midas = new Midas({useIframe: 'about:blank'});
271
+ this.iframe = $$('.midas-iframe-window')[0];
272
+ });
273
+
274
+ waits(100);
275
+
276
+ runs(function() {
277
+ if (!this.midas.toolbar) return;
278
+ expect(this.midas.toolbar.options['contentWindow'] == this.iframe.contentWindow).toEqual(true);
279
+ })
280
+ });
281
+
282
+ });
283
+
284
+ });
@@ -0,0 +1,39 @@
1
+ describe('Native Extensions', function () {
2
+
3
+ beforeEach(function() {
4
+ jasmine.loadFixture('native_extensions_fixture');
5
+ });
6
+
7
+ it('should compare two NodeLists', function() {
8
+ var div = $('div1');
9
+ expect(typeof(div.childNodes.equals)).toEqual('function');
10
+
11
+ expect(div.childNodes.equals($('div1').childNodes)).toEqual(true);
12
+ expect(div.childNodes.equals($('div2').childNodes)).toEqual(false);
13
+ expect(div.childNodes.equals($('div3').childNodes)).toEqual(false);
14
+ });
15
+
16
+ it('should convert numbers to hex', function() {
17
+ var number1 = 15;
18
+ var number2 = 0;
19
+
20
+ expect(number1.toHex()).toEqual('0F');
21
+ expect(number2.toHex()).toEqual('00');
22
+ });
23
+
24
+ it('should convert rgb strings to hex', function() {
25
+ var string1 = 'RGB(255, 255, 255)';
26
+ var string2 = 'rgb(0 0 0)';
27
+
28
+ expect(string1.toHex()).toEqual('#FFFFFF');
29
+ expect(string2.toHex()).toEqual('#000000');
30
+ });
31
+
32
+ it('should have a isTop() function on window', function() {
33
+ var iframe = $('iframe1');
34
+ iframe.contentWindow.isTop = window.isTop;
35
+
36
+ expect(window.isTop()).toEqual(true);
37
+ expect(iframe.contentWindow.isTop()).toEqual(false);
38
+ });
39
+ });
@@ -0,0 +1,59 @@
1
+ describe('Midas.Palette', function() {
2
+
3
+ beforeEach(function() {
4
+ jasmine.loadFixture('midas_fixture');
5
+ });
6
+
7
+ afterEach(function () {
8
+ try {
9
+ this.palette.destroy();
10
+ this.palette = null;
11
+ } catch(e) {}
12
+ });
13
+
14
+ it('should accept options in the constructor', function() {
15
+ this.palette = new Midas.Palette($('palette_button'), 'backcolor', {element: $('toolbar')}, {lettuce: 'banana'});
16
+
17
+ expect(this.palette.options['lettuce']).toEqual('banana');
18
+ });
19
+
20
+ it('should make a palette', function() {
21
+ this.palette = new Midas.Palette($('palette_button'), 'backcolor', {element: $('toolbar')});
22
+
23
+ expect(this.palette.element).not.toBeFalsy();
24
+ expect($$('.midas-palette').length).toEqual(1);
25
+ });
26
+
27
+ stub('should show when the button is clicked', function() {
28
+
29
+ });
30
+
31
+ stub('should hide', function() {
32
+
33
+ });
34
+
35
+ stub('should position itself properly', function() {
36
+
37
+ });
38
+
39
+ stub('should know if it is visible or not', function() {
40
+
41
+ });
42
+
43
+ stub('should load contents from a url', function() {
44
+
45
+ });
46
+
47
+ stub('should call a setup function', function() {
48
+
49
+ });
50
+
51
+ it('should destroy', function() {
52
+ this.palette = new Midas.Palette($('palette_button'), 'backcolor', {element: $('toolbar')});
53
+ this.palette.destroy();
54
+
55
+ expect(this.palette.element).toBeFalsy();
56
+ expect($$('.midas-palette').length).toEqual(0);
57
+ });
58
+
59
+ });