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 +18 -0
- data/.rbenv-version +1 -0
- data/.rspec +2 -0
- data/Gemfile +12 -0
- data/LICENSE +22 -0
- data/README.md +40 -0
- data/Rakefile +2 -0
- data/app/assets/javascripts/feedback.js +664 -0
- data/app/assets/javascripts/libs/html2canvas.js +3246 -0
- data/app/assets/javascripts/libs/html2canvas.min.js +70 -0
- data/app/assets/stylesheets/feedback/base.css +738 -0
- data/app/controllers/feedback_and_screencaps_controller.rb +51 -0
- data/app/models/feedback_and_screencap.rb +5 -0
- data/app/views/feedback_and_screencaps/index.html.erb +25 -0
- data/app/views/feedback_and_screencaps/show.html.erb +15 -0
- data/config/routes.rb +3 -0
- data/feedbackandscreencap.gemspec +23 -0
- data/lib/application_controller.rb +13 -0
- data/lib/engine.rb +11 -0
- data/lib/feedbackandscreencap.rb +6 -0
- data/lib/feedbackandscreencap/version.rb +3 -0
- data/lib/rails/generators/feedbackandscreencap/feedbackandscreencap_generator.rb +56 -0
- data/lib/rails/generators/feedbackandscreencap/templates/initializer.rb +5 -0
- data/lib/rails/generators/feedbackandscreencap/templates/migration.rb +9 -0
- data/lib/rails/generators/feedbackandscreencap/templates/schema.rb +12 -0
- data/spec/models/feedbackandscreencap_spec.rb +10 -0
- data/spec/spec_helper.rb +35 -0
- metadata +170 -0
data/.gitignore
ADDED
data/.rbenv-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.9.3-p125
|
data/.rspec
ADDED
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,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
|
+
|