feedbackandscreencap 0.0.4

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.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.DS_Store
3
+ *.rbc
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/.rbenv-version ADDED
@@ -0,0 +1 @@
1
+ 1.9.3-p125
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in feedbackandscreencap.gemspec
4
+ gemspec
5
+ gem 'rspec'
6
+ gem 'rails'
7
+ gem 'rspec-rails'
8
+ gem 'simplecov'
9
+ gem 'shoulda-matchers'
10
+ gem 'sqlite3'
11
+ gem 'feedbackandscreencap', :path => '../.'
12
+ #gem 'actioncontroller'
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Alberta Motor Association
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # FeedbackAndScreencap
2
+
3
+ This gem displays the feedback.js feedback button and processes the http post to create a new feedback record with text and image.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'feedback_and_screencap'
10
+
11
+ And then execute:
12
+
13
+ $ bundle install
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install feedback_and_screencap
18
+
19
+ Once it is installed, you need to do one thing:
20
+
21
+ * your html body tag needs to be modified:
22
+
23
+ <body onload="Feedback.init()">
24
+
25
+
26
+ ## Usage
27
+
28
+ rails g feedbackandscreencap
29
+
30
+ this create a migration and copy over the views, controller, model and assets
31
+
32
+ ##Contributing to feedback.js
33
+ Feedback.js is project by Niklas von Hertzen (https://github.com/niklasvh) and any issuesw with the javascript libraries or css should be directed to him via: https://github.com/niklasvh/feedback.js/
34
+
35
+ ## Contributing to the Rails Gem
36
+ 1. Fork it
37
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
38
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
39
+ 4. Push to the branch (`git push origin my-new-feature`)
40
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,664 @@
1
+ (function( win, doc, undefined ){
2
+
3
+ if ( win.Feedback !== undefined ) {
4
+ return;
5
+ }
6
+
7
+ // log proxy function
8
+ var log = function( msg ) {
9
+ win.console.log( msg );
10
+ },
11
+ // function to remove elements, input as arrays
12
+ removeElements = function( remove ) {
13
+ for (var i = 0, len = remove.length; i < len; i++ ) {
14
+ var item = Array.prototype.pop.call( remove );
15
+ if ( item !== undefined ) {
16
+ if (item.parentNode !== null ) { // check that the item was actually added to DOM
17
+ item.parentNode.removeChild( item );
18
+ }
19
+ }
20
+ }
21
+ },
22
+ getBounds = function( el ) {
23
+ return el.getBoundingClientRect();
24
+ },
25
+ emptyElements = function( el ) {
26
+ var item;
27
+ while( (( item = el.firstChild ) !== null ? el.removeChild( item ) : false) ) {}
28
+ },
29
+ element = function( name, text ) {
30
+ var el = doc.createElement( name );
31
+ el.appendChild( doc.createTextNode( text ) );
32
+ return el;
33
+ },
34
+ // script onload function to provide support for IE as well
35
+ scriptLoader = function( script, func ){
36
+
37
+ if (script.onload === undefined) {
38
+ // IE lack of support for script onload
39
+
40
+ if( script.onreadystatechange !== undefined ) {
41
+
42
+ var intervalFunc = function() {
43
+ if (script.readyState !== "loaded" && script.readyState !== "complete") {
44
+ win.setTimeout( intervalFunc, 250 );
45
+ } else {
46
+ // it is loaded
47
+ func();
48
+ }
49
+
50
+
51
+ };
52
+
53
+ win.setTimeout( intervalFunc, 250 );
54
+
55
+ } else {
56
+ log("ERROR: We can't track when script is loaded");
57
+ }
58
+
59
+ } else {
60
+ return func;
61
+ }
62
+
63
+ },
64
+ h2cQueue,
65
+ h2cCanvas,
66
+ h2cDone = false,
67
+ clickDone = false,
68
+ fcAvailable,
69
+ html2obj,
70
+ canvasHolder,
71
+ blackoutBox,
72
+ highlightBox,
73
+ highlightClose,
74
+ nextButton,
75
+ body,
76
+ CANVAS = "canvas",
77
+ DIV = "div",
78
+ H2C_IGNORE = "data-html2canvas-ignore",
79
+ PX = "px",
80
+ highlightContainer,
81
+ mouseMoveEvent,
82
+ modalBody,
83
+ message,
84
+ mouseClickEvent,
85
+ dataExclude = "data-exclude",
86
+ h2cAvailable;
87
+
88
+ win.Feedback = {
89
+ /**
90
+ * options
91
+ * string h2cPath
92
+ * string fcPath
93
+ * string label - Button label text, default "Send Feedback"
94
+ * string labelClass - Button class(es), default "btn bottom-right"
95
+ * string closeClass - Close class(es), default "close"
96
+ * string closeLabel
97
+ * string modalClass
98
+ * string modalHeader
99
+ * string modalHeaderClass
100
+ * string modalIntro
101
+ * string buttonClass
102
+ * Element appendTo - where to append button, default doc.body
103
+ */
104
+
105
+ debug: true,
106
+ log: log,
107
+
108
+ init: function( options ) {
109
+ var button,
110
+ modal,
111
+ body = doc.body,
112
+ glass = doc.createElement(DIV),
113
+ returnMethods = {
114
+
115
+
116
+
117
+
118
+ // open send feedback modal window
119
+ open: function() {
120
+ var modalHeader = doc.createElement(DIV),
121
+ modalFooter = doc.createElement(DIV),
122
+ script,
123
+ // execute the html2canvas script
124
+ runH2c = function(){
125
+ try {
126
+
127
+ options.onrendered = options.onrendered || function( canvas ) {
128
+ h2cCanvas = canvas;
129
+
130
+ // window.setTimeout(function(){
131
+ h2cDone = true;
132
+ nextFunc();
133
+ // }, 3000);
134
+
135
+
136
+ };
137
+
138
+ html2obj = win.html2canvas([ doc.body ], options);
139
+
140
+
141
+ } catch( e ) {
142
+
143
+ h2cDone = true;
144
+ nextFunc();
145
+ log("Error in html2canvas: " + e.message);
146
+ }
147
+ },
148
+
149
+
150
+ // Close button
151
+ a = element("a", options.closeLabel || "×");
152
+
153
+ modalBody = doc.createElement(DIV);
154
+
155
+ body.appendChild( glass );
156
+
157
+ // user message
158
+ message = doc.createElement("textarea");
159
+
160
+ if ( win.html2canvas === undefined && script === undefined ) {
161
+
162
+ // let's load html2canvas library while user is writing message
163
+
164
+ script = doc.createElement("script");
165
+ script.src = options.h2cPath || "libs/html2canvas.js";
166
+ script.onerror = function() {
167
+ h2cAvailable = false;
168
+ log("Failed to load html2canvas library, check that the path is correctly defined");
169
+ };
170
+
171
+ script.onload = (scriptLoader)(script, function() {
172
+
173
+ if (win.html2canvas === undefined) {
174
+ log("Loaded html2canvas, but library not found");
175
+ h2cAvailable = false;
176
+ return;
177
+ }
178
+
179
+ win.html2canvas.logging = win.Feedback.debug;
180
+ runH2c();
181
+
182
+ h2cAvailable = true;
183
+ });
184
+
185
+
186
+
187
+
188
+ button.parentNode.appendChild( script );
189
+ } else {
190
+ // html2canvas already loaded, just run it then
191
+ runH2c();
192
+ }
193
+
194
+ a.className = options.closeClass || "close";
195
+ a.onclick = returnMethods.close;
196
+ a.href = "#";
197
+
198
+ button.disabled = true;
199
+
200
+
201
+ modalHeader.appendChild( a );
202
+ modalHeader.appendChild( element("h3", options.modalHeader || "Send Feedback" ) );
203
+
204
+ modalHeader.className = "feedback-header";
205
+
206
+
207
+ modalBody.className = "feedback-body";
208
+
209
+ modalBody.appendChild( element("p", options.modalIntro || "Please describe the issue you are experiencing") );
210
+
211
+ modalBody.appendChild( message );
212
+
213
+
214
+ // Next button
215
+ nextButton = element( "button", options.nextLabel || "Continue" );
216
+
217
+ nextButton.className = options.buttonClass || "btn";
218
+ nextButton.onclick = function() {
219
+ clickDone = true;
220
+ nextButton.disabled = true;
221
+
222
+ emptyElements( modalBody ); // clear it of all elements
223
+
224
+ nextFunc();
225
+
226
+ };
227
+
228
+ function nextFunc() {
229
+
230
+ var progressBar,
231
+ action = true; // true highlight, false blackout
232
+
233
+ if ( clickDone ) {
234
+
235
+ nextButton.onclick = function( e ) {
236
+ e.preventDefault();
237
+
238
+ // remove event listeners
239
+ body.removeEventListener("mousemove", mouseMoveEvent, false);
240
+ body.removeEventListener("click", mouseClickEvent, false);
241
+
242
+ returnMethods.review();
243
+
244
+
245
+
246
+ };
247
+ }
248
+
249
+ // canvas ready and user has entered message
250
+ if (h2cDone && clickDone) {
251
+ nextButton.disabled = false;
252
+
253
+ var timer,
254
+ removeElement,
255
+ ctx,
256
+ buttonClickFunction = function( e ) {
257
+ e.preventDefault();
258
+ blackoutButton.classList.toggle("active");
259
+ highlightButton.classList.toggle("active");
260
+ action = !action;
261
+ },
262
+ clearBox = function() {
263
+ blackoutBox.style.left = "-5px";
264
+ blackoutBox.style.top = "-5px";
265
+ blackoutBox.style.width = "0px";
266
+ blackoutBox.style.height = "0px";
267
+ blackoutBox.setAttribute(dataExclude, true);
268
+
269
+
270
+ highlightBox.style.left = "-5px";
271
+ highlightBox.style.top = "-5px";
272
+ highlightBox.style.width = "0px";
273
+ highlightBox.style.height = "0px";
274
+ highlightBox.setAttribute(dataExclude, true);
275
+
276
+ win.clearTimeout( timer );
277
+ },
278
+ hideClose = function() {
279
+ highlightClose.style.left = "-50px";
280
+ highlightClose.style.top = "-50px";
281
+
282
+ },
283
+ blackoutButton = element("a", "Blackout"),
284
+ highlightButton = element("a", "Highlight"),
285
+ previousElement;
286
+
287
+
288
+ // delegate mouse move event for body
289
+ mouseMoveEvent = function(e) {
290
+
291
+ // set close button
292
+ if ( e.target !== previousElement && (e.target.classList.contains("feedback-blackedout") || e.target.classList.contains("feedback-highlighted"))) {
293
+
294
+ var left = (parseInt(e.target.style.left, 10) + parseInt(e.target.style.width, 10));
295
+ left = Math.max( left, 10 );
296
+
297
+ left = Math.min( left, win.innerWidth - 15 );
298
+
299
+ var top = (parseInt(e.target.style.top, 10));
300
+ top = Math.max( top, 10 );
301
+
302
+ highlightClose.style.left = left + PX;
303
+ highlightClose.style.top = top + PX;
304
+ removeElement = e.target;
305
+ clearBox();
306
+ previousElement = undefined;
307
+ return;
308
+ }
309
+
310
+ // don't do anything if we are highlighting a close button or body tag
311
+ if (e.target.nodeName === "BODY" || e.target === highlightClose || e.target === modal || e.target === nextButton || e.target.parentNode === modal || e.target.parentNode === modalHeader) {
312
+ // we are not gonna blackout the whole page or the close item
313
+ clearBox();
314
+ previousElement = e.target;
315
+ return;
316
+ }
317
+
318
+ hideClose();
319
+
320
+ if (e.target !== previousElement ) {
321
+ previousElement = e.target;
322
+
323
+ win.clearTimeout( timer );
324
+
325
+ timer = win.setTimeout(function(){
326
+ var bounds = getBounds( previousElement ),
327
+ item;
328
+
329
+ if ( action === false ) {
330
+ item = blackoutBox;
331
+ } else {
332
+ item = highlightBox;
333
+ item.width = bounds.width;
334
+ item.height = bounds.height;
335
+ ctx.drawImage(h2cCanvas, win.pageXOffset + bounds.left, win.pageYOffset + bounds.top, bounds.width, bounds.height, 0, 0, bounds.width, bounds.height );
336
+ }
337
+
338
+ // we are only targetting IE>=9, so window.pageYOffset works fine
339
+ item.setAttribute(dataExclude, false);
340
+ item.style.left = win.pageXOffset + bounds.left + PX;
341
+ item.style.top = win.pageYOffset + bounds.top + PX;
342
+ item.style.width = bounds.width + PX;
343
+ item.style.height = bounds.height + PX;
344
+ }, 100);
345
+
346
+
347
+
348
+ }
349
+
350
+
351
+ };
352
+
353
+
354
+ // delegate event for body click
355
+ mouseClickEvent = function( e ){
356
+ if (button.disabled === false) {
357
+ return;
358
+ }
359
+
360
+ e.preventDefault();
361
+
362
+
363
+ if ( action === false) {
364
+ if ( blackoutBox.getAttribute(dataExclude) === "false") {
365
+ var blackout = doc.createElement(DIV);
366
+ blackout.classList.add('feedback-blackedout');
367
+ blackout.style.left = blackoutBox.style.left;
368
+ blackout.style.top = blackoutBox.style.top;
369
+ blackout.style.width = blackoutBox.style.width;
370
+ blackout.style.height = blackoutBox.style.height;
371
+
372
+ body.appendChild( blackout );
373
+ previousElement = undefined;
374
+ }
375
+ } else {
376
+ if ( highlightBox.getAttribute(dataExclude) === "false") {
377
+
378
+ highlightBox.classList.add('feedback-highlighted');
379
+ highlightBox.classList.remove('feedback-highlight-element');
380
+ highlightBox = doc.createElement('canvas');
381
+
382
+ ctx = highlightBox.getContext("2d");
383
+
384
+ highlightBox.classList.add("feedback-highlight-element");
385
+
386
+ body.appendChild( highlightBox );
387
+ clearBox();
388
+ previousElement = undefined;
389
+ }
390
+ }
391
+
392
+
393
+
394
+ };
395
+
396
+ modal.classList.add('feedback-animate-toside');
397
+
398
+
399
+ blackoutBox = doc.createElement('div');
400
+ highlightBox = doc.createElement( CANVAS );
401
+
402
+ ctx = highlightBox.getContext("2d");
403
+
404
+
405
+ highlightClose = element(DIV, "×");
406
+ highlightContainer = doc.createElement('div');
407
+
408
+ highlightClose.id = "feedback-highlight-close";
409
+
410
+
411
+ highlightClose.addEventListener("click", function(){
412
+ removeElement.parentNode.removeChild( removeElement );
413
+ hideClose();
414
+ }, false);
415
+
416
+ body.appendChild( highlightClose );
417
+
418
+ h2cCanvas.classList.add('feedback-canvas');
419
+
420
+
421
+ body.appendChild( h2cCanvas);
422
+
423
+
424
+ var buttonItem = [ highlightButton, blackoutButton ];
425
+
426
+ modalBody.appendChild( element("p", "Highlight or blackout important information") );
427
+
428
+ // add highlight and blackout buttons
429
+ for (var i = 0; i < 2; i++ ) {
430
+ buttonItem[ i ].classList.add('btn');
431
+ buttonItem[ i ].classList.add('btn-small');
432
+ buttonItem[ i ].classList.add( i === 0 ? 'active' : 'btn-inverse');
433
+
434
+ buttonItem[ i ].href = "#";
435
+ buttonItem[ i ].onclick = buttonClickFunction;
436
+
437
+ modalBody.appendChild( buttonItem[ i ] );
438
+
439
+ modalBody.appendChild( doc.createTextNode(" ") );
440
+
441
+ }
442
+
443
+
444
+
445
+ highlightContainer.id = "feedback-highlight-container";
446
+ highlightContainer.style.width = h2cCanvas.width + PX;
447
+ highlightContainer.style.height = h2cCanvas.height + PX;
448
+
449
+ highlightBox.classList.add("feedback-highlight-element");
450
+ blackoutBox.id = "feedback-blackout-element";
451
+ body.appendChild( highlightBox );
452
+ highlightContainer.appendChild( blackoutBox );
453
+
454
+ body.appendChild( highlightContainer );
455
+
456
+ // bind mouse delegate events
457
+ body.addEventListener("mousemove", mouseMoveEvent, false);
458
+ body.addEventListener("click", mouseClickEvent, false);
459
+
460
+ } else if ( clickDone ) {
461
+
462
+ }
463
+ }
464
+
465
+
466
+
467
+ modalFooter.className = "feedback-footer";
468
+ modalFooter.appendChild( nextButton );
469
+
470
+ // modal container
471
+ modal = doc.createElement(DIV);
472
+ modal.className = options.modalClass || "modal";
473
+ modal.setAttribute(H2C_IGNORE, true); // don't render in html2canvas
474
+
475
+
476
+
477
+ modal.appendChild( modalHeader );
478
+ modal.appendChild( modalBody );
479
+ modal.appendChild( modalFooter );
480
+
481
+ button.parentNode.appendChild( modal );
482
+ },
483
+
484
+
485
+
486
+
487
+
488
+
489
+
490
+ // review the gathered data
491
+ review: function() {
492
+ modal.classList.add("feedback-animate-review");
493
+ emptyElements( modalBody );
494
+
495
+ var browserSpecs = doc.createElement(DIV);
496
+
497
+
498
+ if ( h2cCanvas !== undefined ) {
499
+ var ctx = h2cCanvas.getContext("2d"),
500
+ canvasCopy,
501
+ copyCtx,
502
+ radius = 5;
503
+ ctx.fillStyle = "#000";
504
+
505
+ // draw blackouts
506
+ Array.prototype.slice.call(doc.getElementsByClassName('feedback-blackedout'), 0).forEach( function( item ) {
507
+ var bounds = getBounds( item );
508
+ ctx.fillRect( bounds.left, bounds.top, bounds.width, bounds.height );
509
+ body.removeChild( item );
510
+
511
+ });
512
+
513
+
514
+ var items = Array.prototype.slice.call(doc.getElementsByClassName('feedback-highlighted'), 0);
515
+
516
+ if (items.length > 0 ) {
517
+
518
+ // copy canvas
519
+ canvasCopy = doc.createElement( CANVAS );
520
+ copyCtx = canvasCopy.getContext('2d');
521
+ canvasCopy.width = h2cCanvas.width;
522
+ canvasCopy.height = h2cCanvas.height;
523
+
524
+ copyCtx.drawImage(h2cCanvas, 0, 0);
525
+
526
+ ctx.fillStyle = "#777";
527
+ ctx.globalAlpha = 0.5;
528
+ ctx.fillRect( 0, 0, h2cCanvas.width, h2cCanvas.height );
529
+
530
+ ctx.beginPath();
531
+
532
+ items.forEach( function( item ) {
533
+ var bounds = getBounds( item );
534
+
535
+ var x = parseInt(item.style.left, 10),
536
+ y = parseInt(item.style.top, 10),
537
+ width = parseInt(item.style.width, 10),
538
+ height = parseInt(item.style.height, 10);
539
+
540
+ ctx.moveTo(x + radius, y);
541
+ ctx.lineTo(x + width - radius, y);
542
+ ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
543
+ ctx.lineTo(x + width, y + height - radius);
544
+ ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
545
+ ctx.lineTo(x + radius, y + height);
546
+ ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
547
+ ctx.lineTo(x, y + radius);
548
+ ctx.quadraticCurveTo(x, y, x + radius, y);
549
+ body.removeChild( item );
550
+
551
+ });
552
+ ctx.closePath();
553
+ ctx.clip();
554
+
555
+ ctx.globalAlpha = 1;
556
+
557
+ ctx.drawImage(canvasCopy, 0,0);
558
+ removeElements( [ glass ] );
559
+ }
560
+
561
+ canvasCopy = doc.createElement( CANVAS );
562
+ copyCtx = canvasCopy.getContext('2d');
563
+ canvasCopy.width = 300;
564
+ canvasCopy.height = Math.round(h2cCanvas.height * (canvasCopy.width / h2cCanvas.width));
565
+
566
+ copyCtx.drawImage(h2cCanvas, 0, 0, h2cCanvas.width, h2cCanvas.height, 0, 0, canvasCopy.width, canvasCopy.height);
567
+ modalBody.appendChild( canvasCopy );
568
+
569
+ fullSize = doc.createElement( CANVAS );
570
+ copyCtx = fullSize.getContext('2d');
571
+ fullSize.width = h2cCanvas.width;
572
+ fullSize.height = h2cCanvas.height;
573
+
574
+ copyCtx.drawImage(h2cCanvas, 0, 0, h2cCanvas.width, h2cCanvas.height, 0, 0, fullSize.width, fullSize.height);
575
+
576
+
577
+ modalBody.appendChild( canvasCopy );
578
+
579
+ nextButton.firstChild.nodeValue = "Send form";
580
+ nextButton.onclick = function()
581
+ {
582
+ var http = new XMLHttpRequest();
583
+ var url = "feedback_and_screencaps";
584
+ var dataUrl = fullSize.toDataURL();
585
+ var params = "feedback_and_screencap[message]=" + message.value + "&feedback_and_screencap[screencap]=" + encodeURIComponent(dataUrl); //canvasCopy.toDataURL("image/png");
586
+ http.open("POST", url, true);
587
+ //Send the proper header information along with the request
588
+ http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
589
+ http.send(params);
590
+ returnMethods.close();
591
+ };
592
+
593
+ h2cCanvas.classList.add("feedback-canvas-complete");
594
+
595
+ browserSpecs.style.height = canvasCopy.height + 20 + PX;
596
+
597
+ }
598
+
599
+ browserSpecs.classList.add("feedback-browser");
600
+ browserSpecs.appendChild(element("h3", "Browser information"));
601
+
602
+ modalBody.appendChild( browserSpecs );
603
+
604
+ modalBody.appendChild( element("p", options.modalIntro || "Please describe the issue you are experiencing") );
605
+
606
+ modalBody.appendChild( message );
607
+
608
+
609
+ },
610
+
611
+
612
+
613
+
614
+
615
+
616
+
617
+ // close modal window
618
+ close: function() {
619
+
620
+ button.disabled = false;
621
+ h2cDone = false;
622
+ clickDone = false;
623
+
624
+
625
+ // remove feedback elements
626
+
627
+ removeElements( [ modal, glass, blackoutBox, highlightBox, highlightClose, highlightContainer, h2cCanvas ] );
628
+ removeElements( doc.getElementsByClassName('feedback-blackedout') );
629
+ removeElements( doc.getElementsByClassName('feedback-highlighted') );
630
+
631
+ // remove event listeners
632
+ body.removeEventListener("mousemove", mouseMoveEvent, false);
633
+ body.removeEventListener("click", mouseClickEvent, false);
634
+
635
+ h2cCanvas = undefined;
636
+
637
+
638
+ return false;
639
+
640
+ }
641
+ };
642
+
643
+ glass.classList.add("feedback-glass");
644
+ glass.style.pointerEvents = "none";
645
+ glass.setAttribute(H2C_IGNORE, true);
646
+
647
+ options = options || {};
648
+
649
+ button = element("button", options.label || "Send Feedback");
650
+ button.className = options.labelClass || "btn bottom-right";
651
+
652
+ button.setAttribute(H2C_IGNORE, true);
653
+
654
+ button.onclick = returnMethods.open;
655
+
656
+ ((options.appendTo !== undefined) ? options.appendTo : doc.body).appendChild( button );
657
+ }
658
+
659
+ };
660
+
661
+
662
+ })( window, document );
663
+
664
+