1/// <reference path="../../jquery-1.10.2.intellisense.js" /> 2 3var menu_close_timer = null; // Keeps track of mouse out delay timer to close user menu. 4var regex_valid_email = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/; 5 6$(function () { 7 8 $(document).on('sscevent:asynclogin', globalHandleLogin); // Attach to login event. 9 10 // Attach event to open User-Menu when UserName labeled Account Menu item is clicked. 11 $('#global_header').on('click', '.gh-account-menu', function () { 12 var $this = $(this); 13 var mpos = $this.position(); 14 15 $('.gh-account-menu-select').css('top', mpos.top + $this.outerHeight()).css('right', $(window).width() - (mpos.left + $this.outerWidth())).fadeIn(); 16 }); 17 18 // Close account menu when any item inside is clicked. 19 $('#global_header').on('click', '.gh-account-menu-item', function () { 20 $('.gh-account-menu-select').fadeOut(); 21 }); 22 23 // Attach event to begin timer when mouse exits menu to fade menu out. 24 $('.gh-account-menu-select').on('mouseleave', function () { 25 menu_close_timer = window.setTimeout(closeMenu, 1250); 26 }); 27 28 // Attach event to canel timer if they re-enter the menu (so it doesn't close when they just temporarily cross out of the border while navigating). 29 $('.gh-account-menu-select').on('mouseenter', function () { 30 if (menu_close_timer) 31 window.clearTimeout(menu_close_timer); 32 }); 33 34 function closeMenu() { 35 window.clearTimeout(menu_close_timer); 36 $('.gh-account-menu-select').fadeOut(); 37 } 38 39 $(document).on('click', '.link-signout', function () { 40 $.ajax({
41 type: 'POST', 42 url: '/api/Security/LogOff', 43 success: function (msg) { 44 if (msg.Success) { 45 window.location = '/' + (global_branding.URLPrefix ? global_branding.URLPrefix : ''); 46 } else 47 alert('LogOut Failed:\n\n' + msg.Msg); 48 }, 49 error: function () { 50 alert('Network or Server Error Attempting to Log Out!'); 51 } 52 }); 53 }); 54 55 makeAccountLinks(); 56 57 $('.enter-nosubmit, .enter-nextinput').on('keydown', function (e) { 58 59 if (e.keyCode == 13) { 60 61 var $this = $(this); 62 63 // Cancel the default event if this is enter-nosubmit 64 if ($this.is('.enter-nosubmit')) 65 return false; 66 67 // Go up to the parent enter-nextinput-group and find the next enter-nextinput after the current one. 68 if ($this.is('.enter-nextinput')) { 69 var $current = $this, found_item = false; 70 $this.closest('.enter-nextinput-group').find('.enter-nextinput').each(function () { 71 if (found_item) { 72 $(this).focus(); 73 return false; 74 } 75 76 if ($(this).is($current)) 77 found_item = true; 78 }); 79 return false; 80 } 81 } 82 83 return true; 84 }); 85 86 $('.allow-positive-decimal').on('keypress', function (e) { 87 88 if ((e.which >= 48 && e.which <= 57) || e.which == 46) { 89 return true; 90 } 91 92 return false; 93 }); 94 95 $('.button-cancel').on('click', function () { 96 window.history.back(); // Goes back, which should set the URL hash to users which should show the users list section 97 return false; 98 }); 99 100 $('.warning-unobtrusive').on('mouseenter', function () { 101 var $this = $(this); 102 $this.stop(); 103 104 if (window.timer_warning) { 105 window.clearTimeout(window.timer_warning); 106 window.timer_warning = null; 107 } 108 109 var bottom = parseFloat($this.css('bottom')); 110 if (bottom < 0) { 111 $this.animate({ 'bottom': 0 }, -(bottom / $this.outerHeight()) * 500); 112 } 113 114 }).on('mouseleave', function () { 115 window.timer_warning = window.setTimeout(hideWarning, 1500); 116 }); 117 118}); 119 120// This handles updating the header menus when a user logs in after loading the page. 121function globalHandleLogin(e, user_name) { 122 // Remove the join and sign-in links from the mobile & non-mobile 123 $('#li_join, #li_sign_in, #mobile_join, #mobile_sign_in').remove(); 124 125 // Create the user-name drop-down menu button that holds the user links for non-mobile with the sign-out link 126 $('nav.top.hidden-mobile ul').append('<li><div class="dropdown" data-dropdown><a href="#" class="account-name" data-dropdown-toggle style="padding-right: 3em;">Menu <svg style="position: absolute;" viewBox="0 0 24 24"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/></a><div class="dropdown-content account-nav right" data-dropdown-content><ul><li><a href="#" class="gh-account-menu-item sign-out link-signout">Sign Out</a></li></ul></div></div></li>'); 127 128 // Create the account links and signout for the mobible nav 129 $('div.mobile-nav-panel div.account').append('<ul class="nav-secondary"></ul><a href="#" class="btn btn-small link-signout">Sign Out</a>'); 130 131 makeAccountLinks(); 132} 133 134function makeAccountLinks() { 135 if (account_links) { 136 $(account_links) 137 .each(function () { 138 if (this.Type == 'Item') { 139 140 this.Text = $.trim(this.Text).toLowerCase() == 'account' ? elideEmailMiddle(window.username, 158) : this.Text; 141 142 var $link_item = $('<a class="gh-account-menu-item"></a>').html(this.Text); 143 if (this.Href) { 144 if (this.BrandingPrefix && global_branding.URLPrefix) { 145 if (this.Href.substr(0, 1) == '/') { 146 this.Href = this.Href.replace(/^\//, '/' + global_branding.URLPrefix); 147 } 148 } 149
150 $link_item.prop('href', this.Href); 151 } 152 if (this.ClientId) { 153 $link_item.prop('id', this.ClientId); 154 } 155 if (this.ClientClass) { 156 $link_item.addClass(this.ClientClass); 157 } 158 159 $('nav.top.hidden-mobile div.dropdown ul li:last').before($('<li></li>').append($link_item)); 160 161 if (!this.NoMobile) { 162 $('div.mobile-nav-panel div.account ul.nav-secondary').append($('<li></li>').append($link_item.clone())); 163 } 164 } 165 }); 166 } 167 168 if ($('nav.top.hidden-mobile div.dropdown li span.inbox-unread-count').length) { 169 $('nav.top.hidden-mobile #header_unread').show().find('.inbox-unread-count').text($('nav.top.hidden-mobile div.dropdown li span.inbox-unread-count').text()); 170 } 171} 172 173function elideEmailMiddle(email, width) { 174 var $tw = $('<div style="font-size:12.25px;display:none;"></div>'); 175 $('body').prepend($tw); 176 $tw.html(email); 177 if ($tw.width() <= width) { 178 $tw.remove(); 179 return email; 180 } 181 182 var lp = email.substr(0, email.indexOf('@') + 1); 183 184 $tw.html(lp); 185 if ($tw.width() > width) { 186 do { 187 lp = lp.substr(0, lp.length - 1); 188 $tw.html(lp + '…'); 189 } while ($tw.width() > width) 190 191 $tw.remove(); 192 return lp + '…'; 193 } 194 195 var rp = email.substr(email.indexOf('@') + 1); 196 var rpl = rp.substr(0, (rp.length + 1) / 2); 197 var rpr = rp.substr((rp.length + 1) / 2); 198 199 $tw.html(lp + rpl + '…' + rpr) 200 var cut_right = true; 201 while ($tw.width() > width) { 202 if (cut_right) { rpr = rpr.substr(1); } 203 else { rpl = rpl.substr(0, rpl.length - 1); } 204 if (!rpl.length && !rpr.length) { $tw.remove(); return email; } // failsafe 205 cut_right = !cut_right; 206 $tw.html(lp + rpl + '…' + rpr) 207 } 208 209 $tw.remove(); 210 return lp + rpl + '…' + rpr; 211} 212 213function modalCloseAll() { 214 $(document).trigger('close-modal-manual'); 215} 216 217function modalOpen(modal_name) { 218 $(document).trigger('close-modal-manual'); 219 $(document).trigger('open-modal-manual', [modal_name]); 220} 221 222function displayErrorMessage(msg) { 223 displayMessage('Error', msg); 224} 225 226function displaySuccessMessage(msg) { 227 displayMessage('Success', msg); 228} 229 230function displayMessage(title, msg) { 231 $('div[data-modal="generic-message"] div.modal-content-body').html((title ? '<h2>' + title + '</h2>' : '') + msg); 232 modalOpen('generic-message'); 233} 234 235function parseJSONDateTime(json_datetime) { 236 if (json_datetime) 237 return new Date(parseInt(json_datetime.match(/\/Date\(([0-9]+)\)\//)[1])); 238 else 239 return null; 240} 241 242function getDateTimeString(date_val) { 243 if (date_val) { 244 return $.datepicker.formatDate('m/d/yy', date_val) + ' ' + date_val.getHours() + ':' + ('0' + date_val.getMinutes()).substr(-2); 245 } 246 else { 247 return 'Unknown'; 248 } 249} 250 251function matchObject(obj, filter, fields) { 252 253 // Does it makes sense to combine all fields into one target string and search it just once (with regex?) Might need to use a funky char for a delimiter between the strings when combining them to prevent false positives being created. 254 if (fields) { 255 for (var i = 0; i < fields.length; i++) { 256 if (obj[fields[i]]) { 257 // TODO: Let's use case insensitive regex and try to support wildcards in the search. 258 if (obj[fields[i]].toString().toLowerCase().indexOf(filter) >= 0) 259 return true; 260 } 261 } 262 } 263 264 return false; 265} 266 267// Call a webmethod without input or specific HTTP method-verb 268function loadGlobalData(data_array, key, url, callback, error_callback) { 269 loadGlobalDataInput(data_array, key, url, null, callback, error_callback); 270} 271 272// Call a webmethod with input, but without specific HTTP method-verb 273function loadGlobalDataInput(data_array, key, url, data, callback, error_callback) { 274 loadGlobalDataMethod(data_array, key, null, url, data, callback, error_callback); 275} 276 277// Call a web method with the appropriate HTTP method(verb), input data & URL, & 278// store the result in a data_array and call the callback. This will bypass the 279// ajax if the exisiting data is already loaded, and just call the call back, so 280// multiple methods that need the same data can all request it using this 281// method, but only the first one actually triggers the AJAX request, and the 282// following calls just use the cached data. 283// Parameters 284// data_array: The object (associative array) in which to place the received data if it is not already there. 285// key: The named index to use for the recieved/cached data. 286// method: The HTTP method to use most likely 'GET' or 'POST'. If null then GET is default. 287// url: The URL for the web-method endpoint. 288// data: Any input data to pass to the web-method. 289// callback: The method to call upon successful HTTP communication with web-method. 290// prev_request: This method returns the XHR object, so you can save it and pass it back the next time you make the same request. If it's still running, it will cancel it before calling it again. 291function loadGlobalDataMethod(data_array, key, method, url, data, callback, error_callback, prev_request) { 292 293 // Kill any still pending request. 294 if (prev_request && prev_request.readyState != 4) { 295 prev_request.abort(); 296 } 297 298 // If we already store something under that index, then just call the callback. 299 if (data_array[key] != null) { 300 callback(); 301 return null; 302 } 303 else { 304 // Call the web-method. 305 return $.ajax({
306 type: (method ? method : 'GET'), url: url, dataType: "json", data: data, 307 success: function (msg) { 308 if (msg.Success) { 309 // Store the data we recieved, then call the callback. 310 // TODO: What about success=false in api result? 311 data_array[key] = msg.Data; 312 callback(); 313 } else { 314 if (!error_callback) { 315 displayErrorMessage('Unexpected Error Loading Global Data of Type "' + key + '"'); 316 } else { 317 error_callback(msg); 318 } 319 } 320 }, 321 error: function (response) { 322 // readyState=0 means unsent, which is probably because it was cancelled. 323 if (response.readyState > 0) { 324 if (!error_callback) { 325 // If a network error or server error happened, inform the user, but do not call the callback. 326 displayErrorMessage('Network or Server Error Loading Global Data of Type "' + key + '"'); 327 } else { 328 error_callback(); 329 } 330 } 331 } 332 }); 333 } 334} 335 336function selectFirstTab(tabgroup) { 337 var tab_toggle = $($('a[data-tab-group="' + tabgroup + '"]').removeClass('active')[0]).addClass('active').data('tab-toggle'); 338 $('.tab-content[data-tab-group="' + tabgroup + '"]').hide(); 339 $('.tab-content[data-tab-group="' + tabgroup + '"][data-tab-pane="' + tab_toggle + '"]').show(); 340} 341 342// Creates an English description of a number of minutes, in units of either min, hours, or days. 343function minutesDescription(minutes) { 344 345 if (minutes < 60) 346 return minutes + ' min'; 347 348 if (minutes < (24 * 60)) 349 return Math.round(minutes / 60) + ' hours'; 350 351 return Math.round(minutes / 60 / 24) + ' days'; 352} 353 354// Uses JavaScript's Localization to format the number for the Locale if available. This handles putting the thousands-separator and decimal appropriate for the local culture. 355function formatNumber(n) { 356 if (Number.prototype.toLocaleString && !isNaN(n)) { 357 return parseFloat(n).toLocaleString(); 358 } 359 360 return n; 361} 362 363// Adds a single row with full table width and centered message. $table is a jQuery object holding the table element. Message can be HTML string 364function appendTableMessage($table, message, no_clear) { 365 var num_cols = $table.children('thead').find('th,td').length; 366 if (!no_clear) { $table.find('tbody').empty(); } 367 $table.find('tbody').append($('<tr>').append($('<td style="text-align: center;"></td>').prop('colspan', num_cols).html(message))); 368} 369 370// Parses any URL Query Parameters and puts them in an array 371function queryParams() { 372 373 var vars = []; 374 var q = document.URL.split('?')[1]; 375 if (q != undefined) { 376 q = q.split('#')[0]; // Remove any hash fragment 377 q = q.split('&'); // split the variable pairs out 378 for (var i = 0; i < q.length; i++) { 379 var valpair = q[i].split('='); 380 vars.push(valpair[1]); 381 vars[valpair[0]] = valpair[1]; 382 } 383 } 384 385 return vars; 386} 387 388function isValidEmail(email) { return regex_valid_email.test(email); } 389 390// Returns a string with the status of the property based on the input flags. 391// Truth Table (X=Don't Care) 392// Approved | F | T | F | F | 393// Declined | F | X | T | F | 394// Submitted | F | X | X | T | 395// =========================== 396// Result | D | P | R | S | Key: D=Draft, P=Published, R=Declined, S=Submitted 397function getListingStatus(approved, declined, submitted) { 398 if (approved) { return 'Published'; } 399 if (declined) { return 'Declined'; } 400 if (submitted) { return 'Submitted'; } 401 return 'Draft'; 402} 403 404// Returns null if val is null, or empty/whitespace string, otherwise returns orig val. 405function nullEmpty(val) { 406 if (!val || $.trim(val) == '') 407 return null; 408 else 409 return val; 410} 411 412// Returns true if object member named 'member' is the only non-null value. 413function onlyValue(obj, member) { 414 // Make sure the member they are talking about has a value 415 if (!obj[member]) return false; 416 417 var keys = Object.keys(obj); 418 // Look for any members that have a value that aren't the member 419 var idx = keys.findIndex(function (key) { 420 return (obj[key] && key != this); 421 }, member); 422 423 // Return whether we found one 424 return idx < 0; 425} 426 427// Returns true if object members in array members are the only non-null values. 428function onlyValues(obj, members) { 429 430 // Make sure all the members asked for have values. 431 for (var i = 0; i < members.length; i++) { 432 if (!obj[members[i]]) return false; 433 } 434 435 var keys = Object.keys(obj); 436 // Look for any members that have a value that aren't in the members list 437 var idx = keys.findIndex(function (key) { 438 return (obj[key] && this.indexOf(key) < 0); 439 }, members); 440 441 442 // Return whether we found one 443 return idx < 0; 444} 445 446function getExtentVals() { 447 448 var default_extent_vals = [-97, -87.8, 28.8, 36.8]; 449 450 if (global_branding && global_branding['MapInitialExtent']) { 451 452 var extent_vals = global_branding['MapInitialExtent'].split(','); 453 // There should be exactly 4 (bounding box corners) 454 if (extent_vals.length == 4) { 455 // Make sure all the extent values are valid floats 456 for (var evi = 0; evi < 4; evi++) { 457 if (!isNaN(extent_vals[evi])) { 458 extent_vals[evi] = parseFloat(extent_vals[evi]); 459 } 460 else { 461 return default_extent_vals; 462 } 463 } 464 465 return extent_vals; 466 } 467 } 468 469 return default_extent_vals; 470} 471 472function isNonNullEmpty(obj) { 473 if (obj != null && obj.length == 0) 474 return true; 475 476 return false; 477} 478 479// Takes a JavaScript function arguments object and creates a name value pair object from the variable names and the values passed. 480// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments 481function objectifyArguments(arg_list) { 482 483 // Extract the argument names from the text of the function itself. Remove newlines and spaces and extract the bit between the parens after the keyword function. 484 var arg_names_match = arg_list.callee.toString().replace(/\n/g, '').replace(/\s*/g, '').match(/^function\(([^)]+)\)/); 485 if (!arg_names_match) 486 return null; 487 488 // Create an empty object to store the name/values. 489 var values = {}; 490 491 // Split the argument names (from the function text) and loop through them adding each as the name with it's value from the arg_list 492 var arg_names = arg_names_match[1].split(','); 493 for (var i = 0; i < arg_names.length; i++) { 494 values[arg_names[i]] = arg_list[i]; 495 } 496 497 return values; 498} 499 500// Processes a string to make it suitable for a title by removing non-alpha-numeric chars and replacing space
500s with dashes as well as optionally limiting the lenth 501function makeURLTitle(text, len) { 502 return text.replace(/[^a-zA-Z0-9 ]/g, '').replace(/ +/g, '-').substr(0, len); 503} 504 505function setCookie(cname, cvalue, exdays) { 506 var d = new Date(); 507 d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000)); 508 var expires = "expires=" + d.toUTCString(); 509 document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"; 510} 511 512function getCookie(cname) { 513 var name = cname + "="; 514 var ca = document.cookie.split(';'); 515 for (var i = 0; i < ca.length; i++) { 516 var c = ca[i]; 517 while (c.charAt(0) == ' ') { 518 c = c.substring(1); 519 } 520 if (c.indexOf(name) == 0) { 521 return c.substring(name.length, c.length); 522 } 523 } 524 return ""; 525} 526 527function isPasswordValid(password) { 528 if (!password) { return false; } 529 530 // Regular Exps don't have an AND operator, so it's difficult to write a regex that matches valid passwords, when validity is defined by something AND something else AND something else. But, regular expressions do have an OR operator, so just apply DeMorgan's theorem, and write a regex that matches invalid passwords 531 return !password.match('^(.{0,5}|[^0-9]*|[^A-Z]*|[^a-z]*)$'); 532} 533 534function getRandom(length) { 535 536 return Math.floor(Math.pow(10, length - 1) + Math.random() * 9 * Math.pow(10, length - 1)); 537 538} 539 540function countdownRedirect($counter) { 541 542 var cd = parseInt($counter.html()); 543 544 if (cd <= 0) { 545 window.location = $counter.data('redirect-target'); 546 } 547 else { 548 // If we are just starting, then don't decrement till after the 1st second. 549 if ($counter.is('.coundown-active')) { 550 cd--; 551 $counter.html(cd); 552 } else { 553 $counter.addClass('coundown-active'); 554 } 555 556 window.setTimeout(function () { countdownRedirect($counter); }, 1000); 557 } 558} 559 560function formatFileSize(size) { 561 if (size < 1024) { 562 return size.toLocaleString() + " B"; 563 } 564 565 if (size < 1024 * 1024) { 566 return (size / 1024).toFixed().toLocaleString() + " KB"; 567 } 568 569 return (size / 1024 / 1024).toFixed(1).toLocaleString() + " MB"; 570} 571 572function escapeHTML(html) { 573 var text = document.createTextNode(html); 574 var p = document.createElement('p'); 575 p.appendChild(text); 576 return p.innerHTML; 577}
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.