rails-iframe-resizer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 56c7db13cd4aa2a909fb1518cbe9bd5fe737a06d
4
+ data.tar.gz: e0a26711c2eedae3287c0d2aea8463d9c1540494
5
+ SHA512:
6
+ metadata.gz: 6289344e72115d546da411897f28bc3c2815b9fe600fb8b540da2fc4ef4ea73f994eb2a017963532ab736e29c573e0327806682381cae4464873a4911734806d
7
+ data.tar.gz: e4685b0a74377409f40f876832932276789f1b5808d2d4026bf2fe97595a9c9c5d16b8ffb91596eccd04a3cf2bfa86fa5b34f76b1d80e4a97671e26df34056d2
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Chris Mytton
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,16 @@
1
+ # Iframe Resizer for Rails
2
+
3
+ Rails wrapping for https://github.com/davidjbradshaw/iframe-resizer
4
+
5
+ ## Usage
6
+
7
+ Inside application.js, add:
8
+
9
+ ```js
10
+ // for host
11
+ //= require iframeResizer.min
12
+ // for iframe client
13
+ //= require iframeResizer.contentWindow.min
14
+ ```
15
+
16
+ Remove js min file source map comment, it is hard to embed into rails.
@@ -0,0 +1,578 @@
1
+ /*
2
+ * File: iframeSizer.contentWindow.js
3
+ * Desc: Include this file in any page being loaded into an iframe
4
+ * to force the iframe to resize to the content size.
5
+ * Requires: iframeResizer.js on host page.
6
+ * Author: David J. Bradshaw - dave@bradshaw.net
7
+ * Contributor: Jure Mav - jure.mav@gmail.com
8
+ */
9
+
10
+ ;(function() {
11
+ 'use strict';
12
+
13
+ var
14
+ autoResize = true,
15
+ base = 10,
16
+ bodyBackground = '',
17
+ bodyMargin = 0,
18
+ bodyMarginStr = '',
19
+ bodyPadding = '',
20
+ calculateWidth = false,
21
+ doubleEventList = {'resize':1,'click':1},
22
+ eventCancelTimer = 128,
23
+ height = 1,
24
+ firstRun = true,
25
+ heightCalcModeDefault = 'offset',
26
+ heightCalcMode = heightCalcModeDefault,
27
+ initLock = true,
28
+ initMsg = '',
29
+ interval = 32,
30
+ logging = false,
31
+ msgID = '[iFrameSizer]', //Must match host page msg ID
32
+ msgIdLen = msgID.length,
33
+ myID = '',
34
+ publicMethods = false,
35
+ resetRequiredMethods = {max:1,scroll:1,bodyScroll:1,documentElementScroll:1},
36
+ targetOriginDefault = '*',
37
+ target = window.parent,
38
+ tolerance = 0,
39
+ triggerLocked = false,
40
+ triggerLockedTimer = null,
41
+ width = 1;
42
+
43
+
44
+ function addEventListener(el,evt,func){
45
+ if ('addEventListener' in window){
46
+ el.addEventListener(evt,func, false);
47
+ } else if ('attachEvent' in window){ //IE
48
+ el.attachEvent('on'+evt,func);
49
+ }
50
+ }
51
+
52
+ function formatLogMsg(msg){
53
+ return msgID + '[' + myID + ']' + ' ' + msg;
54
+ }
55
+
56
+ function log(msg){
57
+ if (logging && ('object' === typeof window.console)){
58
+ console.log(formatLogMsg(msg));
59
+ }
60
+ }
61
+
62
+ function warn(msg){
63
+ if ('object' === typeof window.console){
64
+ console.warn(formatLogMsg(msg));
65
+ }
66
+ }
67
+
68
+
69
+ function init(){
70
+ log('Initialising iFrame');
71
+ readData();
72
+ setMargin();
73
+ setBodyStyle('background',bodyBackground);
74
+ setBodyStyle('padding',bodyPadding);
75
+ injectClearFixIntoBodyElement();
76
+ checkHeightMode();
77
+ stopInfiniteResizingOfIFrame();
78
+ setupPublicMethods();
79
+ startEventListeners();
80
+ sendSize('init','Init message from host page');
81
+ }
82
+
83
+ function readData(){
84
+
85
+ var data = initMsg.substr(msgIdLen).split(':');
86
+
87
+ function strBool(str){
88
+ return 'true' === str ? true : false;
89
+ }
90
+
91
+ myID = data[0];
92
+ bodyMargin = (undefined !== data[1]) ? Number(data[1]) : bodyMargin; //For V1 compatibility
93
+ calculateWidth = (undefined !== data[2]) ? strBool(data[2]) : calculateWidth;
94
+ logging = (undefined !== data[3]) ? strBool(data[3]) : logging;
95
+ interval = (undefined !== data[4]) ? Number(data[4]) : interval;
96
+ publicMethods = (undefined !== data[5]) ? strBool(data[5]) : publicMethods;
97
+ autoResize = (undefined !== data[6]) ? strBool(data[6]) : autoResize;
98
+ bodyMarginStr = data[7];
99
+ heightCalcMode = (undefined !== data[8]) ? data[8] : heightCalcMode;
100
+ bodyBackground = data[9];
101
+ bodyPadding = data[10];
102
+ tolerance = (undefined !== data[11]) ? Number(data[11]) : tolerance;
103
+ }
104
+
105
+ function chkCSS(attr,value){
106
+ if (-1 !== value.indexOf('-')){
107
+ warn('Negative CSS value ignored for '+attr);
108
+ value='';
109
+ }
110
+ return value;
111
+ }
112
+
113
+ function setBodyStyle(attr,value){
114
+ if ((undefined !== value) && ('' !== value) && ('null' !== value)){
115
+ document.body.style[attr] = value;
116
+ log('Body '+attr+' set to "'+value+'"');
117
+ }
118
+ }
119
+
120
+ function setMargin(){
121
+ //If called via V1 script, convert bodyMargin from int to str
122
+ if (undefined === bodyMarginStr){
123
+ bodyMarginStr = bodyMargin+'px';
124
+ }
125
+ chkCSS('margin',bodyMarginStr);
126
+ setBodyStyle('margin',bodyMarginStr);
127
+ }
128
+
129
+ function stopInfiniteResizingOfIFrame(){
130
+ document.documentElement.style.height = '';
131
+ document.body.style.height = '';
132
+ log('HTML & body height set to "auto"');
133
+ }
134
+
135
+ function initWindowResizeListener(){
136
+ addEventListener(window,'resize', function(){
137
+ sendSize('resize','Window resized');
138
+ });
139
+ }
140
+
141
+ function initWindowClickListener(){
142
+ addEventListener(window,'click', function(){
143
+ sendSize('click','Window clicked');
144
+ });
145
+ }
146
+
147
+ function checkHeightMode(){
148
+ if (heightCalcModeDefault !== heightCalcMode){
149
+ if (!(heightCalcMode in getHeight)){
150
+ warn(heightCalcMode + ' is not a valid option for heightCalculationMethod.');
151
+ heightCalcMode='bodyScroll';
152
+ }
153
+ log('Height calculation method set to "'+heightCalcMode+'"');
154
+ }
155
+ }
156
+
157
+ function startEventListeners(){
158
+ if ( true === autoResize ) {
159
+ initWindowResizeListener();
160
+ initWindowClickListener();
161
+ setupMutationObserver();
162
+ }
163
+ else {
164
+ log('Auto Resize disabled');
165
+ }
166
+ }
167
+
168
+ function injectClearFixIntoBodyElement(){
169
+ var clearFix = document.createElement('div');
170
+ clearFix.style.clear = 'both';
171
+ clearFix.style.display = 'block'; //Guard against this having been globally redefined in CSS.
172
+ document.body.appendChild(clearFix);
173
+ }
174
+
175
+ function setupPublicMethods(){
176
+ if (publicMethods) {
177
+ log('Enable public methods');
178
+
179
+ window.parentIFrame = {
180
+ close: function closeF(){
181
+ sendSize('close','parentIFrame.close()', 0, 0);
182
+ },
183
+ getId: function getIdF(){
184
+ return myID;
185
+ },
186
+ reset: function resetF(){
187
+ resetIFrame('parentIFrame.size');
188
+ },
189
+ scrollTo: function scrollToF(x,y){
190
+ sendMsg(y,x,'scrollTo'); // X&Y reversed at sendMsg uses hieght/width
191
+ },
192
+ scrollToOffset: function scrollToF(x,y){
193
+ sendMsg(y,x,'scrollToOffset'); // X&Y reversed at sendMsg uses hieght/width
194
+ },
195
+ sendMessage: function sendMessageF(msg,targetOrigin){
196
+ sendMsg(0,0,'message',JSON.stringify(msg),targetOrigin);
197
+ },
198
+ setHeightCalculationMethod: function setHeightCalculationMethodF(heightCalculationMethod){
199
+ heightCalcMode = heightCalculationMethod;
200
+ checkHeightMode();
201
+ },
202
+ setTargetOrigin: function setTargetOriginF(targetOrigin){
203
+ log('Set targetOrigin: '+targetOrigin);
204
+ targetOriginDefault = targetOrigin;
205
+ },
206
+ size: function sizeF(customHeight, customWidth){
207
+ var valString = ''+(customHeight?customHeight:'')+(customWidth?','+customWidth:'');
208
+ lockTrigger();
209
+ sendSize('size','parentIFrame.size('+valString+')', customHeight, customWidth);
210
+ }
211
+ };
212
+ }
213
+ }
214
+
215
+ function initInterval(){
216
+ if ( 0 !== interval ){
217
+ log('setInterval: '+interval+'ms');
218
+ setInterval(function(){
219
+ sendSize('interval','setInterval: '+interval);
220
+ },Math.abs(interval));
221
+ }
222
+ }
223
+
224
+ function setupInjectElementLoadListners(mutations){
225
+ function addLoadListener(element){
226
+ if (element.height === undefined || element.width === undefined || 0 === element.height || 0 === element.width){
227
+ log('Attach listerner to '+element.src);
228
+ addEventListener(element,'load', function imageLoaded(){
229
+ sendSize('imageLoad','Image loaded');
230
+ });
231
+ }
232
+ }
233
+
234
+ mutations.forEach(function (mutation) {
235
+ if (mutation.type === 'attributes' && mutation.attributeName === 'src'){
236
+ addLoadListener(mutation.target);
237
+ } else if (mutation.type === 'childList'){
238
+ var images = mutation.target.querySelectorAll('img');
239
+ Array.prototype.forEach.call(images,function (image) {
240
+ addLoadListener(image);
241
+ });
242
+ }
243
+ });
244
+ }
245
+
246
+ function setupMutationObserver(){
247
+
248
+ var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
249
+
250
+ function createMutationObserver(){
251
+ var
252
+ target = document.querySelector('body'),
253
+
254
+ config = {
255
+ attributes : true,
256
+ attributeOldValue : false,
257
+ characterData : true,
258
+ characterDataOldValue : false,
259
+ childList : true,
260
+ subtree : true
261
+ },
262
+
263
+ observer = new MutationObserver(function(mutations) {
264
+ sendSize('mutationObserver','mutationObserver: ' + mutations[0].target + ' ' + mutations[0].type);
265
+ setupInjectElementLoadListners(mutations); //Deal with WebKit asyncing image loading when tags are injected into the page
266
+ });
267
+
268
+ log('Enable MutationObserver');
269
+ observer.observe(target, config);
270
+ }
271
+
272
+ if (MutationObserver){
273
+ if (0 > interval) {
274
+ initInterval();
275
+ } else {
276
+ createMutationObserver();
277
+ }
278
+ }
279
+ else {
280
+ warn('MutationObserver not supported in this browser!');
281
+ initInterval();
282
+ }
283
+ }
284
+
285
+
286
+ // document.documentElement.offsetHeight is not reliable, so
287
+ // we have to jump through hoops to get a better value.
288
+ function getBodyOffsetHeight(){
289
+ function getComputedBodyStyle(prop) {
290
+ function convertUnitsToPxForIE8(value) {
291
+ var PIXEL = /^\d+(px)?$/i;
292
+
293
+ if (PIXEL.test(value)) {
294
+ return parseInt(value,base);
295
+ }
296
+
297
+ var
298
+ style = el.style.left,
299
+ runtimeStyle = el.runtimeStyle.left;
300
+
301
+ el.runtimeStyle.left = el.currentStyle.left;
302
+ el.style.left = value || 0;
303
+ value = el.style.pixelLeft;
304
+ el.style.left = style;
305
+ el.runtimeStyle.left = runtimeStyle;
306
+
307
+ return value;
308
+ }
309
+
310
+ var
311
+ el = document.body,
312
+ retVal = 0;
313
+
314
+ if (('defaultView' in document) && ('getComputedStyle' in document.defaultView)) {
315
+ retVal = document.defaultView.getComputedStyle(el, null);
316
+ retVal = (null !== retVal) ? retVal[prop] : 0;
317
+ } else {//IE8
318
+ retVal = convertUnitsToPxForIE8(el.currentStyle[prop]);
319
+ }
320
+
321
+ return parseInt(retVal,base);
322
+ }
323
+
324
+ return document.body.offsetHeight +
325
+ getComputedBodyStyle('marginTop') +
326
+ getComputedBodyStyle('marginBottom');
327
+ }
328
+
329
+ function getBodyScrollHeight(){
330
+ return document.body.scrollHeight;
331
+ }
332
+
333
+ function getDEOffsetHeight(){
334
+ return document.documentElement.offsetHeight;
335
+ }
336
+
337
+ function getDEScrollHeight(){
338
+ return document.documentElement.scrollHeight;
339
+ }
340
+
341
+ //From https://github.com/guardian/iframe-messenger
342
+ function getLowestElementHeight() {
343
+ var
344
+ allElements = document.querySelectorAll('body *'),
345
+ allElementsLength = allElements.length,
346
+ maxBottomVal = 0,
347
+ timer = new Date().getTime();
348
+
349
+ for (var i = 0; i < allElementsLength; i++) {
350
+ if (allElements[i].getBoundingClientRect().bottom > maxBottomVal) {
351
+ maxBottomVal = allElements[i].getBoundingClientRect().bottom;
352
+ }
353
+ }
354
+
355
+ timer = new Date().getTime() - timer;
356
+
357
+ log('Parsed '+allElementsLength+' HTML elements');
358
+ log('LowestElement bottom position calculated in ' + timer + 'ms');
359
+
360
+ return maxBottomVal;
361
+ }
362
+
363
+ function getAllHeights(){
364
+ return [
365
+ getBodyOffsetHeight(),
366
+ getBodyScrollHeight(),
367
+ getDEOffsetHeight(),
368
+ getDEScrollHeight()
369
+ ];
370
+ }
371
+
372
+ function getMaxHeight(){
373
+ return Math.max.apply(null,getAllHeights());
374
+ }
375
+
376
+ function getMinHeight(){
377
+ return Math.min.apply(null,getAllHeights());
378
+ }
379
+
380
+ function getBestHeight(){
381
+ return Math.max(getBodyOffsetHeight(),getLowestElementHeight());
382
+ }
383
+
384
+ var getHeight = {
385
+ offset : getBodyOffsetHeight, //Backward compatability
386
+ bodyOffset : getBodyOffsetHeight,
387
+ bodyScroll : getBodyScrollHeight,
388
+ documentElementOffset : getDEOffsetHeight,
389
+ scroll : getDEScrollHeight, //Backward compatability
390
+ documentElementScroll : getDEScrollHeight,
391
+ max : getMaxHeight,
392
+ min : getMinHeight,
393
+ grow : getMaxHeight,
394
+ lowestElement : getBestHeight
395
+ };
396
+
397
+ function getWidth(){
398
+ return Math.max(
399
+ document.documentElement.scrollWidth,
400
+ document.body.scrollWidth
401
+ );
402
+ }
403
+
404
+ function sendSize(triggerEvent, triggerEventDesc, customHeight, customWidth){
405
+
406
+ var currentHeight,currentWidth;
407
+
408
+ function recordTrigger(){
409
+ if (!(triggerEvent in {'reset':1,'resetPage':1,'init':1})){
410
+ log( 'Trigger event: ' + triggerEventDesc );
411
+ }
412
+ }
413
+
414
+ function resizeIFrame(){
415
+ height = currentHeight;
416
+ width = currentWidth;
417
+
418
+ sendMsg(height,width,triggerEvent);
419
+ }
420
+
421
+ function isDoubleFiredEvent(){
422
+ return triggerLocked && (triggerEvent in doubleEventList);
423
+ }
424
+
425
+ function isSizeChangeDetected(){
426
+ function checkTolarance(a,b){
427
+ var retVal = Math.abs(a-b) <= tolerance;
428
+ return !retVal;
429
+ }
430
+
431
+ currentHeight = (undefined !== customHeight) ? customHeight : getHeight[heightCalcMode]();
432
+ currentWidth = (undefined !== customWidth ) ? customWidth : getWidth();
433
+
434
+ return checkTolarance(height,currentHeight) ||
435
+ (calculateWidth && checkTolarance(width,currentWidth));
436
+
437
+ //return (height !== currentHeight) ||
438
+ // (calculateWidth && width !== currentWidth);
439
+ }
440
+
441
+ function isForceResizableEvent(){
442
+ return !(triggerEvent in {'init':1,'interval':1,'size':1});
443
+ }
444
+
445
+ function isForceResizableHeightCalcMode(){
446
+ return (heightCalcMode in resetRequiredMethods);
447
+ }
448
+
449
+ function logIgnored(){
450
+ log('No change in size detected');
451
+ }
452
+
453
+ function checkDownSizing(){
454
+ if (isForceResizableEvent() && isForceResizableHeightCalcMode()){
455
+ resetIFrame(triggerEventDesc);
456
+ } else if (!(triggerEvent in {'interval':1})){
457
+ recordTrigger();
458
+ logIgnored();
459
+ }
460
+ }
461
+
462
+ if (!isDoubleFiredEvent()){
463
+ if (isSizeChangeDetected()){
464
+ recordTrigger();
465
+ lockTrigger();
466
+ resizeIFrame();
467
+ } else {
468
+ checkDownSizing();
469
+ }
470
+ } else {
471
+ log('Trigger event cancelled: '+triggerEvent);
472
+ }
473
+ }
474
+
475
+ function lockTrigger(){
476
+ if (!triggerLocked){
477
+ triggerLocked = true;
478
+ log('Trigger event lock on');
479
+ }
480
+ clearTimeout(triggerLockedTimer);
481
+ triggerLockedTimer = setTimeout(function(){
482
+ triggerLocked = false;
483
+ log('Trigger event lock off');
484
+ log('--');
485
+ },eventCancelTimer);
486
+ }
487
+
488
+ function triggerReset(triggerEvent){
489
+ height = getHeight[heightCalcMode]();
490
+ width = getWidth();
491
+
492
+ sendMsg(height,width,triggerEvent);
493
+ }
494
+
495
+ function resetIFrame(triggerEventDesc){
496
+ var hcm = heightCalcMode;
497
+ heightCalcMode = heightCalcModeDefault;
498
+
499
+ log('Reset trigger event: ' + triggerEventDesc);
500
+ lockTrigger();
501
+ triggerReset('reset');
502
+
503
+ heightCalcMode = hcm;
504
+ }
505
+
506
+ function sendMsg(height,width,triggerEvent,msg,targetOrigin){
507
+ function setTargetOrigin(){
508
+ if (undefined === targetOrigin){
509
+ targetOrigin = targetOriginDefault;
510
+ } else {
511
+ log('Message targetOrigin: '+targetOrigin);
512
+ }
513
+ }
514
+
515
+ function sendToParent(){
516
+ var
517
+ size = height + ':' + width,
518
+ message = myID + ':' + size + ':' + triggerEvent + (undefined !== msg ? ':' + msg : '');
519
+
520
+ log('Sending message to host page (' + message + ')');
521
+ target.postMessage( msgID + message, targetOrigin);
522
+ }
523
+
524
+ setTargetOrigin();
525
+ sendToParent();
526
+ }
527
+
528
+ function receiver(event) {
529
+ function isMessageForUs(){
530
+ return msgID === (''+event.data).substr(0,msgIdLen); //''+ Protects against non-string messages
531
+ }
532
+
533
+ function initFromParent(){
534
+ initMsg = event.data;
535
+ target = event.source;
536
+
537
+ init();
538
+ firstRun = false;
539
+ setTimeout(function(){ initLock = false;},eventCancelTimer);
540
+ }
541
+
542
+ function resetFromParent(){
543
+ if (!initLock){
544
+ log('Page size reset by host page');
545
+ triggerReset('resetPage');
546
+ } else {
547
+ log('Page reset ignored by init');
548
+ }
549
+ }
550
+
551
+ function getMessageType(){
552
+ return event.data.split(']')[1];
553
+ }
554
+
555
+ function isMiddleTier(){
556
+ return ('iFrameResize' in window);
557
+ }
558
+
559
+ function isInitMsg(){
560
+ //test if this message is from a child below us. This is an ugly test, however, updating
561
+ //the message format would break backwards compatibity.
562
+ return event.data.split(':')[2] in {'true':1,'false':1};
563
+ }
564
+
565
+ if (isMessageForUs()){
566
+ if (firstRun && isInitMsg()){ //Check msg ID
567
+ initFromParent();
568
+ } else if ('reset' === getMessageType()){
569
+ resetFromParent();
570
+ } else if (event.data !== initMsg && !isMiddleTier()){
571
+ warn('Unexpected message ('+event.data+')');
572
+ }
573
+ }
574
+ }
575
+
576
+ addEventListener(window, 'message', receiver);
577
+
578
+ })();