1/****************************************************************************************//** 2 \file table_display.js * 3 \brief Javascript functions for the basic table display. * 4 * \ 5 * 6 This file contains a function/object that implements the Fast Table Shortcode. Upon * 7 instantiation, it uses the page DOM to create a tabular display of meetings that are * 8 returned by a focused semantic search. The meetings are segregated by weekday, and only * 9 one weekday is populated at a time. Clicking on another weekday will start an AJAX * 10 query to the server, fetching the meetings for that weekday (and only that weekday). * 11 Search results are cached. * 12 * 13 The displayed meetings can be sorted by clicking on the column headers (with the sort * 14 direction being changed by repeated clicks). Changing the sort flushes all cached data. * 15 * 16 This class was designed to be highly self-contained. It can be instantiated multiple * 17 times per page, with each instance reflecting a different set of settings from the * 18 BMLT Administration setup (Theme, Week Start, Time Format and Root Server). The basic * 19 search parameters are passed in upon instantiation. These define the dataset to be * 20 fetched and displayed. The dataset is retrieved through a standard JSON semantic query * 21 to the instance's Root Server. * 22 * 23 The weekday tabs tend to be the "hubs" of the functionality. JSON search results are * 24 attached to the weekday tabs, and this data is used to create the weekday search * 25 display. The formats JSON is held by the main class, as that is used throughout the * 26 class. Otherwise, we try to encapsulate data and functionality as much as possible. * 27 * 28 This should only be used on Root Server version 2.7.8 or above, because the town sort * 29 will not work properly on older versions (but this will generally work, anyway). * 30 * 31 SOME NOTES ON CONVENTIONS: * 32 You will notice an abundance of semicolons. This is because this file is passed through * 33 an optimizer that crunches the entire file into one line. In this case, semicolons are * 34 absolutely vital. * 35 * 36 We use a lot of completion block functions, which is fairly standard for JavaScript. * 37 These are for things like mouseovers and click handlers. * 38 * 39 We use ul elements, as opposed to table elements, in order to maximize the potential * 40 for CSS modification of the displayed results (such as for responsive design). * 41 * 42 This file is part of the BMLT Common Satellite Base Class Project. The project GitHub * 43 page is available here: https://github.com/MAGSHARE/BMLT-Common-CMS-Plugin-Class * 44 * 45 This file is part of the Basic Meeting List Toolbox (BMLT). * 46 * 47 Find out more at: https://bmlt.app * 48 * 49 BMLT is free software: you can redistribute it and/or modify * 50 it under the terms of the GNU General Public License as published by *
51 the Free Software Foundation, either version 3 of the License, or * 52 (at your option) any later version. * 53 * 54 BMLT is distributed in the hope that it will be useful, * 55 but WITHOUT ANY WARRANTY; without even the implied warranty of * 56 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 57 GNU General Public License for more details. * 58 * 59 You should have received a copy of the GNU General Public License * 60 along with this code. If not, see <http://www.gnu.org/licenses/>. * 61 ********************************************************************************************/ 62 63/****************************************************************************************//** 64 \brief This class function implements the [[bmlt_table]] shortcode. Most of the shortcode 65 functionality is JavaScript. This function builds a new DOM tree that contains 66 a <div> element that will, in turn, contain a couple of lists that are warped 67 using CSS into a semblance of a table. 68 \param in_display_id The element DOM ID of the container div. 69 \param in_settings_id The ID of the settings used for this table. 70 \param in_theme_name The name of the styling theme. 71 \param in_ajax_base_uri The base URI for AJAX callbacks. 72 \param in_start_weekday The weekday that starts the week. 0 is Sunday, 6 is Saturday. 73 \param in_is_military_time True, if the time is to be displayed as military. 74 \param in_search_params The query parameters for the base search. 75 ********************************************************************************************/ 76function TableSearchDisplay( in_display_id, in_settings_id, in_theme_name, in_ajax_base_uri, in_start_weekday, in_is_military_time, in_search_params ) 77{ 78 /**************************************************************************************** 79 * INSTANCE DATA MEMBERS * 80 ****************************************************************************************/ 81 82 // These are the parameters passed in. 83 var my_settings_id; ///\var my_settings_id This is the unique ID of the BMLT settings that we use to reference default values. 84 var my_ajax_base_uri; ///\var my_ajax_base_uri This is the base URI for our AJAX calls. 85 var my_theme_name; ///\var my_theme_name This is the name of our styling theme. 86 var my_search_query_params; ///\var my_search_query_params These are the search parameters that we use to populate the search. 87 var my_start_weekday; ///\var my_start_weekday The day of week that starts the header (0 is Sunday, 6 is Saturday). 88 var my_military_time; ///\var my_military_time True, if the start time is military. 89 90 // These are DOM elements that we track. 91 var my_container_object; ///\var my_container_object This is the div block that surrounds this table. 92 var my_header_container; ///\var my_header_container The div element that will contain the weekday tabs. 93 var my_body_container; ///\var my_body_container The table element that will contain the search results. 94 var my_body_container_header; ///\var my_body_container_header The table thead element that will contain the search results. 95 var my_body_container_body; ///\var my_body_container_body The table tbody element that will contain the search results. 96 var my_weekday_links; ///\var my_weekday_links An array of li elements, containing the weekday tabs 97 var my_selected_tab; ///\var my_selected_tab This will be the selected tab object. 98 99 var my_selected_sort_key; ///\var my_selected_sort_key The currently selected sort key. 100 var my_sort_dir; ///\var my_sort_dir The currently selected sort direction. 101 102 var my_format_data; ///\var my_format_data This is the JSON object that contains the format data for the search. It is loaded at startup, and not changed afterwards. 103 104 // These are basically constants. They are the sort keys. 105 var my_sort_key_time; ///\var my_sort_key_time The sort keys for sorting by start time. 106 var my_sort_key_meeting_name; ///\var my_sort_key_meeting_name The sort keys for sorting by meeting name. 107 var my_sort_key_town; ///\var my_sort_key_town The sort keys for sorting by borough/town. 108 var my_sort_key_address; ///\var my_sort_key_address The sort keys for sorting by street address/location name. 109 110 this.my_sort_key_time = 'start_time,location_nation,location_province,location_sub_province,location_city_subsection,location_municipality,location_neighborhood,meeting_name'; 111 this.my_sort_key_meeting_name = 'meeting_name,start_time,location_city_subsection,location_municipality'; 112 this.my_sort_key_town = 'location_city_subsection,location_municipality,start_time,meeting_name'; 113 this.my_sort_key_address = 'location_text,location_street,location_city_subsection,location_municipality,start_time,meeting_name'; 114 115 /**************************************************************************************** 116 *################################### MAIN FUNCTION CODE ################################* 117 ****************************************************************************************/ 118 119 // Start off by getting our container. 120 this.my_container_object = document.getElementById(in_display_id); 121 this.my_container_object.handlerObject = this; // Attach a context object (this function instance). 122 123 // Set up the parameters passed in. 124 this.my_settings_id = in_settings_id; 125 this.my_ajax_base_uri = in_ajax_base_uri; 126 this.my_theme_name = in_theme_name; 127 this.my_start_weekday = in_start_weekday; 128 this.my_search_query_params = in_search_params; 129 this.my_military_time = in_is_military_time; 130 131 // Initial values. 132 this.my_selected_sort_key = this.my_sort_key_time; 133 this.my_sort_dir = 'asc'; 134 135 // This makes the elements visible (This means that non-JS browsers see nothing but the <noscript> element). 136 this.my_container_object.style.display = 'block'; 137 138 // Fire off the first shot -get the formats, which will cascade to the first search. 139 this.domBuilder_CreateHeader(); 140}; 141 142/**************************************************************************************** 143 * ############################### CLASS PROTOTYPE FUNCTIONS #############################* 144 ****************************************************************************************/ 145 146/**************************************************************************************** 147 * ############################### COMMUNICATIONS ROUTINES ###############################* 148 ****************************************************************************************/ 149/************************************************************************************//** 150 AJAX HANDLER * 151 ****************************************************************************************/ 152 153/************************************************************************************//** 154 \brief A simple, generic AJAX request function. 155 \param url The URI to be called. This should include all parameters in GET form (even if this is POST).
156 \param callback The success callback 157 \param method The method ('get', 'GET', 'post' or 'POST') 158 \param extra_data If supplied, extra data to be delivered to the callback. 159 \returns a new XMLHTTPRequest object. 160 ****************************************************************************************/ 161TableSearchDisplay.prototype.comms_AjaxRequest = function ( url, callback, method, extra_data ) { 162 /********************************************************************************//** 163 * \brief Create a generic XMLHTTPObject. 164 * This will account for the various flavors imposed by different browsers. 165 * \returns a new XMLHTTPRequest object. 166 ************************************************************************************/ 167 168 function createXMLHTTPObject() 169 { 170 // Various systems will produce one or more instances of these classes. 171 var XMLHttpArray = [ 172 function () { 173 return new XMLHttpRequest()}, // Your average browser. 174 function () { 175 return new ActiveXObject("Msxml2.XMLHTTP")}, // These are for Microsoft browsers. 176 function () { 177 return new ActiveXObject("Msxml2.XMLHTTP")}, 178 function () { 179 return new ActiveXObject("Microsoft.XMLHTTP")} 180 ]; 181 182 var xmlhttp = false; 183 184 // Go through the array until you hit a dispatcher that was properly instantiated. 185 // Once you find it, make that our dispatcher. 186 for ( var i=0; i < XMLHttpArray.length; i++ ) 187 { 188 try 189 { 190 xmlhttp = XMLHttpArray[i](); 191 } 192 catch(e) 193 { 194 continue; 195 }; 196 break; 197 }; 198 199 return xmlhttp; 200 }; 201 202 // Set up a dispatcher. 203 var req = createXMLHTTPObject(); 204 req.finalCallback = callback; // Point to the given callback. 205 var sVars = null; 206 method = method.toString().toUpperCase(); // Clean the input method to all uppercase. 207 208 // Split the URL up, if this is a POST. 209 if (method == "POST" ) { 210 var rmatch = /^([^\?]*)\?(.*)$/.exec(url); 211 url = rmatch[1]; 212 sVars = rmatch[2]; 213 // This horrible, horrible kludge, is because Drupal insists on having its q parameter in the GET list only. 214 var rmatch_kludge = /(q=admin\/settings\/bmlt)&?(.*)/.exec(rmatch[2]); 215 if (rmatch_kludge && rmatch_kludge[1] ) { 216 url += '?'+rmatch_kludge[1]; 217 sVars = rmatch_kludge[2]; 218 }; 219 }; 220 221 // If we are passing some extra data to the callback, we do so here. 222 if (extra_data ) { 223 req.extra_data = extra_data; 224 }; 225 226 // Create the HTTPRequest 227 req.open(method, url, true); 228 229 // In the case of POST, our data is separate from the URI. 230 if (method == "POST" ) { 231 req.setRequestHeader("Method", "POST "+url+" HTTP/1.1"); 232 req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); 233 }; 234 235 // Set the function to be called as the request does its thing. 236 req.onreadystatechange = function ( ) { 237 if (req.readyState != 4 ) { return; // We only care about the end. 238 } 239 if(req.status != 200 ) { return; // If we failed, we drop out. 240 } 241 callback(req, req.extra_data); // If we made it this far, we call our callback. 242 req = null; // Kill the request. 243 }; 244 245 req.send(sVars); // Throw the switch, Igor! 246 247 return req; 248}; 249 250/************************************************************************************//** 251 \brief Called to load all the format objects for the search. 252 This will give us the "used formats" for the given filtered search. This will 253 request a JSON response from the server. It does not use switcher=GetFormats. 254 Instead, it requests the formats only rom switcher=GetSearchResults. This will 255 allow the number of formats to be limited to only those used for the search results. 256 This uses the filter established by the query parameters passed in on instantiation. 257 This function initiates an AJAX call that will return and call the class function 258 comms_callback_loadloadFormatData. 259 ****************************************************************************************/ 260TableSearchDisplay.prototype.comms_loadFormatData = function () { 261 // Since loading the formats happens, regardless of the weekday, we set all the spinners going. 262 for ( var i = 0; i < 7; i++ ) 263 { 264 var tab_object = this.my_weekday_links[i]; 265 tab_object.className = 'bmlt_table_header_weekday_list_element is_loading'; 266 }; 267 268 // Ask for the formats used by the search set (not every format in the database). 269 // Tell the formats to come alone, bearing small, unmarked bills. 270 var uri = encodeURI(this.my_search_query_params + '&bmlt_settings_id=' + this.my_settings_id + '&get_formats_only=1').toString(); 271 uri = this.my_ajax_base_uri + encodeURI('switcher=GetSearchResults&') + ur
271i; 272 this.m_ajax_request = this.comms_AjaxRequest(uri, this.comms_callback_loadloadFormatData, 'get', this); 273}; 274 275/************************************************************************************//** 276 \brief Called to load a weekday. 277 This is called when the tab doesn't yet have a cache. It requests a JSON response 278 from the Root server for switcher=GetSearchResults. 279 This uses the filter established by the query parameters passed in on instantiation. 280 This function initiates an AJAX call that will return and call the class function 281 comms_callback_loadloadWeekdayData. 282 \param in_tab_object The tab DOM object. 283 ****************************************************************************************/ 284TableSearchDisplay.prototype.comms_loadWeekdayData = function ( in_tab_object ) { 285 // We append the weekday, sort and sort order parameters to the URI. 286 var uri = encodeURI(this.my_search_query_params + '&sort_keys=' + this.my_selected_sort_key + ((this.my_sort_dir == 'desc') ? ',desc' : '') +'&bmlt_settings_id=' + this.my_settings_id + '&weekdays[]=' + (parseInt(in_tab_object.index) + 1).toString()).toString(); 287 uri = this.my_ajax_base_uri + encodeURI('switcher=GetSearchResults&') + uri; 288 this.m_ajax_request = this.comms_AjaxRequest(uri, this.comms_callback_loadloadWeekdayData, 'get', in_tab_object); 289}; 290 291/**************************************************************************************** 292 * ##################################### AJAX CALLBACKS ##################################* 293 ****************************************************************************************/ 294 295/************************************************************************************//** 296 \brief Called when the format data is loaded. 297 This loads the function object with a cache of the retrieved JSON data. This 298 data is loaded once, then used to construct the format objects in the table. 299 This is called once, at the start. After the format data has been loaded, the 300 callback then initiates a new search for the current weekday. 301 \param in_response_object The HTTPRequest object for this AJAX call. 302 \param in_context_object The TableSearchDisplay object that initiated the call. 303 ****************************************************************************************/ 304TableSearchDisplay.prototype.comms_callback_loadloadFormatData = function ( in_response_object, in_context_object ) { 305 for ( var i = 0; i < 7; i++ ) 306 { 307 var tab_object = in_context_object.my_weekday_links[i]; 308 tab_object.className = 'bmlt_table_header_weekday_list_element'; 309 }; 310 311 eval("var format_data = " + in_response_object.responseText + ";"); // Extract the JSON object with the returned data. 312 in_context_object.my_format_data = format_data.formats; 313 314 // Now that we have the formats, get the meeting list for today. 315 var d = new Date(); 316 in_context_object.my_header_container.selectTab(d.getDay()); 317}; 318 319/************************************************************************************//** 320 \brief Called when the weekday data is loaded. 321 This loads the tab object with a cache of the retrieved JSON data, and has 322 the tab object select itself. 323 \param in_response_object The HTTPRequest object for this AJAX call. 324 \param in_tab_object The Tab object that denotes the chosen weekday. 325 ****************************************************************************************/ 326TableSearchDisplay.prototype.comms_callback_loadloadWeekdayData = function ( in_response_object, in_tab_object ) { 327 eval("var json_response = " + in_response_object.responseText + ";"); // Extract the JSON object with the returned data. 328 in_tab_object.weekday_json_data = json_response; 329 in_tab_object.select(); 330}; 331 332/**************************************************************************************** 333 * #################################### UTILITY ROUTINES #################################* 334 ****************************************************************************************/ 335 336/************************************************************************************//** 337 \brief This turns military time to ante meridian format or cleaned military time. 338 If the time is midnight or noon, the localized string for that is returned. 339 \param in_time_string A string with the time in military format. 340 \returns A string, with the properly formatted time ("Noon", "Midnight", "HH:MM" or "HH:MM AM|PM"). 341 ****************************************************************************************/ 342TableSearchDisplay.prototype.utility_convertTime = function ( in_time_string ) { 343 var ret = in_time_string; 344 var time_array = in_time_string.split(":"); 345 var hours = parseInt(time_array[0]); 346 var minutes = parseInt(time_array[1]); 347 348 // We use "Noon" and "Midnight" strings for those times. 349 // Kludge, to make "Midnight" late, as the computer wants it to be early. 350 // Anything over 23:54:59 is "Midnight." 351 if (((0 == hours) && (0 == minutes)) || ((hours == 23) && (minutes >= 55)) ) { 352 ret = g_table_ampm_array[3]; // Midnight 353 } 354 else if ((12 == hours) && (0 == minutes) ) { 355 ret = g_table_ampm_array[2]; // Noon 356 } 357 else if (!this.my_military_time ) { 358 var ampm = ( hours >= 12 ) ? g_table_ampm_array[1] : g_table_ampm_array[0]; 359 360 // Subtract 12 from afternoon. 361 if (hours > 12 ) { 362 hours -= 12; 363 }; 364 365 // Padding with a leading 0. 366 if (minutes < 10 ) { 367 minutes = "0" + minutes.toString(); 368 }; 369 370 ret = hours.toString() + ':' + minutes + ' ' + ampm; 371 } 372 else 373 { 374 // Padding with a leading 0. 375 if (minutes < 10 ) { 376 minutes = "0" + minutes.toString(); 377 }; 378 379 380 ret = hours.toString() + ':' + minutes.toString(); 381 }; 382 383 return ret; 384}; 385 386/************************************************************************************//** 387 \brief This builds a readable aggregated street address from the components in the JSON. 388 Very simply, we just do the location name and street address. 389 \param in_json_data The JSON data for one meeting. 390 \returns a string, with the address HTML. 391 ****************************************************************************************/ 392TableSearchDisplay.prototype.utility_createStreetAddress = function ( in_json_data ) { 393 var ret = ''; 394 395 if (in_json_data.location_text && in_json_data.location_text.toString() ) { 396 ret = '<span class="bmlt_table_location_text_span">'; // This allows folks to style the location name. 397 ret += in_json_data.location_text.toString(); 398 if ((ret != '') && in_json_data.location_street && in_json_data.location_street.toString() ) { 399 ret += '<span class="bmlt_table_location_text_comma_span">, </span>'; 400 }; 401 ret += '</span>'; 402 }; 403 404 if (in_json_data.location_street && in_json_data.location_street.toString() ) { 405 ret += '<span class="bmlt_table_location_street_span">'; // This allows folks to style the street. 406 ret += in_json_data.location_street.toString(); 407 ret += '</span>'; 408 }; 409 410 return ret; 411}; 412 413/************************************************************************************//** 414 \brief This builds an aggregated town from the components in the JSON. 415 \param in_json_data The JSON data for one meeting. 416 \returns a string, with the town HTML. 417 ****************************************************************************************/ 418TableSearchDisplay.prototype.utility_createAddressTown = function ( in_json_data ) { 419 var hasTown = false;
420 var ret = '<span class="bmlt_table_town_span">'; // This allows folks to style the town. 421 422 // We prefer boroughs, as folks think of them as "towns." 423 if (in_json_data.location_city_subsection && in_json_data.location_city_subsection.toString() ) { 424 ret += in_json_data.location_city_subsection.toString(); 425 hasTown = true; 426 } 427 else 428 { 429 if (in_json_data.location_municipality && in_json_data.location_municipality.toString() ) { 430 ret += in_json_data.location_municipality.toString(); 431 hasTown = true; 432 }; 433 }; 434 435 if (hasTown && in_json_data.location_province && in_json_data.location_province.toString() ) { 436 ret += '<span class="bmlt_table_town_comma_span">, </span>'; 437 }; 438 439 ret += '</span>'; 440 441 // Add the state. 442 if (in_json_data.location_province && in_json_data.location_province.toString() ) { 443 ret += '<span class="bmlt_table_state_span">'; // This allows folks to style the state. 444 ret += in_json_data.location_province.toString(); 445 ret += '</span>'; 446 }; 447 448 return ret; 449}; 450 451/**************************************************************************************** 452 * #################################### THIRD-PARTY CODE #################################* 453 ****************************************************************************************/ 454/** 455 \param in_pattern The printf pattern to use 456 \param ... any variables needed to satisfy the pattern. 457sprintf() for JavaScript 0.6 458 459Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com> 460All rights reserved. 461 462Redistribution and use in source and binary forms, with or without 463modification, are permitted provided that the following conditions are met: 464 * Redistributions of source code must retain the above copyright 465 notice, this list of conditions and the following disclaimer. 466 * Redistributions in binary form must reproduce the above copyright 467 notice, this list of conditions and the following disclaimer in the 468 documentation and/or other materials provided with the distribution. 469 * Neither the name of sprintf() for JavaScript nor the 470 names of its contributors may be used to endorse or promote products 471 derived from this software without specific prior written permission. 472 473THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 474ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 475WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 476DISCLAIMED. IN NO EVENT SHALL Alexandru Marasteanu BE LIABLE FOR ANY 477DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 478(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 479LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 480ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 481(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 482SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 483 484 485Changelog: 4862007.04.03 - 0.1: 487 - initial release 4882007.09.11 - 0.2: 489 - feature: added argument swapping 4902007.09.17 - 0.3: 491 - bug fix: no longer throws exception on empty paramenters (Hans Pufal) 4922007.10.21 - 0.4: 493 - unit test and patch (David Baird) 4942010.05.09 - 0.5: 495 - bug fix: 0 is now preceeded with a + sign 496 - bug fix: the sign was not at the right position on padded results (Kamal Abdali) 497 - switched from GPL to BSD license 4982010.05.22 - 0.6: 499 - reverted to 0.4 and fixed the bug regarding the sign of the number 0 500 Note: 501 Thanks to Raphael Pigulla <raph (at] n3rd [dot) org> (http://www.n3rd.org/) 502 who warned me about a bug in 0.5, I discovered that the last update was 503 a regress. I appologize for that. 504 */ 505TableSearchDisplay.prototype.utility_sprintf = function () { 506 function str_repeat( i, 507 m 508 ) { 509 for (var o = []; m > 0; o[--m] = i); 510 return o.join(''); 511 }; 512 513 var i = 0, a, f = arguments[i++], o = [], m, p, c, x, s = ''; 514 515 while (f) 516 { 517 if (m = /^[^\x25]+/.exec(f)) { 518 o.push(m[0]); 519 } 520 else if (m = /^\x25{2}/.exec(f)) { 521 o.push('%'); 522 } 523 else if (m = /^\x25(?:(\d+)\$)?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(f)) { 524 if (((a = arguments[m[1] || i++]) == null) || (a == undefined)) { 525 throw('Too few arguments.'); 526 }; 527 528 if (/[^s]/.test(m[7]) && (typeof(a) != 'number')) { 529 throw('Expecting number but found ' + typeof(a)); 530 }; 531 532 switch (m[7]) 533 { 534 case 'b': a = a.toString(2); break; 535 case 'c': a = String.fromCharCode(a); break; 536 case 'd': a = parseInt(a,10); break; 537 case 'e': a = m[6] ? a.toExponential(m[6]) : a.toExponential(); break; 538 case 'f': a = m[6] ? parseFloat(a).toFixed(m[6]) : parseFloat(a); break; 539 case 'o': a = a.toString(8); break; 540 case 's': a = ((a = String(a)) && m[6] ? a.substring(0, m[6]) : a); break; 541 case 'u': a = Math.abs(a); break; 542 case 'x': a = a.toString(16); break; 543 case 'X': a = a.toString(16).toUpperCase(); break; 544 }; 545 546 a = (/[def]/.test(m[7]) && m[2] && a >= 0 ? '+' + a : a);
547 c = m[3] ? m[3] == '0' ? '0' : m[3].charAt(1) : ' '; 548 x = m[5] - String(a).length - s.length; 549 p = m[5] ? str_repeat(c, x) : ''; 550 o.push(s + (m[4] ? a + p : p + a)); 551 } 552 else 553 { 554 throw('Huh ?!'); 555 }; 556 557 f = f.substring(m[0].length); 558 }; 559 560 return o.join(''); 561}; 562 563/**************************************************************************************** 564 * ################################## DOM BUILDER ROUTINES ###############################* 565 * **************************************************************************************** 566 These routines all construct the DOM tree for the shortcode. The weekday header is 567 displayed all the time (with the selected tab changing), but the table data (and the 568 header) is reconstructed from scratch each time. 569 *************************************************************************************//** 570 \brief Creates the weekday selection header. 571 \returns the instantiated DOM object. 572 ****************************************************************************************/ 573TableSearchDisplay.prototype.domBuilder_CreateHeader = function () { 574 this.my_weekday_links = new Array(); 575 this.my_header_container = document.createElement('ul'); // Create the header container. 576 this.my_header_container.handler = this; 577 this.my_header_container.className = 'bmlt_table_header_weekday_list'; 578 579 this.my_container_object.appendChild(this.my_header_container); 580 581 var startingIndex = this.my_start_weekday; // Allows us to start from different weekdays. 582 583 // Build the list of weekday tab objects. 584 for ( var i = 0; i < 7; i++ ) 585 { 586 this.my_weekday_links[i] = this.domBuilder_CreateOneWeekday(startingIndex++); 587 if (startingIndex == 7 ) { 588 startingIndex = 0; 589 }; 590 591 this.my_header_container.appendChild(this.my_weekday_links[i]); 592 }; 593 594 // This inline function tells the header to select the indexed (0-based) tab. 595 this.my_header_container.selectTab = function ( in_weekday ///< The day of the week (0 is Sunday, 6 is Saturday) 596 ) { 597 for ( var i = 0; i < 7; i++ ) 598 { 599 if (this.handler && this.handler.my_weekday_links && this.handler.my_weekday_links[i] ) { 600 // If this is the day we are looking at, we select it. 601 if (this.handler.my_weekday_links[i].index == in_weekday ) { 602 this.handler.my_weekday_links[i].select(); 603 }; 604 }; 605 }; 606 }; 607 608 this.comms_loadFormatData(); 609}; 610 611/************************************************************************************//** 612 \brief Creates one weekday button for the header. 613 \param in_weekday_index The index of the weekday to be created. 0 is Sunday, 6 is Saturday. 614 \returns the instantiated DOM object. 615 ****************************************************************************************/ 616TableSearchDisplay.prototype.domBuilder_CreateOneWeekday = function ( in_weekday_index ) { 617 // First set up the weekday tab element. It will anchor the context for the data. 618 var weekdayElement = document.createElement('li'); // Create the basic list element. 619 weekdayElement.handler = this; // We will do this a lot. It allows anyone to grab the object context from the element. 620 weekdayElement.index = in_weekday_index; // This is the 0-based weekday index. 621 weekdayElement.rest_className = 'bmlt_table_header_weekday_list_element'; 622 weekdayElement.className = weekdayElement.rest_className; 623 624 // Today is a great day! 625 var d = new Date(); 626 if (d.getDay() == in_weekday_index ) { 627 weekdayElement.rest_className += ' is_today'; 628 }; 629 630 // If we click, we select. 631 weekdayElement.onclick = function () { 632 this.select()}; 633 634 // This inline function will force a reload of the search for the given weekday. 635 weekdayElement.reload = function () { 636 if (this.className != (this.rest_className + ' is_loading') ) { 637 if (this.handler.my_body_container ) // Clear any preexisting condition. 638 { 639 this.handler.my_body_container.parentNode.removeChild(this.handler.my_body_container); 640 this.handler.my_body_container.innerHTML = ''; 641 this.handler.my_body_container = null; 642 }; 643 644 this.weekday_json_data = null; 645 this.className = this.rest_className + ' is_loading'; 646 647 // Set a title, saying what is happening. 648 this.setAttribute('title', this.handler.utility_sprintf(g_table_header_tab_loading_format, g_table_weekday_long_name_array[this.index])); 649
650 this.handler.comms_loadWeekdayData(this); 651 }; 652 }; 653 654 // This inline function will read the data from the server if it is not already cached. 655 // If the tab has cached data, then it deselects all the other tabs, selects itself, and populates the main area with the data in its cache. 656 weekdayElement.select = function () { 657 if (!this.weekday_json_data ) { 658 this.reload(); 659 } 660 else 661 { 662 if (this.handler.my_body_container ) // Clear any preexisting condition. 663 { 664 this.handler.my_body_container.parentNode.removeChild(this.handler.my_body_container); 665 this.handler.my_body_container.innerHTML = ''; 666 this.handler.my_body_container = null; 667 }; 668 669 for ( var i = 0; i < 7; i++ ) 670 { 671 if (this.handler && this.handler.my_weekday_links && this.handler.my_weekday_links[i] ) { 672 var tabObject = this.handler.my_weekday_links[i]; 673 // If the indexed object is not ours, then we deselect it. 674 if (tabObject.index != this.index ) { 675 // This is not selected or loading. 676 tabObject.className = tabObject.rest_className; 677 // Set a title, saying what will happen when this is clicked. 678 tabObject.setAttribute('title', this.handler.utility_sprintf(g_table_header_tab_format, g_table_weekday_long_name_array[tabObject.index])); 679 }; 680 }; 681 }; 682 683 this.handler.my_selected_tab = this; 684 this.sort_key = this.handler.my_selected_sort_key; 685 this.sort_dir = this.handler.my_sort_dir; 686 this.handler.domBuilder_PopulateWeekday(this.weekday_json_data, this.index, this.sort_key, this.sort_dir, this); 687 }; 688 }; 689 690 // This will be the span element that holds the weekday name. 691 var textNode = document.createElement('span'); 692 textNode.appendChild(document.createTextNode(g_table_weekday_name_array[in_weekday_index])); 693 textNode.rest_className = 'bmlt_table_weekday_span'; 694 textNode.className = textNode.rest_className; 695 696 // We add these functions to handle changing the class when the cursor moves over the tab. 697 textNode.onmouseover = function () { 698 if (this.parentNode.className != (this.parentNode.rest_className + ' is_loading') ) { 699 if (this.parentNode.handler.my_selected_tab != this.parentNode ) { 700 this.parentNode.className = this.parentNode.rest_className + ' bmlt_table_mouseover'; 701 }; 702 }; 703 }; 704 705 // We restore the class when we mouse out. However, we ignore it if we are loading. 706 textNode.onmouseout = function () { 707 if (!this.parentNode.className.match(/is_loading/) ) { 708 this.parentNode.className = this.parentNode.rest_className; 709 if (this.parentNode.handler.my_selected_tab == this.parentNode ) { 710 this.parentNode.className += ' is_selected'; 711 }; 712 }; 713 }; 714 715 weekdayElement.appendChild(textNode); 716 717 // This is the throbber image. It will be hidden unless the weekday is loading. 718 var throbberImage = document.createElement('img'); 719 // We replace the "default" with our theme. 720 var regex = /themes\x2Fdefault\x2F/; 721 throbberImage.src = g_table_throbber_img_src.replace(regex, 'themes/' + this.my_theme_name.toString() + '/'); 722 throbberImage.className = 'bmlt_table_throbber_image'; 723 weekdayElement.appendChild(throbberImage); 724 725 return weekdayElement; 726}; 727 728/************************************************************************************//** 729 \brief Populates the meeting result table for the given data. 730 \param in_search_results_json A JSON object with the meeting search results. 731 \param in_index The 0-based weekday index. 732 \param in_sort_key The sort key, for the data sort. 733 \param in_sort_dir The sort direction, for the data sort. It will be 'asc' or 'desc'. 734 \param in_tabObject The tab object, which will be set to selected when the weekday is done. 735 \returns the instantiated DOM object. 736 ****************************************************************************************/ 737TableSearchDisplay.prototype.domBuilder_PopulateWeekday = function ( in_search_results_json, in_index, in_sort_key, in_sort_dir, in_tabObject ) { 738 if (null != this.my_body_container ) // Clear any preexisting condition. 739 { 740 this.my_body_container.parentNode.removeChild(this.my_body_container); 741 this.my_body_container.innerHTML = ''; 742 this.my_body_container = null; 743 }; 744 745 this.my_body_container = document.createElement('div'); // Create the main display container element. 746 this.my_body_container.rest_className = 'bmlt_table_div'; 747 this.my_body_container.rest_className += (' bmlt_table_' + ((in_sort_dir == 'asc') ? 'sort_asc' : 'sort_desc') + '_div'); 748 this.my_body_container.rest_className += (' bmlt_table_sort_' + in_sort_key.replace(/,/g, '_') + '_div'); 749 this.my_body_container.className = this.my_body_container.rest_className; 750 this.my_body_container_header = null; 751 this.my_body_container_body = null; 752 753 // Attach all the various DOM elements. 754 this.my_container_object.appendChild(this.my_body_container); 755 this.my_body_container.appendChild(this.domBuilder_PopulateWeekdayHeader(in_index, in_sort_key, in_sort_dir)); 756 this.my_body_container.appendChild(this.domBuilder_PopulateWeekdayBody(in_search_results_json)); 757 758 var div = document.createElement('div'); 759 div.className = 'bmlt_breaker_div'; 760 this.my_body_container.appendChild(div); 761 762 in_tabObject.className = in_tabObject.rest_className + ' is_selected'; 763}; 764 765/************************************************************************************//** 766 \brief Populates the header for the meetings table. 767 \param in_index The 0-based weekday index. 768 \param in_sort_key The sort key, for the data sort. 769 \param in_sort_dir The sort direction, for the data sort. It will be 'asc' or 'desc'. 770 \returns the instantiated DOM object. 771 ****************************************************************************************/ 772TableSearchDisplay.prototype.domBuilder_PopulateWeekdayHeader = function ( in_index, in_sort_key, in_sort_dir ) { 773 if (null != this.my_body_container_header ) // Clear any preexisting condition. 774 { 775 this.my_body_container_header.parentNode.removeChild(this.my_body_container_header); 776 this.my_body_container_header.innerHTML = ''; 777 this.my_body_container_header = null; 778 }; 779 780 this.my_body_container_header = document.createElement('ul'); // Create the header element. 781 this.my_body_container_header.className = 'bmlt_table_header_ul'; 782 783 var theElement = this.domBuilder_PopulateWeekdayHeaderColumn(g_table_time_header_text, this.my_sort_key_time, ((in_sort_key == this.my_sort_key_time) ? in_sort_dir : 'asc'), in_sort_key == this.my_sort_key_time); 784 theElement.className = 'bmlt_table_header_li bmlt_table_header_li_time'; 785 this.my_body_container_header.appendChild(theElement); 786 787 theElement = this.domBuilder_PopulateWeekdayHeaderColumn(g_table_town_header_text, this.my_sort_key_town, ((in_
787sort_key == this.my_sort_key_town) ? in_sort_dir : 'asc'), in_sort_key == this.my_sort_key_town); 788 theElement.className = 'bmlt_table_header_li bmlt_table_header_li_town'; 789 this.my_body_container_header.appendChild(theElement); 790 791 theElement = this.domBuilder_PopulateWeekdayHeaderColumn(g_table_name_header_text, this.my_sort_key_meeting_name, ((in_sort_key == this.my_sort_key_meeting_name) ? in_sort_dir : 'asc'), in_sort_key == this.my_sort_key_meeting_name); 792 theElement.className = 'bmlt_table_header_li bmlt_table_header_li_name'; 793 this.my_body_container_header.appendChild(theElement); 794 795 theElement = this.domBuilder_PopulateWeekdayHeaderColumn(g_table_address_header_text, this.my_sort_key_address, ((in_sort_key == this.my_sort_key_address) ? in_sort_dir : 'asc'), in_sort_key == this.my_sort_key_address); 796 theElement.className = 'bmlt_table_header_li bmlt_table_header_li_address'; 797 this.my_body_container_header.appendChild(theElement); 798 799 theElement = this.domBuilder_PopulateWeekdayHeaderColumn(g_table_format_header_text, null, null, false); 800 theElement.className = 'bmlt_table_header_li bmlt_table_header_li_format'; 801 this.my_body_container_header.appendChild(theElement); 802 803 return this.my_body_container_header; 804}; 805 806/************************************************************************************//** 807 \brief Populates the header for the meetings table. 808 \param in_name The text name for the column. 809 \param in_sort_key The sort key, for the data sort. 810 \param in_sort_dir The sort direction, for the data sort. It will be 'asc' or 'desc'. 811 \param in_is_selected A boolean value. True, if this is the sort key. 812 \returns the instantiated DOM object. 813 ****************************************************************************************/ 814TableSearchDisplay.prototype.domBuilder_PopulateWeekdayHeaderColumn = function ( in_name, in_sort_key, in_sort_dir, in_is_selected ) { 815 var theElement = document.createElement('li'); // Create the column element 816 theElement.key = in_sort_key; // This will be the sort key for this column. 817 var textNode = document.createElement('span'); 818 textNode.appendChild(document.createTextNode(in_name)); 819 textNode.is_selected = false; 820 textNode.rest_className = 'bmlt_table_header_span'; 821 textNode.className = textNode.rest_className; 822 textNode.sort_dir = in_sort_dir; 823 textNode.sort_key = in_sort_key; 824 textNode.handler = this; 825 826 if (in_sort_key ) { 827 if (in_is_selected ) { 828 textNode.rest_className = 'bmlt_table_header bmlt_table_header_selected' + ' bmlt_table_header_selected_sort_direction_' + ((in_sort_dir == 'asc') ? 'asc' : 'desc'); 829 textNode.className = textNode.rest_className; 830 textNode.is_selected = true; 831 }; 832 833 textNode.onmouseover = function () { 834 this.className = this.rest_className + ' bmlt_table_mouseover'; 835 }; 836 837 textNode.onmouseout = function () { 838 this.className = this.rest_className; 839 }; 840 841 textNode.onclick = function () { 842 if (this.handler.my_selected_sort_key == this.sort_key ) { 843 this.sort_dir = ((this.sort_dir == 'asc') ? 'desc' : 'asc'); 844 }; 845 846 this.handler.my_selected_sort_key = this.sort_key; 847 this.handler.my_sort_dir = this.sort_dir; 848 for ( var i = 0; i < 7; i++ ) 849 { 850 this.handler.my_weekday_links[i].weekday_json_data = null; 851 }; 852 853 this.handler.my_selected_tab.reload(); 854 }; 855 } 856 else 857 { 858 textNode.rest_className = 'bmlt_header_formats_span'; 859 textNode.className = textNode.rest_className; 860 }; 861 862 theElement.appendChild(textNode); 863 864 return theElement; 865}; 866 867/************************************************************************************//** 868 \brief Populates the main table for the meetings table. 869 \param in_json_data The JSON data for all the meetings. 870 \returns the instantiated DOM object. 871 ****************************************************************************************/ 872TableSearchDisplay.prototype.domBuilder_PopulateWeekdayBody = function ( in_json_data ) { 873 if (null != this.my_body_container_body ) // Clear any preexisting condition. 874 { 875 this.my_body_container_body.parentNode.removeChild(this.my_body_container_body); 876 this.my_body_container_body.innerHTML = ''; 877 this.my_body_container_body = null; 878 }; 879 880 this.my_body_container_body = document.createElement('ul'); 881 this.my_body_container_body.className = 'bmlt_table_data_ul';
882 883 if (in_json_data.length ) { 884 for ( var i = 0; i < in_json_data.length; i++ ) 885 { 886 var listElement = document.createElement('li'); 887 listElement.className = 'bmlt_table_data_ul_li'; 888 listElement.className += (' bmlt_row' + ((i % 2) ? '_odd' : '_even')); 889 var meeting_json = in_json_data[i]; 890 var meeting_object = this.domBuilder_PopulateWeekdayBody_one_meeting(meeting_json); 891 listElement.appendChild(meeting_object); 892 this.my_body_container_body.appendChild(listElement); 893 }; 894 } 895 else 896 { 897 var listElement = document.createElement('li'); 898 listElement.className = 'bmlt_table_data_ul_no_meetings_li'; 899 listElement.innerHTML = '<span class="bmlt_table_no_meetings_span">' + this.utility_sprintf(g_table_no_meetings_format, g_table_weekday_long_name_array[this.my_selected_tab.index]) + '</span>'; 900 this.my_body_container_body.appendChild(listElement); 901 }; 902 903 return this.my_body_container_body; 904}; 905 906/************************************************************************************//** 907 \brief Populates one row (meeting) 908 \param in_json_data The JSON data for the meeting. 909 \returns the instantiated DOM object. 910 ****************************************************************************************/ 911TableSearchDisplay.prototype.domBuilder_PopulateWeekdayBody_one_meeting = function ( in_json_data ) { 912 var rowElement = document.createElement('ul'); 913 rowElement.className = 'bmlt_table_data_ul_li_ul'; 914 var comments = null; 915 916 if (in_json_data.comments ) { 917 comments = in_json_data.comments; 918 }; 919 if (in_json_data.location_info ) { 920 location_info = in_json_data.location_info; 921 }; 922 923 var latitude = in_json_data.latitude; 924 var longitude = in_json_data.longitude; 925 926 var time_column = this.domBuilder_PopulateWeekdayBody_one_column('time', this.utility_convertTime(in_json_data.start_time.toString()), 0, 0); 927 var town_column = this.domBuilder_PopulateWeekdayBody_one_column('town', this.utility_createAddressTown(in_json_data), 0, 0); 928 var name_column = this.domBuilder_PopulateWeekdayBody_one_column('name', in_json_data.meeting_name.toString(), 0, 0); 929 var address_column = this.domBuilder_PopulateWeekdayBody_one_column('address', this.utility_createStreetAddress(in_json_data), latitude, longitude); 930 var format_column = this.domBuilder_PopulateWeekdayBody_one_column('formats', in_json_data.formats.toString(), 0, 0); 931 932 rowElement.appendChild(time_column); 933 rowElement.appendChild(town_column); 934 rowElement.appendChild(name_column); 935 rowElement.appendChild(address_column); 936 rowElement.appendChild(format_column); 937 938 if (in_json_data.comments ) { 939 var comments_column = this.domBuilder_PopulateWeekdayBody_one_column('comments', in_json_data.comments.toString(), 0, 0); 940 rowElement.appendChild(comments_column); 941 }; 942 if (in_json_data.location_info ) { 943 var location_info_column = this.domBuilder_PopulateWeekdayBody_one_column('location_info', in_json_data.location_info.toString(), 0, 0); 944 rowElement.appendChild(location_info_column); 945 }; 946 947 var div = document.createElement('div'); 948 div.className = 'bmlt_breaker_div'; 949 rowElement.appendChild(div); 950 951 return rowElement; 952}; 953 954/************************************************************************************//** 955 \brief Populates one column of a meeting row. 956 \param in_tag The tag used as a key for this column 957 \param in_string The name of the column (displayed as a string) 958 \param in_latitude if supplied, the longitude of a meeting (is usually null) 959 \param in_longitude if supplied, the latitude of a meeting (is usually null) 960 \returns the instantiated DOM object. 961 ****************************************************************************************/ 962TableSearchDisplay.prototype.domBuilder_PopulateWeekdayBody_one_column = function ( in_tag, in_string, in_latitude, in_longitude ) { 963 var columnElement = document.createElement('li'); 964 columnElement.className = 'bmlt_table_data_ul_li_ul_li bmlt_table_data_ul_li_ul_li_' + in_tag; 965 966 if (in_tag == 'formats' ) { 967 var formats_div = this.domBuilder_createFormats(in_string); 968 if (formats_div ) { 969 columnElement.appendChild(formats_div); 970 }; 971 } 972 else if (in_latitude && in_longitude ) { 973 var textNode = document.createElement('span'); 974 textNode.className = 'bmlt_table_data_ul_li_ul_li_span bmlt_table_data_ul_li_ul_li_span_' + in_tag; 975 var anchorNode = document.createElement('a'); 976 textNode.className = 'bmlt_table_data_ul_li_ul_li_span_a bmlt_table_data_ul_li_ul_li_span_a_' + in_tag; 977 anchorNode.href= this.utility_sprintf(g_table_map_link_uri_format, parseFloat(in_latitude), parseFloat(in_longitude)); 978 anchorNode.innerHTML = in_string; 979 textNode.appendChild(anchorNode); 980 columnElement.appendChild(textNode); 981 } 982 else 983 { 984 var textNode = document.createElement('span'); 985 textNode.className = 'bmlt_table_data_ul_li_ul_li_span bmlt_table_data_ul_li_ul_li_span_' + in_tag; 986 textNode.innerHTML = in_string ? in_string : ' '; 987 columnElement.appendChild(textNode); 988 }; 989 990 return columnElement; 991}; 992 993/************************************************************************************//** 994 \brief This builds a div element for the given formats. The div is filled with spans; one for each format. 995 \param in_formats_string The JSON data for one meeting. 996 \returns the instantiated DOM object. 997 ****************************************************************************************/ 998TableSearchDisplay.prototype.domBuilder_createFormats = function ( in_formats_string ) { 999 var ret = document.createElement('div'); 1000 ret.className = 'bmlt_formats_div'; 1001 var formatsArray = in_formats_string.split(','); 1002 1003 for ( var i = 0; i < formatsArray.length; i++ ) 1004 { 1005 var format_string = formatsArray[i].toString(); 1006 var span = this.domBuilder_createOneFormatSpan(format_string); 1007 if (span ) { 1008 ret.appendChild(span); 1009 }; 1010 }; 1011 1012 if (ret.childNodes.length == 0 ) { 1013 ret = null; 1014 } 1015 else 1016 { 1017 var div = document.createElement('div'); 1018 div.className = 'bmlt_breaker_div'; 1019 ret.appendChild(div); 1020 }; 1021 1022 return ret; 1023}; 1024 1025/************************************************************************************//**
1026 \brief This builds a span element for the given format. 1027 \param in_format_string format key. 1028 \returns the instantiated DOM object. 1029 ****************************************************************************************/ 1030TableSearchDisplay.prototype.domBuilder_createOneFormatSpan = function ( in_format_string ) { 1031 if (in_format_string ) { 1032 var ret = document.createElement('span'); 1033 ret.rest_className = 'bmlt_format_span bmlt_format_span_' + in_format_string; 1034 ret.className = ret.rest_className; 1035 ret.appendChild(document.createTextNode(in_format_string)); 1036 for ( var i = 0; i < this.my_format_data.length; i++ ) 1037 { 1038 var format_object = this.my_format_data[i]; 1039 1040 if (format_object.key_string == in_format_string ) { 1041 var title_string = format_object.name_string; 1042 ret.setAttribute('title', title_string); 1043 var description_string = format_object.description_string; 1044 ret.onclick = function () { 1045 alert(description_string); 1046 }; 1047 1048 ret.onmouseover = function () { 1049 this.className = this.rest_className + ' bmlt_table_mouseover'; 1050 }; 1051 1052 ret.onmouseout = function () { 1053 this.className = this.rest_className; 1054 }; 1055 }; 1056 }; 1057 return ret; 1058 }; 1059 1060 return null; 1061};
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.