rails_admin_live_edit 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +19 -0
  4. data/Rakefile +24 -0
  5. data/app/views/live_edit/_ra_live_editor.html.haml +136 -0
  6. data/lib/rails_admin_live_edit.rb +1 -0
  7. data/lib/rails_admin_live_edit/engine.rb +4 -0
  8. data/lib/rails_admin_live_edit/version.rb +3 -0
  9. data/vendor/assets/javascripts/rails_admin/plugins/live_edit/ui.js +23 -0
  10. data/vendor/assets/javascripts/rmodal.js/LICENSE +21 -0
  11. data/vendor/assets/javascripts/rmodal.js/README.md +57 -0
  12. data/vendor/assets/javascripts/rmodal.js/bower.json +28 -0
  13. data/vendor/assets/javascripts/rmodal.js/dist/rmodal-no-bootstrap.css +37 -0
  14. data/vendor/assets/javascripts/rmodal.js/dist/rmodal.css +14 -0
  15. data/vendor/assets/javascripts/rmodal.js/dist/rmodal.js +201 -0
  16. data/vendor/assets/javascripts/rmodal.js/dist/rmodal.js.map +1 -0
  17. data/vendor/assets/javascripts/rmodal.js/dist/rmodal.min.js +2 -0
  18. data/vendor/assets/javascripts/rmodal.js/dist/rmodal.min.js.map +1 -0
  19. data/vendor/assets/javascripts/rmodal.js/gulpfile.js +99 -0
  20. data/vendor/assets/javascripts/rmodal.js/index.html +111 -0
  21. data/vendor/assets/javascripts/rmodal.js/index.js +193 -0
  22. data/vendor/assets/javascripts/rmodal.js/index.js.map +1 -0
  23. data/vendor/assets/javascripts/rmodal.js/karma.conf.js +66 -0
  24. data/vendor/assets/javascripts/rmodal.js/logo.png +0 -0
  25. data/vendor/assets/javascripts/rmodal.js/package.json +59 -0
  26. data/vendor/assets/javascripts/rmodal.js/src/rmodal-no-bootstrap.css +37 -0
  27. data/vendor/assets/javascripts/rmodal.js/src/rmodal.css +14 -0
  28. data/vendor/assets/javascripts/rmodal.js/src/rmodal.js +188 -0
  29. data/vendor/assets/javascripts/rmodal.js/test/rmodal.test.js +636 -0
  30. metadata +72 -0
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "rmodal",
3
+ "version": "1.0.24",
4
+ "description": "A simple modal dialog with no external dependencies. IE9+ supported.",
5
+ "main": "index.js",
6
+ "module": "src/rmodal.js",
7
+ "jsnext:main": "src/rmodal.js",
8
+ "scripts": {
9
+ "test": "./node_modules/.bin/gulp test"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/zewish/rmodal.js.git"
14
+ },
15
+ "keywords": [
16
+ "modal",
17
+ "dialog",
18
+ "javascript",
19
+ "simple",
20
+ "plain",
21
+ "browser",
22
+ "browserify"
23
+ ],
24
+ "author": "Iskren Slavov <iskren.s@gmail.com>",
25
+ "license": "MIT",
26
+ "bugs": {
27
+ "url": "https://github.com/zewish/rmodal.js/issues"
28
+ },
29
+ "homepage": "http://rmodal.js.org/",
30
+ "devDependencies": {
31
+ "buble": "^0.15.2",
32
+ "chai": "^3.5.0",
33
+ "eslint": "^3.13.1",
34
+ "gulp": "^3.9.1",
35
+ "gulp-eslint": "^3.0.1",
36
+ "gulp-rename": "^1.2.2",
37
+ "gulp-replace": "^0.5.4",
38
+ "gulp-sourcemaps": "^2.4.0",
39
+ "gulp-uglify": "^2.0.0",
40
+ "istanbul": "^0.4.5",
41
+ "istanbul-coveralls": "^1.0.3",
42
+ "karma": "^1.1.2",
43
+ "karma-chai": "^0.1.0",
44
+ "karma-commonjs": "^1.0.0",
45
+ "karma-coverage": "^1.1.1",
46
+ "karma-coveralls": "^1.1.2",
47
+ "karma-mocha": "^1.3.0",
48
+ "karma-phantomjs-launcher": "^1.0.2",
49
+ "karma-sinon": "^1.0.5",
50
+ "mocha": "^3.2.0",
51
+ "phantomjs-prebuilt": "^2.1.14",
52
+ "rollup": "^0.41.1",
53
+ "rollup-plugin-buble": "^0.15.0",
54
+ "rollup-stream": "^1.18.0",
55
+ "sinon": "^1.17.7",
56
+ "vinyl-buffer": "^1.0.0",
57
+ "vinyl-source-stream": "^1.1.0"
58
+ }
59
+ }
@@ -0,0 +1,37 @@
1
+ body {
2
+ padding: 0;
3
+ margin: 0;
4
+ }
5
+
6
+ body.modal-open {
7
+ overflow-x: hidden;
8
+ overflow-y: auto;
9
+ }
10
+
11
+ .modal {
12
+ display: none;
13
+ background: rgba(0, 0, 0, .30);
14
+ z-index: 999;
15
+ padding: 30px 0;
16
+
17
+ position: fixed;
18
+ top: 0;
19
+ left: 0;
20
+ right: 0;
21
+ bottom: 0;
22
+
23
+ overflow-x: hidden;
24
+ overflow-y: auto;
25
+ }
26
+
27
+ .modal .modal-dialog {
28
+ position: relative;
29
+ margin: 30px auto;
30
+ width: 1100px;
31
+ border-radius: 6px;
32
+ -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5);
33
+ box-shadow: 0 3px 9px rgba(0, 0, 0, .5);
34
+
35
+ background: #fff;
36
+ margin: 0 auto;
37
+ }
@@ -0,0 +1,14 @@
1
+ .modal {
2
+ display: none;
3
+ background: rgba(0, 0, 0, .30);
4
+ z-index: 999;
5
+ }
6
+
7
+ .modal .modal-dialog {
8
+ position: relative;
9
+ margin: 30px auto;
10
+ width: 1100px;
11
+ border-radius: 6px;
12
+ -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5);
13
+ box-shadow: 0 3px 9px rgba(0, 0, 0, .5);
14
+ }
@@ -0,0 +1,188 @@
1
+ 'use strict';
2
+
3
+ let is = (obj, type) => Object.prototype.toString.call(obj).toLowerCase() === `[object ${type}]`;
4
+
5
+ let addClass = (el, cls) => {
6
+ let arr = el.className
7
+ .split(/\s+/)
8
+ .filter((c) => !!c && c == cls);
9
+
10
+ if (!arr.length) {
11
+ el.className += ` ${cls}`;
12
+ }
13
+ };
14
+
15
+ let removeClass = (el, cls) => {
16
+ el.className = el.className
17
+ .split(/\s+/)
18
+ .filter((c) => !!c && c != cls)
19
+ .join(' ');
20
+ };
21
+
22
+ class RModal {
23
+ constructor(el, opts) {
24
+ this.opened = false;
25
+
26
+ this.opts = {
27
+ bodyClass: 'modal-open'
28
+ , dialogClass: 'modal-dialog'
29
+ , dialogOpenClass: 'bounceInDown'
30
+ , dialogCloseClass: 'bounceOutUp'
31
+
32
+ , focus: true
33
+ , focusElements: [
34
+ 'a[href]', 'area[href]', 'input:not([disabled]):not([type=hidden])'
35
+ , 'button:not([disabled])', 'select:not([disabled])'
36
+ , 'textarea:not([disabled])', 'iframe', 'object', 'embed'
37
+ , '*[tabindex]', '*[contenteditable]'
38
+ ]
39
+
40
+ , escapeClose: true
41
+ , content: null
42
+ , closeTimeout: 500
43
+ };
44
+
45
+ Object.keys(opts || {})
46
+ .forEach((key) => {
47
+ /* istanbul ignore else */
48
+ if (opts[key] !== undefined) {
49
+ this.opts[key] = opts[key];
50
+ }
51
+ });
52
+
53
+ this.overlay = el;
54
+ this.dialog = el.querySelector(`.${this.opts.dialogClass}`);
55
+
56
+ if (this.opts.content) {
57
+ this.content(this.opts.content);
58
+ }
59
+ }
60
+
61
+ open(content) {
62
+ this.content(content);
63
+
64
+ if (!is(this.opts.beforeOpen, 'function')) {
65
+ return this._doOpen();
66
+ }
67
+
68
+ this.opts.beforeOpen(() => {
69
+ this._doOpen();
70
+ });
71
+ }
72
+
73
+ _doOpen() {
74
+ addClass(document.body, this.opts.bodyClass);
75
+
76
+ removeClass(this.dialog, this.opts.dialogCloseClass);
77
+ addClass(this.dialog, this.opts.dialogOpenClass);
78
+
79
+ this.overlay.style.display = 'block';
80
+
81
+ if (this.opts.focus) {
82
+ this.focusOutElement = document.activeElement;
83
+ this.focus();
84
+ }
85
+
86
+ if (is(this.opts.afterOpen, 'function')) {
87
+ this.opts.afterOpen();
88
+ }
89
+ this.opened = true;
90
+ }
91
+
92
+ close() {
93
+ if (!is(this.opts.beforeClose, 'function')) {
94
+ return this._doClose();
95
+ }
96
+
97
+ this.opts.beforeClose(() => {
98
+ this._doClose();
99
+ });
100
+ }
101
+
102
+ _doClose() {
103
+ removeClass(this.dialog, this.opts.dialogOpenClass);
104
+ addClass(this.dialog, this.opts.dialogCloseClass);
105
+
106
+ removeClass(document.body, this.opts.bodyClass);
107
+
108
+ if (this.opts.focus) {
109
+ this.focus(this.focusOutElement);
110
+ }
111
+
112
+ if (is(this.opts.afterClose, 'function')) {
113
+ this.opts.afterClose();
114
+ }
115
+
116
+ this.opened = false;
117
+ setTimeout(() => {
118
+ this.overlay.style.display = 'none';
119
+ }, this.opts.closeTimeout);
120
+ }
121
+
122
+ content(html) {
123
+ if (html === undefined) {
124
+ return this.dialog.innerHTML;
125
+ }
126
+
127
+ this.dialog.innerHTML = html;
128
+ }
129
+
130
+ elements(selector, fallback) {
131
+ fallback = fallback || window.navigator.appVersion.indexOf('MSIE 9.0') > -1;
132
+ selector = is(selector, 'array') ? selector.join(',') : selector;
133
+
134
+ return [].filter.call(
135
+ this.dialog.querySelectorAll(selector)
136
+ , (element) => {
137
+ if (fallback) {
138
+ var style = window.getComputedStyle(element);
139
+ return style.display !== 'none' && style.visibility !== 'hidden';
140
+ }
141
+
142
+ return element.offsetParent !== null;
143
+ }
144
+ );
145
+ }
146
+
147
+ focus(el) {
148
+ el = el || this.elements(this.opts.focusElements)[0] || this.dialog.firstChild;
149
+
150
+ if (el && is(el.focus, 'function')) {
151
+ el.focus();
152
+ }
153
+ }
154
+
155
+ keydown(ev) {
156
+ if (this.opts.escapeClose && ev.which == 27) {
157
+ this.close();
158
+ }
159
+
160
+ function stopEvent() {
161
+ ev.preventDefault();
162
+ ev.stopPropagation();
163
+ }
164
+
165
+ if (this.opened && ev.which == 9 && this.dialog.contains(ev.target)) {
166
+ var elements = this.elements(this.opts.focusElements)
167
+ , first = elements[0]
168
+ , last = elements[elements.length - 1];
169
+
170
+ if (first == last) {
171
+ stopEvent();
172
+ }
173
+ else if (ev.target == first && ev.shiftKey) {
174
+ stopEvent();
175
+ last.focus();
176
+ }
177
+ else if (ev.target == last && !ev.shiftKey) {
178
+ stopEvent();
179
+ first.focus();
180
+ }
181
+ }
182
+ }
183
+ }
184
+
185
+ RModal.prototype.version = '@@VERSION@@';
186
+ RModal.version = '@@VERSION@@';
187
+
188
+ export default RModal;
@@ -0,0 +1,636 @@
1
+ 'use strict';
2
+
3
+ var RModal = require(__dirname + '/../index.js');
4
+
5
+ describe('RModal', function() {
6
+ var elBody
7
+ , elOverlay
8
+ , elDialog;
9
+
10
+ var overlay = {
11
+ add: function() {
12
+ elOverlay = document.createElement('div');
13
+ elOverlay.className = 'modal';
14
+ elBody.appendChild(elOverlay);
15
+ }
16
+ , remove: function() {
17
+ var elOverlay = elBody.querySelector('.modal');
18
+ elOverlay.parentNode.removeChild(elOverlay);
19
+ }
20
+ };
21
+
22
+ var dialog = {
23
+ add: function() {
24
+ elDialog = document.createElement('div');
25
+ elDialog.className = 'modal-dialog';
26
+ elOverlay.appendChild(elDialog);
27
+ }
28
+ , remove: function() {
29
+ var elDialog = elOverlay.querySelector('.modal-dialog');
30
+ elDialog.parentNode.removeChild(elDialog);
31
+ }
32
+ };
33
+
34
+ beforeEach(function() {
35
+ elBody = document.querySelector('body');
36
+ overlay.add();
37
+ dialog.add();
38
+ });
39
+
40
+ afterEach(function() {
41
+ dialog.remove();
42
+ overlay.remove();
43
+ });
44
+
45
+ function create(opts) {
46
+ return new RModal(elOverlay, opts);
47
+ }
48
+
49
+ describe('constructor()', function() {
50
+ it('should set "this.opts" to an object if it is not provided', function() {
51
+ var instance = create(undefined);
52
+ expect(instance.opts).to.be.an('object');
53
+ });
54
+
55
+ it('should set defaults for "this.opts" properties when not provided', function() {
56
+ var instance = create();
57
+ expect(instance.opts.bodyClass).to.equal('modal-open');
58
+ expect(instance.opts.dialogClass).to.equal('modal-dialog');
59
+ expect(instance.opts.dialogOpenClass).to.equal('bounceInDown');
60
+ expect(instance.opts.dialogCloseClass).to.equal('bounceOutUp');
61
+
62
+ expect(instance.opts.focus).to.be.true;
63
+ expect(instance.opts.focusElements).to.eql([
64
+ 'a[href]', 'area[href]', 'input:not([disabled]):not([type=hidden])'
65
+ , 'button:not([disabled])', 'select:not([disabled])'
66
+ , 'textarea:not([disabled])', 'iframe', 'object', 'embed'
67
+ , '*[tabindex]', '*[contenteditable]'
68
+ ]);
69
+
70
+ expect(instance.opts.escapeClose).to.be.true;
71
+ });
72
+
73
+ it('should set "this.opts" properties when provided', function() {
74
+ var opts = {
75
+ bodyClass: 'custom-body-class'
76
+ , dialogClass: 'custom-dialog-class'
77
+ , dialogOpenClass: 'custom-dialog-open-class'
78
+ , dialogCloseClass: 'custom-dialog-close-class'
79
+
80
+ , focus: true
81
+ , focusElements: ['my', 'custom', 'elements']
82
+
83
+ , escapeClose: false
84
+ };
85
+ var instance = create(opts);
86
+
87
+ expect(instance.opts.bodyClass).to.equal(opts.bodyClass);
88
+ expect(instance.opts.dialogClass).to.equal(opts.dialogClass);
89
+ expect(instance.opts.dialogOpenClass).to.equal(opts.dialogOpenClass);
90
+ expect(instance.opts.dialogCloseClass).to.equal(opts.dialogCloseClass);
91
+
92
+ expect(instance.opts.focus).to.equal(opts.focus);
93
+ expect(instance.opts.focusElements).to.equal(opts.focusElements);
94
+
95
+ expect(instance.opts.escapeClose).to.be.equal(opts.escapeClose);
96
+ });
97
+
98
+ it('should assign "this.overlay" reference to the "element" param', function() {
99
+ var instance = create();
100
+ expect(instance.overlay).to.be.eql(elOverlay);
101
+ });
102
+
103
+ it('should assign "this.dialog" to an HTMLElement', function() {
104
+ var instance = create();
105
+ expect(instance.dialog).to.be.instanceof(HTMLElement);
106
+ });
107
+
108
+ it('should not call "this.content()"', function() {
109
+ var stub = sinon.stub(RModal.prototype, 'content');
110
+ var instance = create();
111
+
112
+ expect(stub.calledOnce).to.be.false;
113
+ RModal.prototype.content.restore();
114
+ });
115
+
116
+ it('should call "this.content()" with "this.opts.content" as a param', function() {
117
+ var stub = sinon.stub(RModal.prototype, 'content');
118
+ var instance = create({
119
+ content: 'test content'
120
+ });
121
+
122
+ expect(
123
+ stub.withArgs(instance.opts.content).calledOnce
124
+ ).to.be.true;
125
+ RModal.prototype.content.restore();
126
+ });
127
+
128
+ it('should have versions defined in both instance and prototype', function() {
129
+ var instance = create();
130
+
131
+ expect(instance.version).to.be.a('string');
132
+ expect(RModal.prototype.version).to.be.a('string');
133
+ });
134
+ });
135
+
136
+ describe('open()', function() {
137
+ it('should call "this.content()" with "this.opts.content" as param', function() {
138
+ var stub = sinon.stub(RModal.prototype, 'content');
139
+ var instance = create({
140
+ content: 'dummy content'
141
+ });
142
+ instance.open();
143
+
144
+ expect(
145
+ stub.withArgs('dummy content').calledOnce
146
+ ).to.be.true;
147
+ RModal.prototype.content.restore();
148
+ });
149
+
150
+ it('should call "this.opts.beforeOpen" if it is a function"', function() {
151
+ var spy = sinon.spy(function(next) {
152
+ next();
153
+ });
154
+ var instance = create({
155
+ beforeOpen: spy
156
+ });
157
+
158
+ instance.open();
159
+ expect(spy.calledOnce).to.be.true;
160
+ });
161
+
162
+ it('should call "this._doOpen()"', function() {
163
+ var spy = sinon.spy(RModal.prototype, '_doOpen');
164
+ var instance = create({
165
+ beforeOpen: function(next) {
166
+ next();
167
+ }
168
+ });
169
+ instance.open();
170
+ expect(spy.calledOnce).to.be.true;
171
+
172
+ instance = create();
173
+ instance.open();
174
+ expect(spy.calledTwice).to.be.true;
175
+ RModal.prototype._doOpen.restore();
176
+ });
177
+ });
178
+
179
+ describe('_doOpen()', function() {
180
+ it('should add "this.opts.bodyClass" to body.className', function() {
181
+ elBody.className = 'default-class';
182
+ var instance = create({
183
+ bodyClass: 'test-class'
184
+ });
185
+
186
+ instance._doOpen();
187
+ expect(elBody.className).to.be.equal(
188
+ 'default-class ' + instance.opts.bodyClass
189
+ );
190
+ });
191
+
192
+ it('should remove "this.opts.dialogCloseClass" from dialog.className', function() {
193
+ elDialog.className = 'modal-dialog dialog-class1 close-class';
194
+ var instance = create({
195
+ dialogCloseClass: 'close-class'
196
+ });
197
+
198
+ instance._doOpen();
199
+ expect(instance.dialog.className).to.be.equal(
200
+ 'modal-dialog dialog-class1 bounceInDown'
201
+ );
202
+ });
203
+
204
+ it('should add "this.opts.dialogOpenClass" from dialog.className', function() {
205
+ elDialog.className = 'modal-dialog dialog-class2';
206
+ var instance = create({
207
+ dialogOpenClass: 'open-class'
208
+ });
209
+
210
+ instance._doOpen();
211
+ expect(instance.dialog.className).to.be.equal(
212
+ 'modal-dialog dialog-class2 open-class'
213
+ );
214
+ });
215
+
216
+ it('should set "this.overlay.style.display" to "block"', function() {
217
+ elOverlay.style.display = 'none';
218
+ var instance = create();
219
+
220
+ instance._doOpen();
221
+ expect(instance.overlay.style.display).to.be.equal('block');
222
+ });
223
+
224
+ it('should set "this.focusOutElement" to "document.activeElement"', function() {
225
+ var expected = document.activeElement;
226
+ var instance = create();
227
+
228
+ instance._doOpen();
229
+ expect(instance.focusOutElement).to.be.eql(expected);
230
+ });
231
+
232
+ it('should call "this.focus()"'
233
+ , function() {
234
+ var instance = create();
235
+ var spy = sinon.spy(RModal.prototype, 'focus');
236
+
237
+ instance._doOpen();
238
+ expect(spy.calledOnce).to.be.true;
239
+
240
+ RModal.prototype.focus.restore();
241
+ }
242
+ );
243
+
244
+ it('should not call "this.focus()"'
245
+ , function() {
246
+ var instance = create({
247
+ focus: false
248
+ });
249
+ var spy = sinon.spy(RModal.prototype, 'focus');
250
+
251
+ instance._doOpen();
252
+ expect(spy.calledOnce).to.be.false;
253
+
254
+ RModal.prototype.focus.restore();
255
+ }
256
+ );
257
+
258
+ it('should call "this.opts.afterOpen" if it is a function"', function() {
259
+ var spy = sinon.spy();
260
+ var instance = create({
261
+ afterOpen: spy
262
+ });
263
+
264
+ instance._doOpen();
265
+ expect(spy.calledOnce).to.be.true;
266
+ });
267
+ });
268
+
269
+ describe('close()', function() {
270
+ it('should call "this.opts.beforeClose" if it is a function"', function() {
271
+ var spy = sinon.spy(function(next) {
272
+ next();
273
+ });
274
+ var instance = create({
275
+ beforeClose: spy
276
+ });
277
+
278
+ instance.close();
279
+ expect(spy.calledOnce).to.be.true;
280
+ });
281
+
282
+ it('should call "this._doClose()"', function() {
283
+ var spy = sinon.spy(RModal.prototype, '_doClose');
284
+ var instance = create({
285
+ beforeClose: function(next) {
286
+ next();
287
+ }
288
+ });
289
+ instance.close();
290
+ expect(spy.calledOnce).to.be.true;
291
+
292
+ instance = create();
293
+ instance.close();
294
+ expect(spy.calledTwice).to.be.true;
295
+ RModal.prototype._doClose.restore();
296
+ });
297
+ });
298
+
299
+ describe('_doClose()', function() {
300
+ it('should remove "this.opts.dialogOpenClass" from dialog.className', function() {
301
+ elDialog.className = 'modal-dialog dialog-class3 open-class';
302
+ var instance = create({
303
+ dialogOpenClass: 'open-class'
304
+ });
305
+
306
+ instance._doClose();
307
+ expect(instance.dialog.className).to.be.equal(
308
+ 'modal-dialog dialog-class3 bounceOutUp'
309
+ );
310
+ });
311
+
312
+ it('should add "this.opts.dialogCloseClass" from dialog.className', function() {
313
+ elDialog.className = 'modal-dialog dialog-class4';
314
+ var instance = create({
315
+ dialogCloseClass: 'close-class'
316
+ });
317
+
318
+ instance._doClose();
319
+ expect(instance.dialog.className).to.be.equal(
320
+ 'modal-dialog dialog-class4 close-class'
321
+ );
322
+ });
323
+
324
+ it('should remove "this.opts.bodyClass" from body.className', function() {
325
+ elBody.className = 'default-body-class modal-open-class';
326
+ var instance = create({
327
+ bodyClass: 'modal-open-class'
328
+ });
329
+
330
+ instance._doClose();
331
+ expect(elBody.className).to.be.equal('default-body-class');
332
+ });
333
+
334
+ it('should call "this.focus()" with "this.focusOutElement" as param', function() {
335
+ var instance = create();
336
+ var spy = sinon.spy(RModal.prototype, 'focus');
337
+
338
+ instance._doClose();
339
+ expect(
340
+ spy.withArgs(instance.focusOutElement).calledOnce
341
+ ).to.be.true;
342
+ RModal.prototype.focus.restore();
343
+ });
344
+
345
+ it('should not call "this.focus()"', function() {
346
+ var instance = create({
347
+ focus: false
348
+ });
349
+ var spy = sinon.spy(RModal.prototype, 'focus');
350
+
351
+ instance._doClose();
352
+ expect(
353
+ spy.withArgs(instance.focusOutElement).calledOnce
354
+ ).to.be.false;
355
+ RModal.prototype.focus.restore();
356
+ });
357
+
358
+ it('should call "this.opts.afterClose" if it is a function"', function() {
359
+ var spy = sinon.spy();
360
+ var instance = create({
361
+ afterClose: spy
362
+ });
363
+
364
+ instance._doClose();
365
+ expect(spy.calledOnce).to.be.true;
366
+ });
367
+
368
+ it('should call set "this.overlay.style.display" to "none"', function() {
369
+ var timers = sinon.useFakeTimers();
370
+ var instance = create();
371
+
372
+ instance._doClose();
373
+ timers.tick(500);
374
+ expect(instance.overlay.style.display).to.be.equal('none');
375
+ timers.restore();
376
+ });
377
+ });
378
+
379
+ describe('content()', function() {
380
+ it('should return this.dialog.innerHTML', function() {
381
+ var instance = create();
382
+ elDialog.innerHTML = 'testing';
383
+
384
+ expect(instance.content()).to.be.equal(elDialog.innerHTML);
385
+ });
386
+
387
+ it('should change this.dialog.innerHTML if a param is passed', function() {
388
+ var instance = create();
389
+ instance.content('testing2');
390
+
391
+ expect(elDialog.innerHTML).to.be.equal('testing2')
392
+ });
393
+ });
394
+
395
+ describe('elements()', function() {
396
+ it('should filter and return only visible elements', function() {
397
+ elDialog.style.position = 'relative';
398
+ elDialog.innerHTML =
399
+ '<input type="hidden" alt="invisible" />'
400
+ + '<input type="text" value="I am invisible" style="display: none;" />'
401
+ + '<input type="text" value="I am visible" />';
402
+
403
+ var instance = create();
404
+
405
+ var elems = instance.elements(instance.opts.focusElements);
406
+ expect(elems[0]).to.eql(elDialog.children[2]);
407
+ expect(elems.length).to.be.equal(1);
408
+ });
409
+
410
+ it('should filter and return only visible elements when selector is a string', function() {
411
+ elDialog.style.position = 'relative';
412
+ elDialog.innerHTML =
413
+ '<input type="hidden" alt="invisible" />'
414
+ + '<input type="text" value="I am invisible" style="display: none;" />'
415
+ + '<input type="text" value="I am visible" />';
416
+
417
+ var instance = create();
418
+
419
+ var elems = instance.elements('input:not([disabled]):not([type=hidden])');
420
+ expect(elems[0]).to.eql(elDialog.children[2]);
421
+ expect(elems.length).to.be.equal(1);
422
+ });
423
+
424
+ it('should filter and return only visible elements (IE9)', function() {
425
+ elDialog.style.position = 'relative';
426
+ elDialog.innerHTML =
427
+ '<input type="hidden" alt="invisible" />'
428
+ + '<input type="text" value="I am invisible" style="display: none;" />'
429
+ + '<input type="text" value="I am visible" />';
430
+
431
+ var instance = create();
432
+ var elems = instance.elements(instance.opts.focusElements, true);
433
+ expect(elems[0]).to.eql(elDialog.children[2]);
434
+ expect(elems.length).to.be.equal(1);
435
+ });
436
+ });
437
+
438
+ describe('focus()', function() {
439
+ it ('should call "this.element(this.opts.focusElements)"', function() {
440
+ var spy = sinon.spy(RModal.prototype, 'elements');
441
+ var instance = create();
442
+
443
+ instance.focus();
444
+ expect(spy.withArgs(instance.opts.focusElements).calledOnce).to.be.true;
445
+ RModal.prototype.elements.restore();
446
+ });
447
+
448
+ it('should call "element.focus()"', function() {
449
+ elDialog.innerHTML = '<input type="text" class="test" />';
450
+ var el = elDialog.querySelector('input.test');
451
+
452
+ var spy = sinon.spy(el, 'focus');
453
+ var instance = create();
454
+
455
+ instance.focus(el);
456
+ expect(spy.calledOnce).to.be.true;
457
+ });
458
+
459
+ it('should call "this.dialog.firstChild.focus()"', function() {
460
+ var instance = create({
461
+ focus: []
462
+ });
463
+
464
+ elDialog.innerHTML = '<input type="text" />';
465
+ var spy = sinon.spy(instance.dialog.firstChild, 'focus');
466
+
467
+ instance.focus();
468
+ expect(spy.calledOnce).to.be.true;
469
+ });
470
+ });
471
+
472
+ describe('keydown()', function() {
473
+ var stubDialogContains;
474
+ beforeEach(function() {
475
+ stubDialogContains = sinon.stub(elDialog, 'contains').returns(true);
476
+ });
477
+
478
+ afterEach(function() {
479
+ elDialog.contains.restore();
480
+ })
481
+
482
+ it('should call "this.close()" on escape', function() {
483
+ var spy = sinon.spy(RModal.prototype, 'close');
484
+ var instance = create();
485
+ instance.keydown({
486
+ which: 27
487
+ });
488
+
489
+ expect(spy.calledOnce).to.be.true;
490
+ RModal.prototype.close.restore();
491
+ });
492
+
493
+ it('should not call "this.close()" on escape', function() {
494
+ var spy = sinon.spy(RModal.prototype, 'close');
495
+ var instance = create({
496
+ escapeClose: false
497
+ });
498
+ instance.keydown({
499
+ which: 27
500
+ });
501
+
502
+ expect(spy.calledOnce).to.be.false;
503
+ RModal.prototype.close.restore();
504
+ });
505
+
506
+ it('should call "this.dialog.contains()"', function() {
507
+ var instance = create();
508
+ instance.opened = true;
509
+
510
+ instance.keydown({
511
+ which: 9
512
+ , preventDefault: function() {}
513
+ , stopPropagation: function() {}
514
+ });
515
+ expect(
516
+ stubDialogContains.calledOnce
517
+ ).to.be.true;
518
+ });
519
+
520
+ it('should call "this.elements(this.option.focusElements)" on tab', function() {
521
+ var spy = sinon.spy(RModal.prototype, 'elements');
522
+ var instance = create();
523
+ instance.opened = true;
524
+
525
+ instance.keydown({
526
+ which: 9
527
+ , preventDefault: function() {}
528
+ , stopPropagation: function() {}
529
+ });
530
+ expect(
531
+ spy.withArgs(instance.opts.focusElements).calledOnce
532
+ ).to.be.true;
533
+ RModal.prototype.elements.restore();
534
+ });
535
+
536
+ it('should call "stopEvent()"', function() {
537
+ var stub = sinon.stub(RModal.prototype, 'elements')
538
+ .returns([ '1', '1' ]);
539
+
540
+ var instance = create();
541
+ instance.opened = true;
542
+
543
+ var ev = {
544
+ which: 9
545
+ , preventDefault: sinon.spy()
546
+ , stopPropagation: sinon.spy()
547
+ };
548
+ instance.keydown(ev);
549
+
550
+ expect(ev.preventDefault.calledOnce).to.be.true;
551
+ expect(ev.stopPropagation.calledOnce).to.be.true;
552
+
553
+ RModal.prototype.elements.restore();
554
+ });
555
+
556
+ it('should call "stopEvent()" and "last.focus()"', function() {
557
+ var spy = sinon.spy();
558
+ var stub = sinon.stub(RModal.prototype, 'elements')
559
+ .returns([
560
+ 'first'
561
+ , { focus: spy }
562
+ ]);
563
+
564
+ var instance = create();
565
+ instance.opened = true;
566
+
567
+ var ev = {
568
+ which: 9
569
+ , shiftKey: true
570
+ , target: 'first'
571
+ , preventDefault: sinon.spy()
572
+ , stopPropagation: sinon.spy()
573
+ };
574
+ instance.keydown(ev);
575
+
576
+ expect(ev.preventDefault.calledOnce).to.be.true;
577
+ expect(ev.stopPropagation.calledOnce).to.be.true;
578
+ expect(spy.calledOnce).to.be.true;
579
+
580
+ RModal.prototype.elements.restore();
581
+ });
582
+
583
+ it('should call "stopEvent()" and "first.focus()"', function() {
584
+ var spy = sinon.spy();
585
+ var stub = sinon.stub(RModal.prototype, 'elements')
586
+ .returns([
587
+ { focus: spy }
588
+ , 'last'
589
+ ]);
590
+
591
+ var instance = create();
592
+ instance.opened = true;
593
+
594
+ var ev = {
595
+ which: 9
596
+ , shiftKey: false
597
+ , target: 'last'
598
+ , preventDefault: sinon.spy()
599
+ , stopPropagation: sinon.spy()
600
+ };
601
+ instance.keydown(ev);
602
+
603
+ expect(ev.preventDefault.calledOnce).to.be.true;
604
+ expect(ev.stopPropagation.calledOnce).to.be.true;
605
+ expect(spy.calledOnce).to.be.true;
606
+
607
+ RModal.prototype.elements.restore();
608
+ });
609
+
610
+ it('should not call "stopEvent()"', function() {
611
+ var stub = sinon.stub(RModal.prototype, 'elements')
612
+ .returns([
613
+ 1, 2, 3
614
+ ]);
615
+
616
+ var instance = create();
617
+ instance.opened = true;
618
+
619
+ var ev = {
620
+ which: 9
621
+ , preventDefault: sinon.spy()
622
+ , stopPropagation: sinon.spy()
623
+ };
624
+ instance.keydown(ev);
625
+
626
+ expect(ev.preventDefault.calledOnce).to.be.false;
627
+ expect(ev.stopPropagation.calledOnce).to.be.false;
628
+
629
+ RModal.prototype.elements.restore();
630
+ });
631
+ });
632
+
633
+ it('should export RModal constructor', function() {
634
+ expect(RModal).to.be.a('function');
635
+ });
636
+ });