1var time_buffer_value = 0; // Customization of bufer time for DAN 2var is_check_start_time_gone = false; // Check start time or end time for the time, which is gone already TODAY. 3var start_time_checking_index; 4//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 5 6/** 7 * Get dots for partially booked dates. 8 * For parent booking resources with specific capacity, 9 * System do not show partially booked dates, if at least one child booking resource fully available 10 * Otherwise it show maximum number of time-slot in one specific child booking resource 11 * 12 * @param param_calendar_id ID of booking resource 13 * @param my_thisDateTime timestamp of date 14 * @returns {string} 15 */ 16function wpbc_show_date_info_top( param_calendar_id, my_thisDateTime ){ 17 18 var resource_id = parseInt( param_calendar_id.replace( "calendar_booking", '' ) ); 19 20 // console.log( _wpbc.bookings_in_calendar__get( resource_id ) ); // for debug 21 22 // 1. Get child booking resources or single booking resource that exist in dates : [1] | [1,14,15,17] 23 var child_resources_arr = wpbc_clone_obj( _wpbc.booking__get_param_value( resource_id, 'resources_id_arr__in_dates' ) ); 24 25 // '2023-08-21' 26 var sql_date = wpbc__get__sql_class_date( new Date( my_thisDateTime ) ); 27 28 var child_resource_id; 29 var merged_seconds; 30 var dots_count = 0; 31 32 var dots_in__resources = []; 33 34 // Loop all resources ID 35 for ( var res_key in child_resources_arr ){ 36 37 child_resource_id = child_resources_arr[ res_key ]; 38 39 // _wpbc.bookings_in_calendar__get_for_date(2,'2023-08-21')[12].booked_time_slots.merged_seconds = [ "07:00:11 - 07:30:02", "10:00:11 - 00:00:00" ] 40 // _wpbc.bookings_in_calendar__get_for_date(2,'2023-08-21')[2].booked_time_slots.merged_seconds = [ [ 25211, 27002 ], [ 36011, 86400 ] ] 41 42 if ( false !== _wpbc.bookings_in_calendar__get_for_date( resource_id, sql_date ) ){ 43 merged_seconds = _wpbc.bookings_in_calendar__get_for_date( resource_id, sql_date )[ child_resource_id ].booked_time_slots.merged_seconds; // [ [ 25211, 27002 ], [ 36011, 86400 ] ] 44 } else { 45 merged_seconds = []; 46 } 47 48 if ( 0 === merged_seconds.length ){ 49 return ''; // Day available 50 } 51 52 for ( var i = 0; i < merged_seconds.length; i++ ){ 53 if ( ! wpbc_is_this_timeslot__full_day_booked( merged_seconds[i] ) ){ // Check if this fully booked date. If yes, then do not count it 54 dots_count++; 55 } 56 } 57 58 dots_in__resources.push( dots_count ); 59 dots_count = 0; 60 } 61 62 var dots_count_max = Math.max.apply( Math, dots_in__resources ); // Get maximum value in array [ 1, 5, 3] -> 5 63 64 var dot_content = ''; 65 for ( var d = 0; d < dots_count_max; d++ ){ 66 dot_content += '·'; 67 } 68 69 dot_content = ( '' !== dot_content ) ? '<div class="wpbc_time_dots">' + dot_content + '</div>' : ''; 70 71 return dot_content; 72} 73 74 75function wpbc_show_date_info_bottom( param_calendar_id, my_thisDateTime ) { 76 77 if ( typeof( wpbc_show_day_cost_in_date_bottom ) == 'function' ) { 78 79 return wpbc_show_day_cost_in_date_bottom( param_calendar_id, my_thisDateTime ); 80 81 } else { 82 return ''; 83 } 84} 85 86 87 88/** 89 * Hide Tippy tooltip on scroll, to prevent issue on mobile touch devices of showing tooltip at top left corner! 90 * @param evt 91 */ 92jQuery( window ).on( 'scroll', function ( event ){ //FixIn: 9.2.1.5 //FixIn: 9.4.3.3 93 if ( 'function' === typeof( wpbc_tippy ) ){ 94 wpbc_tippy.hideAll(); 95 } 96} ); 97 98 99/** 100 * Check if in booking form exist times fields for booking for specific time-slot 101 * @param resource_id 102 * @param form_elements 103 * @returns {boolean} 104 */ 105function wpbc_is_time_field_in_booking_form( resource_id, form_elements ){ //FixIn: 8.2.1.28 106 107 var count = form_elements.length; 108 var start_time = false; 109 var end_time = false; 110 var duration = false; 111 var element; 112 113 /** 114 * Get from booking form 'rangetime', 'durationtime', 'starttime', 'endtime', if exists. 115 */ 116 for ( var i = 0; i < count; i++ ){ 117 118 element = form_elements[ i ]; 119 120 // Skip elements from garbage 121 if ( jQuery( element ).closest( '.booking_form_garbage' ).length ){ //FixIn: 7.1.2.14 122 continue; 123 } 124 125 if ( 126 ( element.name != undefined ) 127 && ( element.name.indexOf( 'hint' ) !== -1 ) //FixIn: 9.5.5.2 128 ){ 129 var my_element = element.name; //.toString(); 130 if ( my_element.indexOf( 'rangetime' ) !== -1 ){ // Range Time 131 132 return true; 133 } 134 if ( (my_element.indexOf( 'durationtime' ) !== -1) ){ // Duration 135 duration = element.value; 136 } 137 if ( my_element.indexOf( 'starttime' ) !== -1 ){ // Start Time 138 start_time = element.value; 139 } 140 if ( my_element.indexOf( 'endtime' ) !== -1 ){ // End Time 141 end_time = element.value; 142 } 143 } 144 } 145 146 // Duration get Values 147 if ( ( duration !== false ) && ( start_time !== false ) ){ // we have Duration and Start time 148 return true; 149 } 150 151 if ( ( start_time !== false ) && ( end_time !== false ) ){ // we have End time and Start time 152 return true; 153 } 154 155 return false; 156} 157 158 159//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 160//TODO: Continue Refactoring here 2018-04-21
161//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 162 163 164 //PS: This function from ../booking/inc/js/personal.js 165 function wpbc_is_this_time_selection_not_available( resource_id, form_elements ){ 166 167 // Skip this checking if we are in the Admin panel at Add booking page 168 if ( location.href.indexOf( 'page=wpbc-new' ) > 0 ) { 169 return false; 170 } 171 172 var count = form_elements.length; 173 var start_time = false; 174 var end_time = false; 175 var duration = false; 176 var element; 177 var element_start = false; 178 var element_end = false; 179 var element_duration = false; 180 var element_rangetime = false; 181 182 /** 183 * Get from booking form 'rangetime', 'durationtime', 'starttime', 'endtime', if exists. 184 */ 185 for ( var i = 0; i < count; i++ ){ 186 187 element = form_elements[ i ]; 188 189 // Skip elements from garbage 190 if ( jQuery( element ).closest( '.booking_form_garbage' ).length ){ //FixIn: 7.1.2.14 191 continue; 192 } 193 194 if ( 195 ( element.name != undefined ) 196 && ( element.name.indexOf( 'hint' ) === -1 ) //FixIn: 9.5.5.2 //FixIn: 9.6.3.9 197 ){ 198 var my_element = element.name; //.toString(); 199 if ( my_element.indexOf( 'rangetime' ) !== -1 ){ // Range Time 200 if ( element.value == '' ){ //FixIn: 7.0.Beta.19 201 var notice_message_id = wpbc_front_end__show_message__warning( element, _wpbc.get_message( 'message_check_required' ) ); 202 return true; 203 } 204 var my_rangetime = element.value.split( '-' ); 205 if ( my_rangetime.length > 1 ){ 206 start_time = my_rangetime[ 0 ].replace( /(^\s+)|(\s+$)/g, "" ); // Trim 207 end_time = my_rangetime[ 1 ].replace( /(^\s+)|(\s+$)/g, "" ); 208 element_rangetime = element; 209 } 210 } 211 if ( (my_element.indexOf( 'durationtime' ) !== -1) ){ // Duration 212 duration = element.value; 213 element_duration = element; 214 } 215 if ( my_element.indexOf( 'starttime' ) !== -1 ){ // Start Time 216 start_time = element.value; 217 element_start = element; 218 } 219 if ( my_element.indexOf( 'endtime' ) !== -1 ){ // End Time 220 end_time = element.value; 221 element_end = element; 222 } 223 } 224 } 225 226 227 // Duration get Values 228 if ( (duration !== false) && (start_time !== false) ){ // we have Duration and Start time so try to get End time 229 230 var mylocalstarttime = start_time.split( ':' ); 231 var d = new Date( 1980, 1, 1, mylocalstarttime[ 0 ], mylocalstarttime[ 1 ], 0 ); 232 233 var my_duration = duration.split( ':' ); 234 my_duration = my_duration[ 0 ] * 60 * 60 * 1000 + my_duration[ 1 ] * 60 * 1000; 235 d.setTime( d.getTime() + my_duration ); 236 237 var my_hours = d.getHours(); 238 if ( my_hours < 10 ) my_hours = '0' + (my_hours + ''); 239 var my_minutes = d.getMinutes(); 240 if ( my_minutes < 10 ) my_minutes = '0' + (my_minutes + ''); 241 242 // We are get end time 243 end_time = (my_hours + '') + ':' + (my_minutes + ''); 244 if ( end_time == '00:00' ) end_time = '23:59'; 245 } 246 247 248 if ( (start_time === false) || (end_time === false) ){ // We do not have Start or End time or Both of them, so do not check it 249 250 return false; 251 252 } else { 253 254 var valid_time = true; 255 if ( (start_time == '') || (end_time == '') ) valid_time = false; 256 257 if ( !isValidTimeTextField( start_time ) ) valid_time = false; 258 if ( !isValidTimeTextField( end_time ) ) valid_time = false; 259 260 if ( valid_time === true ) 261 if ( 262 (typeof(checkRecurentTimeInside) == 'function') && 263 264 ( _wpbc.get_other_param( 'is_enabled_booking_recurrent_time' ) == true ) 265 ){ // Recheck Time here !!! 266 valid_time = checkRecurentTimeInside( [ start_time, end_time ], resource_id ); 267 } else { 268 269 if ( typeof(checkTimeInside) == 'function' ){ 270 valid_time = checkTimeInside( start_time, true, resource_id ); 271 } 272 273 if ( valid_time === true ){ 274 if ( typeof(checkTimeInside) == 'function' ){ 275 valid_time = checkTimeInside( end_time, false, resource_id ); 276 } 277 } 278 } 279 280 if ( valid_time !== true ){ 281 //return false; // do not show warning for setting pending days selectable, if making booking for time-slot //FixIn: 7.0.1.23 282 if ( (_wpbc.get_other_param( 'is_enabled_change_over' )) && (element_start !== false) && (element_end !== false) ){ //FixIn:6.1.1.1
283 wpbc_front_end__show_message__warning_under_element( '#date_booking' + resource_id, _wpbc.get_message( 'message_check_no_selected_dates' ) ); 284 } 285 if ( element_rangetime !== false ){ wpbc_front_end__show_message__warning_under_element( element_rangetime, _wpbc.get_message( 'message_error_range_time' ) ); } 286 if ( element_duration !== false ){ wpbc_front_end__show_message__warning_under_element( element_duration, _wpbc.get_message( 'message_error_duration_time' ) ); } 287 if ( element_start !== false ){ wpbc_front_end__show_message__warning_under_element( element_start, _wpbc.get_message( 'message_error_start_time' ) ); } 288 if ( element_end !== false ){ wpbc_front_end__show_message__warning_under_element( element_end, _wpbc.get_message( 'message_error_end_time' ) ); } 289 290 return true; 291 292 } else { 293 return false; 294 } 295 296 } 297 298 299 } 300 301 302 function isTimeTodayGone( myTime, sort_date_array ){ 303 var date_to_check = sort_date_array[ 0 ]; 304 if ( is_check_start_time_gone == false ){ 305 date_to_check = sort_date_array[ (sort_date_array.length - 1) ]; 306 } 307 308 if ( parseInt( date_to_check[ 0 ] ) < parseInt( _wpbc.get_other_param( 'today_arr' )[ 0 ] ) ) return true; 309 if ( (parseInt( date_to_check[ 0 ] ) == parseInt( _wpbc.get_other_param( 'today_arr' )[ 0 ] )) && (parseInt( date_to_check[ 1 ] ) < parseInt( _wpbc.get_other_param( 'today_arr' )[ 1 ] )) ) 310 return true; 311 if ( (parseInt( date_to_check[ 0 ] ) == parseInt( _wpbc.get_other_param( 'today_arr' )[ 0 ] )) && (parseInt( date_to_check[ 1 ] ) == parseInt( _wpbc.get_other_param( 'today_arr' )[ 1 ] )) && (parseInt( date_to_check[ 2 ] ) < parseInt( _wpbc.get_other_param( 'today_arr' )[ 2 ] )) ) 312 return true; 313 if ( (parseInt( date_to_check[ 0 ] ) == parseInt( _wpbc.get_other_param( 'today_arr' )[ 0 ] )) && 314 (parseInt( date_to_check[ 1 ] ) == parseInt( _wpbc.get_other_param( 'today_arr' )[ 1 ] )) && 315 (parseInt( date_to_check[ 2 ] ) == parseInt( _wpbc.get_other_param( 'today_arr' )[ 2 ] )) ){ 316 var mytime_value = myTime.split( ":" ); 317 mytime_value = mytime_value[ 0 ] * 60 + parseInt( mytime_value[ 1 ] ); 318 319 var current_time_value = _wpbc.get_other_param( 'today_arr' )[ 3 ] * 60 + parseInt( _wpbc.get_other_param( 'today_arr' )[ 4 ] ); 320 321 if ( current_time_value > mytime_value ) return true; 322 323 } 324 return false; 325 } 326 327 328 function checkTimeInside( mytime, is_start_time, bk_type ){ 329 330 var my_dates_str = document.getElementById( 'date_booking' + bk_type ).value; // GET DATES From TEXTAREA 331 332 if ( my_dates_str.indexOf( ' - ' ) != 0 ){ 333 334 my_dates_str = my_dates_str.replace( ' - ', ', ' ); //FixIn: 10.2.3.2 335 } 336 337 return checkTimeInsideProcess( mytime, is_start_time, bk_type, my_dates_str ); 338 } 339 340 341 function checkRecurentTimeInside( my_rangetime, bk_type ){ 342 343 var valid_time = true; 344 var my_dates_str = document.getElementById( 'date_booking' + bk_type ).value; // GET DATES From TEXTAREA 345 // recurrent time check for all days in loop 346 347 var date_array = my_dates_str.split( ", " ); 348 if ( date_array.length == 2 ){ // This recheck is need for editing booking, with single day 349 if ( date_array[ 0 ] == date_array[ 1 ] ){ 350 date_array = [ date_array[ 0 ] ]; 351 } 352 } 353 var temp_date_str = ''; 354 for ( var i = 0; i < date_array.length; i++ ){ // Get SORTED selected days array 355 temp_date_str = date_array[ i ]; 356 if ( checkTimeInsideProcess( my_rangetime[ 0 ], true, bk_type, temp_date_str ) == false ) valid_time = false; 357 if ( checkTimeInsideProcess( my_rangetime[ 1 ], false, bk_type, temp_date_str ) == false ) valid_time = false; 358 359 } 360 361 return valid_time; 362 } 363 364 365// Function check start and end time at selected days 366 function checkTimeInsideProcess( mytime, is_start_time, bk_type, my_dates_str ){ 367 var i, h, s, m; //FixIn: 9.1.5.1 368 369 var date_array = my_dates_str.split( ", " ); 370 if ( date_array.length == 2 ){ // This recheck is need for editing booking, with single day 371 if ( date_array[ 0 ] == date_array[ 1 ] ){ 372 date_array = [ date_array[ 0 ] ]; 373 } 374 } 375 376 var temp_elemnt; 377 var td_class; 378 var sort_date_array = []; 379 var work_date_array = []; 380 var times_array = []; 381 var is_check_for_time; 382 383 for ( var i = 0; i < date_array.length; i++ ){ // Get SORTED selected days array 384 temp_elemnt = date_array[ i ].split( "." ); 385 sort_date_array[ i ] = [ temp_elemnt[ 2 ], temp_elemnt[ 1 ] + '', temp_elemnt[ 0 ] + '' ]; // [2009,7,1],... 386 } 387 sort_date_array.sort(); // SORT D a t e s 388 for ( i = 0; i < sort_date_array.length;
388 i++ ){ // trnasform to integers 389 sort_date_array[ i ] = [ parseInt( sort_date_array[ i ][ 0 ] * 1 ), parseInt( sort_date_array[ i ][ 1 ] * 1 ), parseInt( sort_date_array[ i ][ 2 ] * 1 ) ]; // [2009,7,1],... 390 } 391 392 if ( ((is_check_start_time_gone) && (is_start_time)) || 393 ((!is_check_start_time_gone) && (!is_start_time)) ){ 394 395 if ( isTimeTodayGone( mytime, sort_date_array ) ) return false; 396 } 397 // CHECK FOR BOOKING INSIDE OF S E L E C T E D DAY RANGE AND FOR TOTALLY BOOKED DAYS AT THE START AND END OF RANGE 398 work_date_array = sort_date_array; 399 for ( var j = 0; j < work_date_array.length; j++ ){ 400 td_class = work_date_array[ j ][ 1 ] + '-' + work_date_array[ j ][ 2 ] + '-' + work_date_array[ j ][ 0 ]; 401 402 if ( (j == 0) || (j == (work_date_array.length - 1)) ) is_check_for_time = true; // Check for time only start and end time 403 else is_check_for_time = false; 404 405 406 } /////////////////////////////////////////////////////////////////////////////////////////////////////// 407 408 409 // Check START OR END time for time no in correct fee range 410 if ( is_start_time ) work_date_array = sort_date_array[ 0 ]; 411 else work_date_array = sort_date_array[ sort_date_array.length - 1 ]; 412 413 td_class = work_date_array[ 1 ] + '-' + work_date_array[ 2 ] + '-' + work_date_array[ 0 ]; 414 415 // Get dates and time from pending dates 416 //deleted 417 // Get dates and time from pending dates 418 //deleted 419 420 421 times_array.sort(); // SORT TIMES 422 423 var times_in_day = []; // array with all times 424 var times_in_day_interval_marks = []; // array with time interval marks 1- stsrt time 2 - end time 425 426 427 for ( i = 0; i < times_array.length; i++ ){ 428 s = times_array[ i ][ 2 ]; // s = 2 - end time, s = 1 - start time 429 // Start close interval 430 if ( (s == 2) && (i == 0) ){ 431 times_in_day[ times_in_day.length ] = 0; 432 times_in_day_interval_marks[ times_in_day_interval_marks.length ] = 1; 433 } 434 // Normal 435 times_in_day[ times_in_day.length ] = times_array[ i ][ 0 ] * 60 + parseInt( times_array[ i ][ 1 ] ); 436 times_in_day_interval_marks[ times_in_day_interval_marks.length ] = s; 437 // End close interval 438 if ( (s == 1) && (i == (times_array.length - 1)) ){ 439 times_in_day[ times_in_day.length ] = (24 * 60); 440 times_in_day_interval_marks[ times_in_day_interval_marks.length ] = 2; 441 } 442 } 443 444 // Get time from entered time 445 var mytime_value = mytime.split( ":" ); 446 mytime_value = mytime_value[ 0 ] * 60 + parseInt( mytime_value[ 1 ] ); 447 448//alert('My time:'+ mytime_value + ' List of times: '+ times_in_day + ' Saved indexes: ' + start_time_checking_index + ' Days: ' + sort_date_array ) ; 449 450 var start_i = 0; 451 if ( start_time_checking_index != undefined ) 452 if ( start_time_checking_index[ 0 ] != undefined ) 453 if ( (!is_start_time) && (sort_date_array.length == 1) ){ 454 start_i = start_time_checking_index[ 0 ]; 455 /*start_i++;*/ 456 } 457 i = start_i; 458 459 // Main checking inside a day 460 for ( i = start_i; i < times_in_day.length; i++ ){ 461 times_in_day[ i ] = parseInt( times_in_day[ i ] ); 462 mytime_value = parseInt( mytime_value ); 463 if ( is_start_time ){ 464 if ( mytime_value > times_in_day[ i ] ){ 465 // Its Ok, lets Loop to next item 466 } else if ( mytime_value == times_in_day[ i ] ){ 467 if ( times_in_day_interval_marks[ i ] == 1 ){ 468 return false; //start time is begin with some other interval 469 } else { 470 if ( (i + 1) <= (times_in_day.length - 1) ){ 471 if ( times_in_day[ i + 1 ] <= mytime_value ) return false; //start time is begin with next elemnt interval 472 else { // start time from end of some other 473 if ( sort_date_array.length > 1 ) 474 if ( (i + 1) <= (times_in_day.length - 1) ) return false; // Its mean that we make end booking at some other day then this and we have some booking time at this day after start booking - its wrong 475 start_time_checking_index = [ i, td_class, mytime_value ]; 476 return true; 477 } 478 } 479 if ( sort_date_array.length > 1 ) 480 if ( (i + 1) <= (times_in_day.length - 1) ) return false; // Its mean that we make end booking at some other day then this and we have some booking time at this day after start booking - its wrong 481 start_time_checking_index = [ i, td_class, mytime_value ]; 482 return true; // start time from end of some other 483 } 484 } else if ( mytime_value < times_in_day[ i ] ){ 485 if ( times_in_day_interval_marks[ i ] == 2 ){ 486 return false; // start time inside of some interval 487 } else { 488 if ( sort_date_array.length > 1 ) 489 if ( (i + 1) <= (times_in_day.length - 1) ) return false; // Its mean that we make end booking at some other day then this and we have some booking time at this day after start booking - its wrong 490 start_time_checking_index = [ i, td_class, mytime_value ]; 491 return true; 492 } 493 } 494 } else { 495 if ( sort_date_array.length == 1 ){ 496 497 if ( start_time_checking_index != undefined ) 498 if ( start_time_checking_index[ 2 ] != undefined ) 499 500 if ( (start_time_checking_index[ 2 ] == times_in_day[ i ]) && (times_in_day_interval_marks[ i ] == 2) ){ // Good, because start time = end of some other interval and we need to get next interval for current end time. 501 } else if ( times_in_day[ i ] < mytime_value ) return false; // some interval begins before end of curent "end time" 502 else { 503 if ( start_time_checking_index[ 2 ] >= mytime_value ) return false; // we are select only one day and end time is earlythe starttime its wrong 504 return true; // if we selected only one day so evrything is fine and end time no inside some other intervals 505 } 506 } else { 507 if ( times_in_day[ i ] < mytime_value ) return false; // Some other interval start before we make end time in the booking at the end day selection 508 else return true; 509 } 510 511 } 512 } 513 514 if ( is_start_time ) start_time_checking_index = [ i, td_class, mytime_value ]; 515 else { 516 if ( start_time_checking_index != undefined ) 517 if ( start_time_checking_index[ 2 ] != undefined ) 518 if ( (sort_date_array.length == 1) && (start_time_checking_index[ 2 ] >= mytime_value) ) return false; // we are select only one day and end time is earlythe starttime its wrong 519 } 520 return true; 521 } 522 523 524 //PS: This function from ../booking/inc/js/personal.js 525 function isValidTimeTextField( timeStr ){
526 // Checks if time is in HH:MM AM/PM format. 527 // The seconds and AM/PM are optional. 528 529 var timePat = /^(\d{1,2}):(\d{2})(\s?(AM|am|PM|pm))?$/; 530 531 var matchArray = timeStr.match( timePat ); 532 if ( matchArray == null ){ 533 return false; //("<?php _e('Time is not in a valid format. Use this format HH:MM or HH:MM AM/PM'); ?>"); 534 } 535 var hour = matchArray[ 1 ]; 536 var minute = matchArray[ 2 ]; 537 var ampm = matchArray[ 4 ]; 538 539 if ( ampm == "" ){ 540 ampm = null 541 } 542 543 if ( hour < 0 || hour > 24 ){ //FixIn: 8.3.1.1 544 return false; //("<?php _e('Hour must be between 1 and 12. (or 0 and 23 for military time)'); ?>"); 545 } 546 if ( hour > 12 && ampm != null ){ 547 return false; //("<?php _e('You can not specify AM or PM for military time.'); ?>"); 548 } 549 if ( minute < 0 || minute > 59 ){ 550 return false; //("<?php _e('Minute must be between 0 and 59.'); ?>"); 551 } 552 return true; 553 }
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.