UNPKG

11.3 kBJavaScriptView Raw
1/*
2 Terminal Kit
3
4 Copyright (c) 2009 - 2020 Cédric Ronvel
5
6 The MIT License (MIT)
7
8 Permission is hereby granted, free of charge, to any person obtaining a copy
9 of this software and associated documentation files (the "Software"), to deal
10 in the Software without restriction, including without limitation the rights
11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the Software is
13 furnished to do so, subject to the following conditions:
14
15 The above copyright notice and this permission notice shall be included in all
16 copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 SOFTWARE.
25*/
26
27"use strict" ;
28
29
30
31/* /!\ DEPRECATED: It should use the bobbox module!!! /!\ */
32
33const Element = require( './Element.js' ) ;
34const Container = require( './Container.js' ) ;
35const boxesChars = require( '../spChars.js' ).box ;
36
37
38
39function Layout( options ) {
40 // Clone options if necessary
41 options = ! options ? {} : options.internal ? options : Object.create( options ) ;
42 options.internal = true ;
43
44 Element.call( this , options ) ;
45
46 this.layoutDef = options.layout ;
47 this.computed = {} ;
48 this.boxesContainer = {} ;
49 this.boxChars = boxesChars.light ;
50
51 if ( options.boxChars ) {
52 if ( typeof options.boxChars === 'object' ) {
53 this.boxChars = options.boxChars ;
54 }
55 else if ( typeof options.boxChars === 'string' && boxesChars[ options.boxChars ] ) {
56 this.boxChars = boxesChars[ options.boxChars ] ;
57 }
58 }
59
60 // Only draw if we are not a superclass of the object
61 if ( this.elementType === 'Layout' && ! options.noDraw ) { this.draw() ; }
62}
63
64module.exports = Layout ;
65
66Layout.prototype = Object.create( Element.prototype ) ;
67Layout.prototype.constructor = Layout ;
68Layout.prototype.elementType = 'Layout' ;
69
70
71
72Layout.prototype.destroy = function( isSubDestroy ) {
73 Element.prototype.destroy.call( this , isSubDestroy ) ;
74} ;
75
76
77
78Layout.prototype.preDrawSelf = function() {
79 var y , tees = {} ;
80
81 this.computeBoundingBoxes() ;
82
83 // Draw the top border
84 this.outputDst.put(
85 { x: this.computed.xmin , y: this.computed.ymin } ,
86 this.boxChars.topLeft + this.boxChars.horizontal.repeat( this.computed.dx - 1 ) + this.boxChars.topRight
87 ) ;
88
89 // Draw the bottom border
90 this.outputDst.put(
91 { x: this.computed.xmin , y: this.computed.ymax } ,
92 this.boxChars.bottomLeft + this.boxChars.horizontal.repeat( this.computed.dx - 1 ) + this.boxChars.bottomRight
93 ) ;
94
95 // Draw the left and right border
96 for ( y = this.computed.ymin + 1 ; y < this.computed.ymax ; y ++ ) {
97 this.outputDst.put( { x: this.computed.xmin , y: y } , this.boxChars.vertical ) ;
98 this.outputDst.put( { x: this.computed.xmax , y: y } , this.boxChars.vertical ) ;
99 }
100
101 this.drawRecursive( this.computed , tees ) ;
102} ;
103
104
105
106Layout.prototype.computeBoundingBoxes = function() {
107 var computed = this.computed = {} ;
108
109 var layoutDef = this.layoutDef ;
110
111 var parent = {
112 width_: this.outputDst.width ,
113 height_: this.outputDst.height ,
114 dx_: this.outputDst.width - 1 ,
115 dy_: this.outputDst.height - 1 ,
116 xmin_: 0 ,
117 ymin_: 0
118 } ;
119
120 var inProgress = {
121 offsetX: ( this.layoutDef.x ) || 0 ,
122 offsetY: ( this.layoutDef.y ) || 0 ,
123 remainingDx: parent.dx_ ,
124 remainingDy: parent.dy_
125 } ;
126
127 this.computeBoundingBoxes_( layoutDef , computed , parent , inProgress ) ;
128} ;
129
130
131
132Layout.prototype.computeBoundingBoxes_ = function( layoutDef , computed , parent , inProgress ) {
133 var i , nextInProgress , hasChild = false ;
134
135 //console.error( "\n\nlayoutDef #" + layoutDef.id + ':\n' , computed ) ;
136
137 this.computeDxDy( layoutDef , computed , parent , inProgress ) ;
138
139 //console.error( "\n\nlayoutDef #" + layoutDef.id + ':\n' , computed ) ;
140
141 computed.xmin_ = parent.xmin_ + inProgress.offsetX ;
142 computed.xmax_ = computed.xmin_ + computed.dx_ ;
143 computed.ymin_ = parent.ymin_ + inProgress.offsetY ;
144 computed.ymax_ = computed.ymin_ + computed.dy_ ;
145
146 //console.error( "\n\nlayoutDef #" + layoutDef.id + ':\n' , computed ) ;
147
148 // Check if it goes out of its parent
149 if ( computed.xmax_ > parent.xmax_ ) {
150 computed.xmax_ = parent.xmax_ ;
151 computed.dx_ = computed.xmax_ - computed.xmin_ ;
152 }
153
154 if ( computed.ymax_ > parent.ymax_ ) {
155 computed.ymax_ = parent.ymax_ ;
156 computed.dy_ = computed.ymax_ - computed.ymin_ ;
157 }
158
159 // Width and height are not used internally, but provided for userland
160 computed.width_ = computed.dx_ + 1 ;
161 computed.height_ = computed.dy_ + 1 ;
162
163 computed.columns = [] ;
164 computed.rows = [] ;
165
166 //console.error( "\n\nlayoutDef #" + layoutDef.id + ':\n' , computed ) ;
167
168 nextInProgress = {
169 offsetX: 0 ,
170 offsetY: 0 ,
171 remainingDx: computed.dx_ ,
172 remainingDy: computed.dy_ ,
173 autoDxCount: 0 ,
174 autoDyCount: 0
175 } ;
176
177 if ( layoutDef.columns && layoutDef.columns.length ) {
178 // First pass
179 for ( i = 0 ; i < layoutDef.columns.length ; i ++ ) {
180 computed.columns[ i ] = {} ;
181 this.computeDxDy( layoutDef.columns[ i ] , computed.columns[ i ] , computed , nextInProgress , true ) ;
182
183 if ( computed.columns[ i ].dx_ !== undefined ) { nextInProgress.remainingDx -= computed.columns[ i ].dx_ ; }
184 else { nextInProgress.autoDxCount ++ ; }
185 }
186
187 for ( i = 0 ; i < layoutDef.columns.length ; i ++ ) {
188 this.computeBoundingBoxes_( layoutDef.columns[ i ] , computed.columns[ i ] , computed , nextInProgress ) ;
189 nextInProgress.offsetX = computed.columns[ i ].xmax_ - computed.xmin_ ;
190 }
191
192 hasChild = true ;
193 }
194 else if ( layoutDef.rows && layoutDef.rows.length ) {
195 // First pass
196 for ( i = 0 ; i < layoutDef.rows.length ; i ++ ) {
197 computed.rows[ i ] = {} ;
198 this.computeDxDy( layoutDef.rows[ i ] , computed.rows[ i ] , computed , nextInProgress , true ) ;
199
200 if ( computed.rows[ i ].dy_ !== undefined ) { nextInProgress.remainingDy -= computed.rows[ i ].dy_ ; }
201 else { nextInProgress.autoDyCount ++ ; }
202 }
203
204 for ( i = 0 ; i < layoutDef.rows.length ; i ++ ) {
205 this.computeBoundingBoxes_( layoutDef.rows[ i ] , computed.rows[ i ] , computed , nextInProgress ) ;
206 nextInProgress.offsetY = computed.rows[ i ].ymax_ - computed.ymin_ ;
207 }
208
209 hasChild = true ;
210 }
211
212 computed.width_ = computed.dx_ + 1 ;
213 computed.height_ = computed.dy_ + 1 ;
214
215 this.round( computed ) ;
216 //console.error( "\n\nfinal #" + layoutDef.id + ':\n' , computed ) ;
217
218 // Container surfaces are only created for "leaf" boxes, i.e. boxes that don't have child
219 if ( ! hasChild ) {
220 if ( this.boxesContainer[ layoutDef.id ] ) {
221 if ( this.boxesContainer[ layoutDef.id ].width !== computed.width - 2 || this.boxesContainer[ layoutDef.id ].height !== computed.height - 2 ) {
222 this.boxesContainer[ layoutDef.id ].resize( {
223 x: 0 ,
224 y: 0 ,
225 width: computed.width - 2 ,
226 height: computed.height - 2
227 } ) ;
228 }
229
230 this.boxesContainer[ layoutDef.id ].outputX = computed.xmin + 1 ;
231 this.boxesContainer[ layoutDef.id ].outputY = computed.ymin + 1 ;
232
233 this.boxesContainer[ layoutDef.id ].moveTo(
234 this.boxesContainer[ layoutDef.id ].outputX ,
235 this.boxesContainer[ layoutDef.id ].outputY
236 ) ;
237 }
238 else {
239 var container = new Container( {
240 internal: true ,
241 id: layoutDef.id ,
242 parent: this ,
243 outputDst: this.outputDst ,
244 outputX: computed.xmin + 1 ,
245 outputY: computed.ymin + 1 ,
246 outputWidth: computed.width - 2 ,
247 outputHeight: computed.height - 2
248 } ) ;
249
250 layoutDef.id = container.id ;
251 this.boxesContainer[ layoutDef.id ] = container ;
252 }
253 }
254} ;
255
256
257
258Layout.prototype.computeDxDy = function( layoutDef , computed , parent , inProgress , firstPass ) {
259 //console.error( ">>>>>>>>>> #" + layoutDef.id + ' firstPass: ' , !! firstPass ) ;
260
261 // Dx
262 if ( firstPass || computed.dx_ === undefined ) {
263 if ( layoutDef.width !== undefined ) {
264 computed.dx_ = Math.max( 0 , Math.min( parent.dx_ , layoutDef.width - 1 ) ) ;
265 }
266 else if ( layoutDef.widthPercent !== undefined ) {
267 computed.dx_ = Math.max( 0 , Math.min( parent.dx_ , parent.dx_ * layoutDef.widthPercent / 100 ) ) ;
268 }
269 else if ( ! firstPass ) {
270 //console.error( ">>>>>>>>>> #" + layoutDef.id + ' remaining dx: ' , inProgress.remainingDx , '/' , inProgress.autoDxCount , ' --- ' , inProgress ) ;
271 computed.dx_ = Math.max( 0 , inProgress.remainingDx / ( inProgress.autoDxCount || 1 ) ) ;
272 //console.error( ">>>>>>>>>> #" + layoutDef.id + ' computed dx: ' , computed.dx_ ) ;
273 }
274 }
275
276 // Dy
277 if ( firstPass || computed.dy_ === undefined ) {
278 if ( layoutDef.height !== undefined ) {
279 computed.dy_ = Math.max( 0 , Math.min( parent.dy_ , layoutDef.height - 1 ) ) ;
280 }
281 else if ( layoutDef.heightPercent !== undefined ) {
282 computed.dy_ = Math.max( 0 , Math.min( parent.dy_ , parent.dy_ * layoutDef.heightPercent / 100 ) ) ;
283 }
284 else if ( ! firstPass ) {
285 computed.dy_ = Math.max( 0 , inProgress.remainingDy / ( inProgress.autoDyCount || 1 ) ) ;
286 }
287 }
288} ;
289
290
291
292Layout.prototype.round = function( computed ) {
293 computed.xmin = Math.round( computed.xmin_ ) ;
294 computed.xmax = Math.round( computed.xmax_ ) ;
295 computed.ymin = Math.round( computed.ymin_ ) ;
296 computed.ymax = Math.round( computed.ymax_ ) ;
297
298 computed.dx = computed.xmax - computed.xmin ;
299 computed.dy = computed.ymax - computed.ymin ;
300 computed.width = computed.dx + 1 ;
301 computed.height = computed.dy + 1 ;
302} ;
303
304
305
306Layout.prototype.drawRecursive = function( computed , tees ) {
307 var i ;
308
309 if ( computed.columns.length ) {
310 for ( i = 0 ; i < computed.columns.length ; i ++ ) {
311 this.drawColumn( computed.columns[ i ] , tees , i === computed.columns.length - 1 ) ;
312 }
313 }
314 else if ( computed.rows.length ) {
315 for ( i = 0 ; i < computed.rows.length ; i ++ ) {
316 this.drawRow( computed.rows[ i ] , tees , i === computed.rows.length - 1 ) ;
317 }
318 }
319} ;
320
321
322
323Layout.prototype.drawColumn = function( computed , tees , last ) {
324 var y ;
325
326 if ( ! last ) {
327 // Draw Tee-junction
328 this.drawTee( computed.xmax , computed.ymin , 'top' , tees ) ;
329 this.drawTee( computed.xmax , computed.ymax , 'bottom' , tees ) ;
330
331 // Draw the right border
332 for ( y = computed.ymin + 1 ; y < computed.ymax ; y ++ ) {
333 this.outputDst.put( { x: computed.xmax , y: y } , this.boxChars.vertical ) ;
334 }
335 }
336
337 this.drawRecursive( computed , tees ) ;
338} ;
339
340
341
342Layout.prototype.drawTee = function( x , y , type , tees ) {
343 var key = x + ':' + y ;
344
345 if ( ! tees[ key ] ) {
346 this.outputDst.put( { x: x , y: y } , this.boxChars[ type + 'Tee' ] ) ;
347 tees[ key ] = type ;
348 }
349 else if ( tees[ key ] !== type ) {
350 this.outputDst.put( { x: x , y: y } , this.boxChars.cross ) ;
351 }
352} ;
353
354
355
356Layout.prototype.drawRow = function( computed , tees , last ) {
357 if ( ! last ) {
358 // Draw Tee-junction
359 this.drawTee( computed.xmin , computed.ymax , 'left' , tees ) ;
360 this.drawTee( computed.xmax , computed.ymax , 'right' , tees ) ;
361
362 // Draw the bottom border
363 this.outputDst.put( { x: computed.xmin + 1 , y: computed.ymax } , this.boxChars.horizontal.repeat( computed.dx - 1 ) ) ;
364 }
365
366 this.drawRecursive( computed , tees ) ;
367} ;
368