rails_admin_live_edit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ });