Skip to content

Commit 41df0bd

Browse files
committed
MOBILE-1988 rte: Resize textarea to fill the screen
1 parent a207fee commit 41df0bd

File tree

4 files changed

+229
-52
lines changed

4 files changed

+229
-52
lines changed

www/core/directives/autorows.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ angular.module('mm.core')
2828
*
2929
* @param {Number} [mmMaxRows] Maximum number of rows to be shown. Defaults to 5.
3030
*/
31-
.directive('mmAutoRows', function() {
31+
.directive('mmAutoRows', function($mmUtil) {
3232

3333
/**
3434
* Determine how many rows should be set for the given textarea.
@@ -42,7 +42,7 @@ angular.module('mm.core')
4242
maxRows = parseInt(attrs.mmMaxRows, 10) || 5,
4343
computedStyle = getComputedStyle(element[0]),
4444
padding = (parseInt(computedStyle.paddingBottom, 10) || 0) + (parseInt(computedStyle.paddingTop, 10) || 0),
45-
height = (element[0].offsetHeight || element[0].height || element[0].clientHeight || 0) - padding,
45+
height = $mmUtil.getElementHeight(element[0]) - padding,
4646
scrollHeight,
4747
rows;
4848

www/core/directives/formattext.js

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -87,27 +87,21 @@ angular.module('mm.core')
8787
* @return {Number} The width of the element in pixels. When 0 is returned it means the element is not visible.
8888
*/
8989
function getElementWidth(element) {
90-
var width = element.offsetWidth || element.width || element.clientWidth;
90+
var width = $mmUtil.getElementWidth(element);
9191

9292
if (!width) {
9393
// All elements inside are floating or inline. Change display mode to allow calculate the width.
9494
var angElement = angular.element(element),
95-
parentNode = element.parentNode,
96-
parentWidth = parentNode.offsetWidth || parentNode.width || parentNode.clientWidth,
95+
parentWidth = $mmUtil.getElementWidth(element.parentNode, true, false, false, true),
9796
previousDisplay = angElement.css('display');
9897

9998
angElement.css('display', 'inline-block');
10099

101-
width = element.offsetWidth || element.width || element.clientWidth;
100+
width = $mmUtil.getElementWidth(element);
102101

103-
if (parentWidth > 0) {
104-
var cs = getComputedStyle(parentNode);
105-
parentWidth -= (parseFloat(cs.paddingLeft) + parseFloat(cs.paddingRight));
106-
107-
// If width is incorrectly calculated use parent width instead.
108-
if (parentWidth > 0 && (!width || width > parentWidth)) {
109-
width = parentWidth;
110-
}
102+
// If width is incorrectly calculated use parent width instead.
103+
if (parentWidth > 0 && (!width || width > parentWidth)) {
104+
width = parentWidth;
111105
}
112106

113107
angElement.css('display', previousDisplay);
@@ -130,7 +124,7 @@ angular.module('mm.core')
130124
// Disable media adapt to correctly calculate the height.
131125
elementAng.removeClass('mm-enabled-media-adapt');
132126

133-
height = element.offsetHeight || element.height || element.clientHeight;
127+
height = $mmUtil.getElementHeight(element);
134128

135129
elementAng.addClass('mm-enabled-media-adapt');
136130

www/core/directives/richtexteditor.js

Lines changed: 132 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,7 @@ angular.module('mm.core')
6969
}
7070

7171
if (ionContentEl.nodeName == 'ION-CONTENT') {
72-
ionContentHeight = ionContentEl.offsetHeight || ionContentEl.height || ionContentEl.clientHeight;
73-
return $window.innerHeight - ionContentHeight;
72+
return $window.innerHeight - $mmUtil.getElementHeight(ionContentEl);
7473
} else {
7574
return 0;
7675
}
@@ -106,6 +105,31 @@ angular.module('mm.core')
106105
}
107106
}
108107

108+
/**
109+
* Get the height of the surrounding elements from the current to the top element.
110+
*
111+
* @param {Object} element Directive DOM element to get surroundings elements from.
112+
* @param {Object} top Top DOM element to stop getting elements.
113+
* @return {Number} Surrounding height in px.
114+
*/
115+
function getSurroundingHeight(element, top) {
116+
var height = 0;
117+
118+
while (element.parentNode && element.parentNode.tagName != "ION-CONTENT" && (!top || element != top)) {
119+
var parent = element.parentNode;
120+
angular.forEach(parent.childNodes, function(child) {
121+
if (child.tagName && element.tagName && element.tagName != 'MM-LOADING' && child != element) {
122+
height += $mmUtil.getElementHeight(child, false, true, true);
123+
}
124+
});
125+
element = parent;
126+
}
127+
var cs = getComputedStyle(element);
128+
height += (parseInt(cs.paddingTop, 10) + parseInt(cs.paddingBottom, 10));
129+
130+
return height;
131+
}
132+
109133
/**
110134
* Search WYSIWYG iframe and format its contents.
111135
*
@@ -293,26 +317,60 @@ angular.module('mm.core')
293317

294318
// Check if we should use rich text editor.
295319
$mmUtil.isRichTextEditorEnabled().then(function(enabled) {
320+
var promise;
321+
296322
scope.richTextEditor = !!enabled;
297323
renderTime = new Date().getTime();
298324

299325
if (enabled) {
300326
// Get current language to configure the editor.
301-
$mmLang.getCurrentLanguage().then(function(lang) {
327+
promise = $mmLang.getCurrentLanguage().then(function(lang) {
302328
defaultOptions.language = changeLanguageCode(lang);
303-
304-
if ($ionicPlatform.isTablet()) {
305-
scope.editorOptions = angular.extend(defaultOptions, scope.options, scope.tabletOptions);
306-
} else {
307-
scope.editorOptions = angular.extend(defaultOptions, scope.options, scope.phoneOptions);
308-
}
309-
310-
editorInitialHeight = scope.editorOptions.height;
311-
adjustHeight = scope.editorOptions.adjustHeight;
312329
});
330+
} else {
331+
promise = $q.when();
313332
}
333+
334+
promise.then(function() {
335+
if ($ionicPlatform.isTablet()) {
336+
scope.editorOptions = angular.extend(defaultOptions, scope.options, scope.tabletOptions);
337+
} else {
338+
scope.editorOptions = angular.extend(defaultOptions, scope.options, scope.phoneOptions);
339+
}
340+
341+
editorInitialHeight = scope.editorOptions.height;
342+
adjustHeight = scope.editorOptions.adjustHeight;
343+
344+
if (!enabled) {
345+
textareaReady();
346+
}
347+
});
314348
});
315349

350+
// Function called when textarea is ready.
351+
function textareaReady() {
352+
var editorEl;
353+
$timeout(function() {
354+
if (firstChange) {
355+
firstChange = false;
356+
editorEl = element.querySelector('.mm-textarea');
357+
resizeContentTextarea(editorEl);
358+
359+
// Listen for event resize.
360+
ionic.on('resize', onResize, window);
361+
}
362+
}, 1000);
363+
364+
scope.$on('$destroy', function() {
365+
ionic.off('resize', onResize, window);
366+
});
367+
368+
// Window resized.
369+
function onResize() {
370+
resizeContentTextarea(editorEl);
371+
}
372+
}
373+
316374
// Function called when editor is ready.
317375
scope.editorReady = function() {
318376
// Editor is ready, setup listeners.
@@ -427,9 +485,60 @@ angular.module('mm.core')
427485
}
428486
};
429487

430-
// Resize the editor to fill the visible screen size (except top bar).
488+
/**
489+
* Resize the textarea to fill the visible screen size (except top bar).
490+
*
491+
* @param {Object} editorEl Textare element
492+
*/
493+
function resizeContentTextarea(editorEl) {
494+
var editorHeight = editorInitialHeight,
495+
contentVisibleHeight,
496+
screenSmallerThanEditor;
497+
498+
if (typeof fixedBarsHeight == 'undefined') {
499+
fixedBarsHeight = calculateFixedBarsHeight(editorEl);
500+
}
501+
502+
contentVisibleHeight = $window.innerHeight - fixedBarsHeight;
503+
504+
// Editor is ready, adjust Height if needed.
505+
if (adjustHeight && contentVisibleHeight > 0) {
506+
var topElement,
507+
height;
508+
509+
if (adjustHeight !== true) {
510+
topElement = document.getElementById(adjustHeight);
511+
contentVisibleHeight = $mmUtil.getElementHeight(topElement) || contentVisibleHeight;
512+
}
513+
514+
height = getSurroundingHeight(element, topElement);
515+
if (contentVisibleHeight > height) {
516+
editorHeight = contentVisibleHeight - height;
517+
editorInitialHeight = editorHeight;
518+
}
519+
}
520+
screenSmallerThanEditor = contentVisibleHeight > 0 && contentVisibleHeight < editorHeight;
521+
522+
if (resized && !screenSmallerThanEditor) {
523+
// The editor was resized but now it isn't needed anymore, undo the resize.
524+
undoResize(editorEl);
525+
} else if (editorHeight > 60 && (resized || screenSmallerThanEditor || adjustHeight)) {
526+
// The visible screen size is lower than the editor size, resize editor to fill the screen.
527+
// We don't resize if the content new height is too small.
528+
angular.element(editorEl).css('height', editorHeight + 'px');
529+
resized = true;
530+
}
531+
}
532+
533+
/**
534+
* Resize the editor to fill the visible screen size (except top bar).
535+
*
536+
* @param {Object} editorEl Editor element of CKE (the biggest element .cke)
537+
* @param {Object} contentsEl Just the writtable part of CKE (.cke_contents)
538+
* @param {Object} toolbar Toolbar of CKE editor (.cke_bottom)
539+
*/
431540
function resizeContent(editorEl, contentsEl, toolbar) {
432-
var toolbarHeight = toolbar.offsetHeight || toolbar.height || toolbar.clientHeight || 0,
541+
var toolbarHeight = $mmUtil.getElementHeight(toolbar),
433542
editorWithToolbarHeight = editorInitialHeight + toolbarHeight,
434543
contentVisibleHeight,
435544
editorContentNewHeight,
@@ -445,31 +554,15 @@ angular.module('mm.core')
445554

446555
// Editor is ready, adjust Height if needed.
447556
if (adjustHeight && !editorMaximized && contentVisibleHeight > 0) {
448-
var currentElement = element,
449-
topElement,
450-
height = 0;
557+
var topElement,
558+
height;
451559

452560
if (adjustHeight !== true) {
453561
topElement = document.getElementById(adjustHeight);
454-
contentVisibleHeight = topElement.offsetHeight || topElement.height || topElement.clientHeight ||
455-
contentVisibleHeight;
562+
contentVisibleHeight = $mmUtil.getElementHeight(topElement) || contentVisibleHeight;
456563
}
457-
while (currentElement.parentNode && currentElement.parentNode.tagName != "ION-CONTENT" &&
458-
(!topElement || currentElement != topElement)) {
459-
460-
var parent = currentElement.parentNode;
461-
angular.forEach(parent.childNodes, function(child) {
462-
if (child.tagName && currentElement.tagName &&
463-
currentElement.tagName != 'MM-LOADING' && child != currentElement) {
464-
height += (child.offsetHeight || child.height || child.clientHeight);
465-
var cs = getComputedStyle(child);
466-
height += (parseInt(cs.borderTop, 10) + parseInt(cs.borderBottom, 10));
467-
}
468-
});
469-
currentElement = parent;
470-
}
471-
var cs = getComputedStyle(currentElement);
472-
height += (parseInt(cs.paddingTop, 10) + parseInt(cs.paddingBottom, 10));
564+
565+
height = getSurroundingHeight(element, topElement);
473566
if (contentVisibleHeight > height) {
474567
editorWithToolbarHeight = contentVisibleHeight - height;
475568
editorInitialHeight = editorWithToolbarHeight - toolbarHeight;
@@ -504,8 +597,10 @@ angular.module('mm.core')
504597

505598
// Undo resize.
506599
function undoResize(editorEl, contentsEl) {
507-
angular.element(editorEl).css('height', 'auto');
508-
angular.element(contentsEl).css('height', editorInitialHeight + 'px');
600+
angular.element(editorEl).css('height', '');
601+
if (contentsEl) {
602+
angular.element(contentsEl).css('height', editorInitialHeight + 'px');
603+
}
509604
resized = false;
510605
}
511606
}

www/core/lib/util.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2259,6 +2259,94 @@ angular.module('mm.core')
22592259
});
22602260
};
22612261

2262+
/**
2263+
* Returns element height of an element.
2264+
*
2265+
* @param {Object} element DOM element to measure.
2266+
* @param {Boolean} [usePadding=false] Use padding to calculate the measure.
2267+
* @param {Boolean} [useMargin=false] Use margin to calculate the measure.
2268+
* @param {Boolean} [useBorder=false] Use borders to calculate the measure.
2269+
* @param {Boolean} [innerMeasure=false] If inner measure is needed: padding, margin or borders will be substracted.
2270+
* @return {Number} Height in pixels.
2271+
*/
2272+
self.getElementHeight = function(element, usePadding, useMargin, useBorder, innerMeasure) {
2273+
var measure = element.offsetHeight || element.height || element.clientHeight || 0;
2274+
2275+
// Measure not correctly taken.
2276+
if (measure <= 0) {
2277+
var angElement = angular.element(element);
2278+
if (angElement.css('display') == '') {
2279+
angElement.css('display', 'inline-block');
2280+
measure = element.offsetHeight || element.height || element.clientHeight || 0;
2281+
angElement.css('display', '');
2282+
}
2283+
}
2284+
2285+
if (usePadding || useMargin || useBorder) {
2286+
var surround = 0,
2287+
cs = getComputedStyle(element);
2288+
if (usePadding) {
2289+
surround += parseInt(cs.paddingTop, 10) + parseInt(cs.paddingBottom, 10);
2290+
}
2291+
if (useMargin) {
2292+
surround += parseInt(cs.marginTop, 10) + parseInt(cs.marginBottom, 10);
2293+
}
2294+
if (useBorder) {
2295+
surround += parseInt(cs.borderTop, 10) + parseInt(cs.borderBottom, 10);
2296+
}
2297+
if (innerMeasure) {
2298+
measure = measure > surround ? measure - surround : 0;
2299+
} else {
2300+
measure += surround;
2301+
}
2302+
}
2303+
return measure;
2304+
};
2305+
2306+
/**
2307+
* Returns element width of an element.
2308+
*
2309+
* @param {Object} element DOM element to measure.
2310+
* @param {Boolean} [usePadding=false] Use padding to calculate the measure.
2311+
* @param {Boolean} [useMargin=false] Use margin to calculate the measure.
2312+
* @param {Boolean} [useBorder=false] Use borders to calculate the measure.
2313+
* @param {Boolean} [innerMeasure=false] If inner measure is needed: padding, margin or borders will be substracted.
2314+
* @return {Number} Witdh in pixels.
2315+
*/
2316+
self.getElementWidth = function(element, usePadding, useMargin, useBorder, innerMeasure) {
2317+
var measure = element.offsetWidth || element.width || element.clientWidth || 0;
2318+
2319+
// Measure not correctly taken.
2320+
if (measure <= 0) {
2321+
var angElement = angular.element(element);
2322+
if (angElement.css('display') == '') {
2323+
angElement.css('display', 'inline-block');
2324+
measure = element.offsetWidth || element.width || element.clientWidth || 0;
2325+
angElement.css('display', '');
2326+
}
2327+
}
2328+
2329+
if (usePadding || useMargin || useBorder) {
2330+
var surround = 0,
2331+
cs = getComputedStyle(element);
2332+
if (usePadding) {
2333+
surround += parseInt(cs.paddingLeft, 10) + parseInt(cs.paddingRight, 10);
2334+
}
2335+
if (useMargin) {
2336+
surround += parseInt(cs.marginLeft, 10) + parseInt(cs.marginRight, 10);
2337+
}
2338+
if (useBorder) {
2339+
surround += parseInt(cs.borderLeft, 10) + parseInt(cs.borderRight, 10);
2340+
}
2341+
if (innerMeasure) {
2342+
measure = measure > surround ? measure - surround : 0;
2343+
} else {
2344+
measure += surround;
2345+
}
2346+
}
2347+
return measure;
2348+
};
2349+
22622350
return self;
22632351
};
22642352
});

0 commit comments

Comments
 (0)