PageSourceSearch

https://members.cla.org.uk/Resources/Shared/Scripts/knockoutExtensions.js?cdv=1703

js cla.org.uk collected 2026-10-01 10:42:24 UTC 23,851 bytes, 609 lines download raw bytes

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.