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}; 126ko.bindingHandlers.dateString = { 127 init: function (element, valueAccessor, allBindingsAccessor) { 128 // coerce date observabel value. 129 //handle date data coming via json from Microsoft 130 var observable = valueAccessor();
131 var value = ko.utils.unwrapObservable(observable); 132 if (String(value).indexOf('/Date(') == 0) { 133 var coercedDate = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1"))); 134 observable(coercedDate); 135 } 136 }, 137 update: function (element, valueAccessor, allBindingsAccessor, viewModel) { 138 var value = valueAccessor(), 139 allBindings = allBindingsAccessor(); 140 var valueUnwrapped = ko.utils.unwrapObservable(value); 141 if (valueUnwrapped != null) { 142 var pattern = allBindings.datePattern || 'dd/MM/yyyy'; 143 if (Object.prototype.toString.call(valueUnwrapped) === '[object Date]') { 144 $(element).text(valueUnwrapped.toString(pattern)); 145 } else { 146 var date = new Date(parseInt(valueUnwrapped.substr(6))); 147 $(element).text(date.toString(pattern)); 148 } 149 } else { 150 var nullText = allBindings.nullText || ''; 151 $(element).text(nullText); 152 } 153 } 154}; 155function FormatAsShortDate(observableValue, emptyDateText) { 156 var dateValue = ko.utils.unwrapObservable(observableValue); 157 if (dateValue != null) { 158 var date = new Date(parseInt(dateValue.substr(6))); 159 var formattedDate = $.format.date(date, jQuery.format.date.defaultShortDateFormat); 160 return formattedDate; 161 } else { 162 return emptyDateText; 163 } 164} 165ko.observableArray.fn.sortByProperty = function (prop) { 166 this.sort(function (obj1, obj2) { 167 if (obj1[prop] == obj2[prop]) 168 return 0; 169 else if (obj1[prop] < obj2[prop]) 170 return -1; 171 else 172 return 1; 173 }); 174}; 175ko.observableArray.fn.pushAll = function (valuesToPush) { 176 var underlyingArray = this(); 177 this.valueWillMutate(); 178 ko.utils.arrayPushAll(underlyingArray, valuesToPush); 179 this.valueHasMutated(); 180 return this; //optional 181}; 182ko.bindingHandlers.fadeVisible = { 183 update: function (element, valueAccessor, allBindingsAccessor) { 184 // First get the latest data that we're bound to 185 var value = valueAccessor(), allBindings = allBindingsAccessor(); 186 187 // Next, whether or not the supplied model property is observable, get its current value 188 var valueUnwrapped = ko.utils.unwrapObservable(value); 189 190 // Grab some more data from another binding property 191 var duration = allBindings.fadeDuration || 400; // 400ms is default duration unless otherwise specified 192 193 // Now manipulate the DOM element 194 if (valueUnwrapped == true) 195 $(element).fadeIn(duration); // Make the element visible 196 else 197 $(element).fadeOut(duration); // Make the element invisible 198 } 199}; 200ko.bindingHandlers.fadeOnChange = { 201 update: function (element, valueAccessor, allBindingsAccessor) { 202 // First get the latest data that we're bound to 203 // var value = valueAccessor() 204 // var allBindings = allBindingsAccessor(); 205 // // Grab some more data from another binding property 206 // var duration = allBindings.fadeDuration || 400; // 400ms is default duration unless otherwise specified 207 // // Now manipulate the DOM element 208 // $(element).fadeIn(duration); // Make the element visible 209 var allBindings = allBindingsAccessor(); 210 // Grab some more data from another binding property 211 var duration = allBindings.fadeDuration || 1000; // 400ms is default duration unless otherwise specified 212 // Now manipulate the DOM element 213 $(element).hide().fadeIn(duration); // Make the element visible 214 } 215}; 216ko.bindingHandlers.slideVisible = { 217 update: function (element, valueAccessor, allBindingsAccessor) { 218 // First get the latest data that we're bound to 219 var value = valueAccessor(), allBindings = allBindingsAccessor(); 220 221 // Next, whether or not the supplied model property is observable, get its current value 222 var valueUnwrapped = ko.utils.unwrapObservable(value); 223 224 // Grab some more data from another binding property 225 var duration = allBindings.slideDuration || 400; // 400ms is default duration unless otherwise specified 226 227 // Now manipulate the DOM element 228 if (valueUnwrapped == true) 229 $(element).slideDown(duration); // Make the element visible 230 else
231 $(element).slideUp(duration); // Make the element invisible 232 } 233}; 234ko.bindingHandlers.slideHorizontalVisible = { 235 update: function (element, valueAccessor, allBindingsAccessor) { 236 // First get the latest data that we're bound to 237 var value = valueAccessor(), allBindings = allBindingsAccessor(); 238 239 // Next, whether or not the supplied model property is observable, get its current value 240 var valueUnwrapped = ko.utils.unwrapObservable(value); 241 242 // Grab some more data from another binding property 243 var duration = allBindings.slideDuration || 400; // 400ms is default duration unless otherwise specified 244 245 // Now manipulate the DOM element 246 if (valueUnwrapped == true) 247 $(element).slide(duration); // Make the element visible 248 else 249 $(element).slideUp(duration); // Make the element invisible 250 } 251}; 252ko.bindingHandlers.datepicker = { 253 init: function (element, valueAccessor, allBindingsAccessor) { 254 var timezoneOffset = new Date().getTimezoneOffset() * 60000; 255 256 //initialize datepicker with some optional options 257 var options = allBindingsAccessor().datepickerOptions || {}; 258 if (!options.dateFormat) options.dateFormat = 'dd/mm/yy'; 259 260 // Adds a 'clear' button to the datepicker. 261 options.closeText = "Clear"; 262 options.showButtonPanel = true; 263 options.onClose = function (dateText, obj) { 264 if ($(window.event.srcElement).hasClass('ui-datepicker-close')) { 265 $(this).datepicker('setDate', null); 266 $(this).trigger('change'); 267 } 268 269 $(this).blur(); 270 }; 271 272 options.beforeShow = function (input) { 273 setTimeout(function () { 274 $(input).datepicker("widget").find(".ui-datepicker-current").hide(); 275 }, 276 1); 277 }; 278 279 $(element).datepicker(options); 280 281 //handle the field changing 282 ko.utils.registerEventHandler(element, "change", function () { 283 var observable = valueAccessor(); 284 285 //check that they haven't pasted rubbish into the box 286 try { 287 $.datepicker.parseDate(options.dateFormat, $(element).val()); 288 } catch (e) { 289 $(element).val(''); 290 observable(null); 291 return; 292 } 293 294 var selectedDate = $(element).datepicker("getDate"); 295 if (selectedDate === undefined || selectedDate === null) { 296 observable(null); 297 } else { 298 var newEpoch = selectedDate.getTime(); 299 observable(new Date(newEpoch - timezoneOffset)); 300 } 301 }); 302 303 var observable = valueAccessor(); 304 var value = ko.utils.unwrapObservable(observable); 305 if (String(value).indexOf('/Date(') == 0) { 306 var coercedDate = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1"))); 307 observable(coercedDate); 308 } 309 310 ko.utils.domNodeDisposal.addDisposeCallback(element, function () { 311 $(element).datepicker("destroy"); 312 }); 313 314 }, 315 update: function (element, valueAccessor) { 316 var timezoneOffset = new Date().getTimezoneOffset() * 60000; 317 var newValue = ko.utils.unwrapObservable(valueAccessor()); 318 var oldValue = $(element).datepicker("getDate"); 319 320 var newEpoch; 321 var oldEpoch; 322 323 if (newValue === undefined || newValue === null) { 324 newEpoch = null; 325 } 326 else if (String(newValue).indexOf('/Date(') == 0) { 327 //handle date data coming via json from Microsoft 328 newEpoch = parseInt(newValue.replace(/\/Date\((.*?)\)\//gi, "$1")); 329 } 330 else if (newValue instanceof Date) { 331 newEpoch = newValue.getTime(); 332 } 333 334 if (oldValue === undefined || oldValue === null) { 335 oldEpoch = null; 336 } 337 else if (String(oldValue).indexOf('/Date(') == 0) { 338 //handle date data coming via json from Microsoft 339 oldEpoch = parseInt(oldValue.replace(/\/Date\((.*?)\)\//gi, "$1")); 340 } 341 else if (oldValue instanceof Date) { 342 oldEpoch = oldValue.getTime(); 343 } 344 345 if (newEpoch !== oldEpoch) { 346 if (newEpoch + timezoneOffset !== oldEpoch) { 347 var date = new Date(newEpoch + timezoneOffset); 348 $(element).datepicker("setDate", date); 349 } 350 351 } else if (newEpoch == null) { 352 $(element).datepicker("setDate", null); 353 } 354 } 355}; 356ko.bindingHandlers.uniqueId = { 357 init: function (element, valueAccessor, allBindings, viewModel, bindingContext) { 358 var value = valueAccessor();
359 var thisPrefix = value || ko.bindingHandlers.uniqueId.prefix; 360 //var options = { prefix: thisPrefix }; 361 var uniqueId = thisPrefix + (++ko.bindingHandlers.uniqueId.counter); 362 element.id = uniqueId; 363 viewModel.generatedUniqueId = uniqueId; 364 }, 365 counter: 0, 366 prefix: "unique" 367}; 368ko.bindingHandlers.uniqueFor = { 369 init: function (element, valueAccessor, allBindingsAccessor) { 370 var value = valueAccessor(); 371 var thisPrefix = value || ko.bindingHandlers.uniqueId.prefix; 372 //var options = allBindingsAccessor().uniqueIdOptions || { prefix: thisPrefix }; 373 374 var uniqueId = thisPrefix + (ko.bindingHandlers.uniqueId.counter + 1); 375 element.setAttribute("for", uniqueId); 376 } 377}; 378ko.bindingHandlers.pickListValue = { 379 update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { 380 var value = valueAccessor(), 381 allBindings = allBindingsAccessor(), 382 dataModel = bindingContext.$parents[bindingContext.$parents.length - 2].DataModel; 383 var pickListItemId = ko.utils.unwrapObservable(value); 384 if (pickListItemId != null && dataModel.PickLists[allBindings.pickListName] && dataModel.PickLists[allBindings.pickListName][pickListItemId]) { 385 $(element).text(dataModel.PickLists[allBindings.pickListName][pickListItemId]()); 386 } else { 387 var nullText = allBindings.nullText || ''; 388 $(element).text(nullText); 389 } 390 } 391}; 392ko.bindingHandlers.expand = { 393 init: function (element, valueAccessor, allBindingsAccessor, viewModel) { 394 if ($(element).hasClass('ui-expander')) { 395 var expander = element; 396 var head = $(expander).find('.ui-expander-head'); 397 var content = $(expander).find('.ui-expander-content'); 398 399 $(head).click(function () { 400 $(head).toggleClass('ui-expander-head-collapsed'); 401 $(content).toggle(); 402 }); 403 } 404 } 405}; 406ko.bindingHandlers.attrIf = { 407 update: function (element, valueAccessor, allBindingsAccessor) { 408 var h = ko.utils.unwrapObservable(valueAccessor()); 409 var show = ko.utils.unwrapObservable(h._if); 410 if (show) { 411 ko.bindingHandlers.attr.update(element, valueAccessor, allBindingsAccessor); 412 } else { 413 for (var k in h) { 414 if (h.hasOwnProperty(k) && k.indexOf("_") !== 0) { 415 $(element).removeAttr(k); 416 } 417 } 418 } 419 } 420}; 421ko.bindingHandlers.singleClick = { 422 init: function (element, valueAccessor) { 423 var handler = valueAccessor(), 424 delay = 200, 425 clickTimeout = false; 426 427 $(element).click(function () { 428 if (clickTimeout !== false) { 429 clearTimeout(clickTimeout); 430 clickTimeout = false; 431 } else { 432 clickTimeout = setTimeout(function () { 433 clickTimeout = false; 434 handler(); 435 }, delay); 436 } 437 }); 438 } 439}; 440ko.bindingHandlers.tooltip = { 441 init: function (element, valueAccessor) { 442 var local = ko.utils.unwrapObservable(valueAccessor()), 443 options = {}; 444 445 ko.utils.extend(options, ko.bindingHandlers.tooltip.options); 446 ko.utils.extend(options, local); 447 448 $(element).tooltip(options); 449 450 ko.utils.domNodeDisposal.addDisposeCallback(element, function () { 451 $(element).tooltip("destroy"); 452 }); 453 }, 454 options: { 455 placement: "right", 456 trigger: "click" 457 } 458}; 459//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. 460//Usage: data-bind="checked: observable, toggle: { on: 'Yes', off: 'No', onstyle: 'success', offstyle: 'danger', size: 'normal', style: 'cssclass' }" 461ko.bindingHandlers.toggle = { 462 init: function (element, valueAccessor, allBindings) { 463 if (!$.fn.bootstrapToggle) { return; } 464 var params = ko.unwrap(valueAccessor()); 465 var on = ko.unwrap(params['on']) || 'On'; 466 var off = ko.unwrap(params['off']) || 'Off'; 467 var onstyle = ko.unwrap(params['onstyle']) || 'primary'; 468 var offstyle = ko.unwrap(params['offstyle']) || 'default'; 469 var size = ko.unwrap(params['size']) || 'normal'; 470 var style = ko.unwrap(params['style']) || ''; 471 $(element).bootstrapToggle({ on: on, off: off, onstyle: onstyle, offstyle: offstyle, size: size, style: style }); 472 $(element).change(function () { $(element).triggerHandler('click'); }); //trigger update to observable 473 } 474}; 475ko.bindingHandlers.autocomplete = { 476 init: function (element, valueAccessor, allBindings) { 477 var options = allBindings()['autocompleteoptions']; 478 var value = valueAccessor(); 479 $(element).val(value()); 480 $(element).autocomplete({ 481 source: ko.unwrap(options), 482 select: function (event, ui) { 483 var value = valueAccessor(); 484 if (ui.item) value(ui.item.value); 485 } 486 }).blur(function () { 487 var value = valueAccessor(); 488 value($(element).val()); 489 }); 490 } 491}; 492 493ko.bindingHandlers.slick = { 494 init: function (element, valueAccessor) { 495 var local = ko.utils.unwrapObservable(valueAccessor()), 496 options = {}; 497 498 ko.utils.extend(options, ko.bindingHandlers.slick.options); 499 ko.utils.extend(options, local); 500 501 $(element).slick(options); 502 }, 503 options: { 504 slidesToShow: 3, 505 slidesToScroll: 1, 506 responsive: [ 507 { 508 breakpoint: 1024, 509 settings: { 510 slidesToShow: 3, 511 slidesToScroll: 3, 512 infinite: true, 513 dots: true 514 } 515 }, 516 { 517 breakpoint: 600, 518 settings: { 519 slidesToShow: 2, 520 slidesToScroll: 2 521 } 522 }, 523 { 524 breakpoint: 480, 525 settings: { 526 slidesToShow: 1, 527 slidesToScroll: 1 528 } 529 } 530 ] 531 } 532}; 533if (!Function.prototype.bind) { 534 Function.prototype.bind = function (oThis) { 535 if (typeof this !== "function") { 536 // closest thing possible to the ECMAScript 5 internal IsCallable function 537 throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); 538 } 539 var aArgs = Array.prototype.slice.call(arguments, 1), 540 fToBind = this, 541 fNOP = function () { }, 542 fBound = function () { 543 return fToBind.apply(this instanceof fNOP && oThis 544 ? this 545 : oThis, 546 aArgs.concat(Array.prototype.slice.call(arguments))); 547 }; 548 fNOP.prototype = this.prototype; 549 fBound.prototype = new fNOP(); 550 return fBound; 551 }; 552} 553 554ko.bindingHandlers.comboTree = { 555 //On iniliatise we need to create our tree 556 init: function (element, valueAccessor, allBindingsAccessor) { 557 558 //initialize combotree with some optional options 559 var options = allBindingsAccessor().treeOptions; 560 561 if (options == undefined) { 562 options = {}; 563 } 564 565 options.source = ko.toJS(ko.unwrap(valueAccessor())); 566 567 SubjectTree = $(element).comboTree(options); 568 569 ko.utils.registerEventHandler(element, "change", function (e) { 570 var observable = allBindingsAccessor().treeValue; 571 572 if (e.originalEvent.detail !== undefined) { 573 observable(e.originalEvent.detail); 574 } 575 else { 576 observable(null); 577 } 578 579 }); 580 581 }, 582 update: function (element, valueAccessor, allBindingsAccessor) { 583 var subjectId = ko.unwrap(allBindingsAccessor().treeValue()); 584 if (subjectId !== undefined && subjectId != null) { 585 586 var tree = valueAccessor(); 587 var flattenedTree = FlattenSubjects([], tree); 588 589 for (var i = 0; i < flattenedTree.length; i++) { 590 if (flattenedTree[i].id == subjectId) { 591 element.value = flattenedTree[i].title; 592 break; 593 } 594 } 595 } else { 596 element.value = ""; 597 } 598 } 599}; 600 601function FlattenSubjects(flattenedTree, tree) { 602 for (var i = 0; i < tree.length; i++) { 603 flattenedTree.push({ id: tree[i].id, title: tree[i].title }); 604 if (tree[i].subs != null && tree[i].subs.length > 0) { 605 flattenedTree = FlattenSubjects(flattenedTree, tree[i].subs); 606 } 607 } 608 return flattenedTree; 609}
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.