1//Author: Silverbear 2//Date: 18/04/2012 3//Summary: A collection of handy custom knockoutJS extensions. 4//jQuery widget needed for dialog 5var jQueryWidget = function (element, valueAccessor, name, constructor) { 6 var options = ko.utils.unwrapObservable(valueAccessor()); 7 var $element = $(element); 8 setTimeout(function () { constructor($element, options) }, 0); 9 //$element.data(name, $widget); 10 11}; 12///A computed observable that can be paused and resumed. 13ko.pauseableComputed = function (evaluatorFunction, evaluatorFunctionTarget) { 14 var _cachedValue = ""; 15 var _isPaused = ko.observable(false); 16 17 //the computed observable that we will return 18 var result = ko.computed(function () { 19 if (!_isPaused()) { 20 //call the actual function that was passed in 21 return evaluatorFunction.call(evaluatorFunctionTarget); 22 } 23 return _cachedValue; 24 }, evaluatorFunctionTarget); 25 //keep track of our current value and set the pause flag to release our actual subscriptions 26 result.pause = function () { 27 _cachedValue = this(); 28 _isPaused(true); 29 }.bind(result); 30 //clear the cached value and allow our computed observable to be re-evaluated 31 result.resume = function () { 32 _cachedValue = ""; 33 _isPaused(false); 34 }; 35 return result; 36}; 37 38ko.extenders.withPrevious = function (target) { 39 // Define new properties for previous value and whether it's changed 40 target.previous = ko.observable(); 41 target.changed = ko.computed(function () { return target() !== target.previous(); }); 42 43 // Subscribe to observable to update previous, before change. 44 target.subscribe(function (v) { 45 target.previous(v); 46 }, null, 'beforeChange'); 47 48 // Return modified observable 49 return target; 50}; 51// a binding that can be used to scroll an element into view when an observable is set. 52ko.bindingHandlers.intoView = { 53 update: function (element, valueAccessor) { 54 if (valueAccessor()) { 55 // Happens on other thread to allow this thread to finish rendering html elements. 56 setTimeout(function () { 57 element.scrollIntoView(); 58 }, 0); 59 }; 60 } 61}; 62//alternative dialog binding 63ko.bindingHandlers.dialogcmd = { 64 init: function (element, valueAccessor, allBindingsAccessor, viewModel) { 65 jQueryWidget(element, valueAccessor, 'dialog', function ($element, options) { 66 return $element.dialog(options); 67 }); 68 } 69}; 70ko.bindingHandlers.dialog = { 71 init: function (element, valueAccessor, allBindingsAccessor) { 72 var options = ko.utils.unwrapObservable(valueAccessor()) || {}; 73 //do in a setTimeout, so the applyBindings doesn't bind twice from element being copied and moved to bottom 74 setTimeout(function () { 75 options.close = function () { 76 if (ko.isObservable(allBindingsAccessor().dialogVisible)) { 77 allBindingsAccessor().dialogVisible(false); 78 } else { 79 allBindingsAccessor().dialogVisible = false; 80 } 81 }; 82 $(element).dialog(options); 83 }, 0); 84 85 //handle disposal (not strictly necessary in this scenario) 86 // ko.utils.domNodeDisposal.addDisposeCallback(element, function () { 87 // $(element).dialog("destroy"); 88 // }); 89 }, 90 update: function (element, valueAccessor, allBindingsAccessor) { 91 var shouldBeOpen = ko.utils.unwrapObservable(allBindingsAccessor().dialogVisible), 92 $el = $(element), 93 dialog = $el.data("uiDialog") || $el.data("dialog"); 94 95 //don't call open/close before initilization 96 if (dialog) { 97 $el.dialog(shouldBeOpen ? "open" : "close"); 98 } 99 } 100}; 101//Custom controlsDescendantBindings for KnockoutJSRegulator JS 102ko.bindingHandlers.dateString = { 103 update: function (element, valueAccessor, allBindingsAccessor, viewModel) { 104 var value = valueAccessor(), 105 allBindings = allBindingsAccessor(); 106 var valueUnwrapped = ko.utils.unwrapObservable(value); 107 var pattern = allBindings.datePattern || 'dd/MM/yyyy'; 108 $(element).text(valueUnwrapped.toString(pattern)); 109 } 110}; 111ko.bindingHandlers.dateStringFromJsonString = { 112 update: function (element, valueAccessor, allBindingsAccessor, viewModel) { 113 var value = valueAccessor(), 114 allBindings = allBindingsAccessor(); 115 var valueUnwrapped = ko.utils.unwrapObservable(value); 116 if (valueUnwrapped != null) { 117 var pattern = allBindings.datePattern || 'dd/MM/yyyy'; 118 var date = new Date(parseInt(valueUnwrapped.substr(6))); 119 $(element).text(date.toString(pattern)); 120 } else { 121 var nullText = allBindings.nullText || ''; 122 $(element).text(nullText); 123 } 124 } 125}; 126 127function FormatDate(date, pattern) { 128 var day = String(date.getUTCDate()).padStart(2, '0');
129 var month = String(date.getUTCMonth() + 1).padStart(2, '0'); // Adding 1 because month index starts from 0 130 var year = date.getUTCFullYear(); 131 132 switch (pattern) { 133 case 'MM/dd/yyyy': 134 return month + '/' + day + '/' + year; 135 case 'yyyy/MM/dd': 136 return year + '/' + month + '/' + day; 137 case 'MM-dd-yyyy': 138 return month + '-' + day + '-' + year; 139 case 'yyyy-MM-dd': 140 return year + '-' + month + '-' + day; 141 // Add more cases for additional patterns as needed 142 default: 143 // Default to 'dd/MM/yyyy' if pattern is not matched - doubles up as a match to this pattern 144 return day + '/' + month + '/' + year; 145 } 146} 147 148 149ko.bindingHandlers.dateString = { 150 init: function (element, valueAccessor, allBindingsAccessor) { 151 // coerce date observabel value. 152 //handle date data coming via json from Microsoft 153 var observable = valueAccessor(); 154 var value = ko.utils.unwrapObservable(observable); 155 if (String(value).indexOf('/Date(') == 0) { 156 var coercedDate = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1"))); 157 observable(coercedDate); 158 } 159 }, 160 update: function (element, valueAccessor, allBindingsAccessor, viewModel) { 161 var value = valueAccessor(), 162 allBindings = allBindingsAccessor(); 163 var valueUnwrapped = ko.utils.unwrapObservable(value); 164 if (valueUnwrapped != null) { 165 var pattern = allBindings.datePattern || 'dd/MM/yyyy'; 166 var dateString = FormatDate(valueUnwrapped, pattern); 167 $(element).text(dateString); 168 } else { 169 var nullText = allBindings.nullText || ''; 170 $(element).text(nullText); 171 } 172 } 173}; 174function FormatAsShortDate(observableValue, emptyDateText) { 175 var dateValue = ko.utils.unwrapObservable(observableValue); 176 if (dateValue != null) { 177 var date = new Date(parseInt(dateValue.substr(6))); 178 var formattedDate = $.format.date(date, jQuery.format.date.defaultShortDateFormat); 179 return formattedDate; 180 } else { 181 return emptyDateText; 182 } 183} 184ko.observableArray.fn.sortByProperty = function (prop) { 185 this.sort(function (obj1, obj2) { 186 if (obj1[prop] == obj2[prop]) 187 return 0; 188 else if (obj1[prop] < obj2[prop]) 189 return -1; 190 else 191 return 1; 192 }); 193}; 194ko.observableArray.fn.pushAll = function (valuesToPush) { 195 var underlyingArray = this(); 196 this.valueWillMutate(); 197 ko.utils.arrayPushAll(underlyingArray, valuesToPush); 198 this.valueHasMutated(); 199 return this; //optional 200}; 201ko.bindingHandlers.fadeVisible = { 202 update: function (element, valueAccessor, allBindingsAccessor) { 203 // First get the latest data that we're bound to 204 var value = valueAccessor(), allBindings = allBindingsAccessor(); 205 206 // Next, whether or not the supplied model property is observable, get its current value 207 var valueUnwrapped = ko.utils.unwrapObservable(value); 208 209 // Grab some more data from another binding property 210 var duration = allBindings.fadeDuration || 400; // 400ms is default duration unless otherwise specified 211 212 // Now manipulate the DOM element 213 if (valueUnwrapped == true) 214 $(element).fadeIn(duration); // Make the element visible 215 else 216 $(element).fadeOut(duration); // Make the element invisible 217 } 218}; 219ko.bindingHandlers.fadeOnChange = { 220 update: function (element, valueAccessor, allBindingsAccessor) { 221 // First get the latest data that we're bound to 222 // var value = valueAccessor() 223 // var allBindings = allBindingsAccessor(); 224 // // Grab some more data from another binding property 225 // var duration = allBindings.fadeDuration || 400; // 400ms is default duration unless otherwise specified 226 // // Now manipulate the DOM element 227 // $(element).fadeIn(duration); // Make the element visible 228 var allBindings = allBindingsAccessor(); 229 // Grab some more data from another binding property 230 var duration = allBindings.fadeDuration || 1000; // 400ms is default duration unless otherwise specified 231 // Now manipulate the DOM element 232 $(element).hide().fadeIn(duration); // Make the element visible 233 } 234}; 235ko.bindingHandlers.slideVisible = { 236 update: function (element, valueAccessor, allBindingsAccessor) { 237 // First get the latest data that we're bound to 238 var value = valueAccessor(), allBindings = allBindingsAccessor(); 239 240 // Next, whether or not the supplied model property is observable, get its current value 241 var valueUnwrapped = ko.utils.unwrapObservable(value); 242 243 // Grab some more data from another binding property 244 var duration = allBindings.slideDuration || 400; // 400ms is default duration unless otherwise specified 245 246 // Now manipulate the DOM element 247 if (valueUnwrapped == true) 248 $(element).slideDown(duration); // Make the element visible 249 else
250 $(element).slideUp(duration); // Make the element invisible 251 } 252}; 253ko.bindingHandlers.slideHorizontalVisible = { 254 update: function (element, valueAccessor, allBindingsAccessor) { 255 // First get the latest data that we're bound to 256 var value = valueAccessor(), allBindings = allBindingsAccessor(); 257 258 // Next, whether or not the supplied model property is observable, get its current value 259 var valueUnwrapped = ko.utils.unwrapObservable(value); 260 261 // Grab some more data from another binding property 262 var duration = allBindings.slideDuration || 400; // 400ms is default duration unless otherwise specified 263 264 // Now manipulate the DOM element 265 if (valueUnwrapped == true) 266 $(element).slide(duration); // Make the element visible 267 else 268 $(element).slideUp(duration); // Make the element invisible 269 } 270}; 271ko.bindingHandlers.datepicker = { 272 init: function (element, valueAccessor, allBindingsAccessor) { 273 var timezoneOffset = new Date().getTimezoneOffset() * 60000; 274 275 //initialize datepicker with some optional options 276 var options = allBindingsAccessor().datepickerOptions || {}; 277 if (!options.dateFormat) options.dateFormat = 'dd/mm/yy'; 278 279 // Adds a 'clear' button to the datepicker. 280 options.closeText = "Clear"; 281 options.showButtonPanel = true; 282 options.onClose = function (dateText, obj) { 283 if ($(window.event.srcElement).hasClass('ui-datepicker-close')) { 284 $(this).datepicker('setDate', null); 285 $(this).trigger('change'); 286 } 287 288 $(this).blur(); 289 }; 290 291 options.beforeShow = function (input) { 292 setTimeout(function () { 293 $(input).datepicker("widget").find(".ui-datepicker-current").hide(); 294 }, 295 1); 296 }; 297 298 $(element).datepicker(options); 299 300 //handle the field changing 301 ko.utils.registerEventHandler(element, "change", function () { 302 var observable = valueAccessor(); 303 304 //check that they haven't pasted rubbish into the box 305 try { 306 $.datepicker.parseDate(options.dateFormat, $(element).val()); 307 } catch (e) { 308 $(element).val(''); 309 observable(null); 310 return; 311 } 312 313 var selectedDate = $(element).datepicker("getDate"); 314 if (selectedDate === undefined || selectedDate === null) { 315 observable(null); 316 } else { 317 var newEpoch = selectedDate.getTime(); 318 // we want to get the timezone offset of the selected date so we account for BST properly 319 timezoneOffset = selectedDate.getTimezoneOffset() * 60000; 320 observable(new Date(newEpoch - timezoneOffset)); 321 } 322 }); 323 324 var observable = valueAccessor(); 325 var value = ko.utils.unwrapObservable(observable); 326 if (String(value).indexOf('/Date(') == 0) { 327 var coercedDate = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1"))); 328 observable(coercedDate); 329 } 330 331 ko.utils.domNodeDisposal.addDisposeCallback(element, function () { 332 $(element).datepicker("destroy"); 333 }); 334 335 }, 336 update: function (element, valueAccessor) { 337 var timezoneOffset = new Date().getTimezoneOffset() * 60000; 338 var newValue = ko.utils.unwrapObservable(valueAccessor()); 339 var oldValue = $(element).datepicker("getDate"); 340 341 var newEpoch; 342 var oldEpoch; 343 344 if (newValue === undefined || newValue === null) { 345 newEpoch = null; 346 } 347 else if (String(newValue).indexOf('/Date(') == 0) { 348 //handle date data coming via json from Microsoft 349 newEpoch = parseInt(newValue.replace(/\/Date\((.*?)\)\//gi, "$1")); 350 } 351 else if (newValue instanceof Date) { 352 newEpoch = newValue.getTime(); 353 } 354 355 if (oldValue === undefined || oldValue === null) { 356 oldEpoch = null; 357 } 358 else if (String(oldValue).indexOf('/Date(') == 0) { 359 //handle date data coming via json from Microsoft 360 oldEpoch = parseInt(oldValue.replace(/\/Date\((.*?)\)\//gi, "$1")); 361 } 362 else if (oldValue instanceof Date) { 363 oldEpoch = oldValue.getTime(); 364 } 365 366 if (newEpoch !== oldEpoch) { 367 if (newEpoch + timezoneOffset !== oldEpoch) { 368 var date = new Date(newEpoch + timezoneOffset); 369 $(element).datepicker("setDate", date); 370 } 371 372 } else if (newEpoch == null) { 373 $(element).datepicker("setDate", null); 374 } 375 } 376}; 377ko.bindingHandlers.uniqueId = { 378 init: function (element, valueAccessor, allBindings, viewModel, bindingContext) { 379 var value = valueAccessor();
380 var thisPrefix = value || ko.bindingHandlers.uniqueId.prefix; 381 //var options = { prefix: thisPrefix }; 382 var uniqueId = thisPrefix + (++ko.bindingHandlers.uniqueId.counter); 383 element.id = uniqueId; 384 viewModel.generatedUniqueId = uniqueId; 385 }, 386 counter: 0, 387 prefix: "unique" 388}; 389ko.bindingHandlers.uniqueFor = { 390 init: function (element, valueAccessor, allBindingsAccessor) { 391 var value = valueAccessor(); 392 var thisPrefix = value || ko.bindingHandlers.uniqueId.prefix; 393 //var options = allBindingsAccessor().uniqueIdOptions || { prefix: thisPrefix }; 394 395 var uniqueId = thisPrefix + (ko.bindingHandlers.uniqueId.counter + 1); 396 element.setAttribute("for", uniqueId); 397 } 398}; 399ko.bindingHandlers.pickListValue = { 400 update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { 401 var value = valueAccessor(), 402 allBindings = allBindingsAccessor(), 403 dataModel = bindingContext.$parents[bindingContext.$parents.length - 2].DataModel; 404 var pickListItemId = ko.utils.unwrapObservable(value); 405 if (pickListItemId != null && dataModel.PickLists[allBindings.pickListName] && dataModel.PickLists[allBindings.pickListName][pickListItemId]) { 406 $(element).text(dataModel.PickLists[allBindings.pickListName][pickListItemId]()); 407 } else { 408 var nullText = allBindings.nullText || ''; 409 $(element).text(nullText); 410 } 411 } 412}; 413ko.bindingHandlers.expand = { 414 init: function (element, valueAccessor, allBindingsAccessor, viewModel) { 415 if ($(element).hasClass('ui-expander')) { 416 var expander = element; 417 var head = $(expander).find('.ui-expander-head'); 418 var content = $(expander).find('.ui-expander-content'); 419 420 $(head).click(function () { 421 $(head).toggleClass('ui-expander-head-collapsed'); 422 $(content).toggle(); 423 }); 424 } 425 } 426}; 427ko.bindingHandlers.attrIf = { 428 update: function (element, valueAccessor, allBindingsAccessor) { 429 var h = ko.utils.unwrapObservable(valueAccessor()); 430 var show = ko.utils.unwrapObservable(h._if); 431 if (show) { 432 ko.bindingHandlers.attr.update(element, valueAccessor, allBindingsAccessor); 433 } else { 434 for (var k in h) { 435 if (h.hasOwnProperty(k) && k.indexOf("_") !== 0) { 436 $(element).removeAttr(k); 437 } 438 } 439 } 440 } 441}; 442ko.bindingHandlers.singleClick = { 443 init: function (element, valueAccessor) { 444 var handler = valueAccessor(), 445 delay = 200, 446 clickTimeout = false; 447 448 $(element).click(function () { 449 if (clickTimeout !== false) { 450 clearTimeout(clickTimeout); 451 clickTimeout = false; 452 } else { 453 clickTimeout = setTimeout(function () { 454 clickTimeout = false; 455 handler(); 456 }, delay); 457 } 458 }); 459 } 460}; 461ko.bindingHandlers.tooltip = { 462 init: function (element, valueAccessor) { 463 var local = ko.utils.unwrapObservable(valueAccessor()), 464 options = {}; 465 466 ko.utils.extend(options, ko.bindingHandlers.tooltip.options); 467 ko.utils.extend(options, local); 468 469 $(element).tooltip(options); 470 471 ko.utils.domNodeDisposal.addDisposeCallback(element, function () { 472 $(element).tooltip("destroy"); 473 }); 474 }, 475 options: { 476 placement: "right", 477 trigger: "click" 478 } 479}; 480//Binding for http://www.bootstraptoggle.com/. JS and CSS file from their CDN but be included on the page separately. Must be used with checked binding. 481//Usage: data-bind="checked: observable, toggle: { on: 'Yes', off: 'No', onstyle: 'success', offstyle: 'danger', size: 'normal', style: 'cssclass' }" 482ko.bindingHandlers.toggle = { 483 init: function (element, valueAccessor, allBindings) { 484 if (!$.fn.bootstrapToggle) { return; } 485 var params = ko.unwrap(valueAccessor()); 486 var on = ko.unwrap(params['on']) || 'On'; 487 var off = ko.unwrap(params['off']) || 'Off'; 488 var onstyle = ko.unwrap(params['onstyle']) || 'primary'; 489 var offstyle = ko.unwrap(params['offstyle']) || 'default'; 490 var size = ko.unwrap(params['size']) || 'normal'; 491 var style = ko.unwrap(params['style']) || ''; 492 $(element).bootstrapToggle({ on: on, off: off, onstyle: onstyle, offstyle: offstyle, size: size, style: style }); 493 $(element).change(function () { $(element).triggerHandler('click'); }); //trigger update to observable 494 } 495}; 496ko.bindingHandlers.autocomplete = { 497 init: function (element, valueAccessor, allBindings) { 498 var options = allBindings()['autocompleteoptions']; 499 var value = valueAccessor(); 500 $(element).val(value()); 501 $(element).autocomplete({ 502 source: ko.unwrap(options), 503 select: function (event, ui) { 504 var value = valueAccessor(); 505 if (ui.item) value(ui.item.value); 506 } 507 }).blur(function () { 508 var value = valueAccessor(); 509 value($(element).val()); 510 }); 511 } 512}; 513 514ko.bindingHandlers.slick = { 515 init: function (element, valueAccessor) { 516 var local = ko.utils.unwrapObservable(valueAccessor()), 517 options = {}; 518 519 ko.utils.extend(options, ko.bindingHandlers.slick.options); 520 ko.utils.extend(options, local); 521 522 $(element).slick(options); 523 }, 524 options: { 525 slidesToShow: 3, 526 slidesToScroll: 1, 527 responsive: [ 528 { 529 breakpoint: 1024, 530 settings: { 531 slidesToShow: 3, 532 slidesToScroll: 3, 533 infinite: true, 534 dots: true 535 } 536 }, 537 { 538 breakpoint: 600, 539 settings: { 540 slidesToShow: 2, 541 slidesToScroll: 2 542 } 543 }, 544 { 545 breakpoint: 480, 546 settings: { 547 slidesToShow: 1, 548 slidesToScroll: 1 549 } 550 } 551 ] 552 } 553}; 554if (!Function.prototype.bind) { 555 Function.prototype.bind = function (oThis) { 556 if (typeof this !== "function") { 557 // closest thing possible to the ECMAScript 5 internal IsCallable function 558 throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); 559 } 560 var aArgs = Array.prototype.slice.call(arguments, 1), 561 fToBind = this, 562 fNOP = function () { }, 563 fBound = function () { 564 return fToBind.apply(this instanceof fNOP && oThis 565 ? this 566 : oThis, 567 aArgs.concat(Array.prototype.slice.call(arguments))); 568 }; 569 fNOP.prototype = this.prototype; 570 fBound.prototype = new fNOP(); 571 return fBound; 572 }; 573} 574 575ko.bindingHandlers.comboTree = { 576 //On iniliatise we need to create our tree 577 init: function (element, valueAccessor, allBindingsAccessor) { 578 579 //initialize combotree with some optional options 580 var options = allBindingsAccessor().treeOptions; 581 582 if (options == undefined) { 583 options = {}; 584 } 585 586 options.source = ko.toJS(ko.unwrap(valueAccessor())); 587 588 SubjectTree = $(element).comboTree(options); 589 590 ko.utils.registerEventHandler(element, "change", function (e) { 591 var observable = allBindingsAccessor().treeValue; 592 593 if (e.originalEvent.detail !== undefined) { 594 observable(e.originalEvent.detail); 595 } 596 else { 597 observable(null); 598 } 599 600 }); 601 602 }, 603 update: function (element, valueAccessor, allBindingsAccessor) { 604 var subjectId = ko.unwrap(allBindingsAccessor().treeValue()); 605 if (subjectId !== undefined && subjectId != null) { 606 607 var tree = valueAccessor(); 608 var flattenedTree = FlattenSubjects([], tree); 609 610 for (var i = 0; i < flattenedTree.length; i++) { 611 if (flattenedTree[i].id == subjectId) { 612 element.value = flattenedTree[i].title; 613 break; 614 } 615 } 616 } else { 617 element.value = ""; 618 } 619 } 620}; 621 622function FlattenSubjects(flattenedTree, tree) { 623 for (var i = 0; i < tree.length; i++) { 624 flattenedTree.push({ id: tree[i].id, title: tree[i].title }); 625 if (tree[i].subs != null && tree[i].subs.length > 0) { 626 flattenedTree = FlattenSubjects(flattenedTree, tree[i].subs); 627 } 628 } 629 return flattenedTree; 630}
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.