Created
July 27, 2016 11:37
-
-
Save Manuel-S/bb4eb7336ac2e4ad88f6a3de193540d5 to your computer and use it in GitHub Desktop.
extended binding syntax and a few custom bindings
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // adds a jquery plugin function to apply ko bindings to a jquery DOM set | |
| $.fn.applyBindings = function (viewModel) { | |
| return this.each(function () { | |
| ko.applyBindings(viewModel, this); | |
| }); | |
| } | |
| $.fn.koClean = function () { | |
| return this.each(function () { | |
| ko.cleanNode(this); | |
| }); | |
| }; | |
| ko.subscribable.fn.subscribeOnce = function (callback, target, event) { | |
| var subscription; | |
| subscription = this.subscribe(function (value) { | |
| if (subscription && subscription.dispose) { | |
| subscription.dispose(); | |
| } | |
| callback.call(target, value); | |
| }, null, event); | |
| }; | |
| /* --- Binding Handlers --- | |
| */ | |
| // hides children while a boolean is set to true and displays a loading icon instead | |
| ko.bindingHandlers['loadingWhen'] = { | |
| init: function (element) { | |
| var | |
| $element = $(element), | |
| currentPosition = $element.css("position"), | |
| $loader = $("<div><span class=\"ui-button-text\"><img src=\""+g_strAppPrefix+"/Images/gearloader.gif\" style=\"height:18px;width:18px;\"/></span></div>") | |
| .addClass('loader').hide(); | |
| //add the loader | |
| $element.append($loader); | |
| //make sure that we can absolutely position the loader against the original element | |
| if (currentPosition == "auto" || currentPosition == "static") | |
| $element.css("position", "relative"); | |
| //center the loader | |
| $loader.css({ | |
| position: "absolute", | |
| top: "50%", | |
| left: "50%", | |
| "margin-left": -($loader.width() / 2) + "px", | |
| "margin-top": -($loader.height() / 2) + "px" | |
| }); | |
| }, | |
| update: function (element, valueAccessor) { | |
| var isLoading = ko.utils.unwrapObservable(valueAccessor()), | |
| $element = $(element), | |
| $childrenToHide = $element.children(":not(div.loader)"), | |
| $loader = $element.find("div.loader"); | |
| if (isLoading) { | |
| $childrenToHide.css("visibility", "hidden").attr("disabled", "disabled"); | |
| $loader.show(); | |
| } | |
| else { | |
| $loader.hide(); | |
| $childrenToHide.css("visibility", "visible").removeAttr("disabled"); | |
| } | |
| } | |
| }; | |
| // binding handler that iterates over all properties of an object like a dictionary and provides key/value variables for the binding engine | |
| ko.bindingHandlers['keyvalue'] = { | |
| makeTemplateValueAccessor: function (valueAccessor, sortCallback) { | |
| return function () { | |
| var values = ko.unwrap(valueAccessor()); | |
| var array = []; | |
| for (var key in values) | |
| if (values.hasOwnProperty(key)) | |
| array.push({ key: key, value: values[key] }); | |
| if (typeof sortCallback === 'function') { | |
| array.sort(sortCallback); | |
| } | |
| return array; | |
| }; | |
| }, | |
| 'init': function (element, valueAccessor, allBindings, viewModel, bindingContext) { | |
| return ko.bindingHandlers['foreach']['init'](element, ko.bindingHandlers['keyvalue'].makeTemplateValueAccessor(valueAccessor, allBindings().sortCallback)); | |
| }, | |
| 'update': function (element, valueAccessor, allBindings, viewModel, bindingContext) { | |
| return ko.bindingHandlers['foreach']['update'](element, | |
| ko.bindingHandlers['keyvalue'].makeTemplateValueAccessor(valueAccessor, allBindings().sortCallback), allBindings, viewModel, bindingContext); | |
| } | |
| }; | |
| /* Function.name polyfill */ | |
| (function () { | |
| var fnNamePrefixRegex = /^[\S\s]*?function\s*/; | |
| var fnNameSuffixRegex = /[\s\(\/][\S\s]+$/; | |
| function _name() { | |
| var name = ""; | |
| if (this === Function || this === Function.prototype.constructor) { | |
| name = "Function"; | |
| } | |
| else if (this !== Function.prototype) { | |
| name = ("" + this).replace(fnNamePrefixRegex, "").replace(fnNameSuffixRegex, ""); | |
| } | |
| return name; | |
| } | |
| // Inspect the polyfill-ability of this browser | |
| var needsPolyfill = !("name" in Function.prototype && "name" in (function x() { })); | |
| var canDefineProp = typeof Object.defineProperty === "function" && | |
| (function () { | |
| var result; | |
| try { | |
| Object.defineProperty(Function.prototype, "_xyz", { | |
| get: function () { | |
| return "blah"; | |
| }, | |
| configurable: true | |
| }); | |
| result = Function.prototype._xyz === "blah"; | |
| delete Function.prototype._xyz; | |
| } | |
| catch (e) { | |
| result = false; | |
| } | |
| return result; | |
| })(); | |
| // Add the "private" property for testing, even if the real property can be polyfilled | |
| //Function.prototype._name = _name; | |
| // Polyfill it! | |
| if (canDefineProp && needsPolyfill) { | |
| Object.defineProperty(Function.prototype, "name", { | |
| get: _name | |
| }); | |
| } | |
| })(); | |
| String.prototype.matchAll = String.prototype.matchAll || function (regexp) { | |
| var matches = []; | |
| this.replace(regexp, function () { | |
| var arr = ([]).slice.call(arguments, 0); | |
| var extras = arr.splice(-2); | |
| arr.index = extras[0]; | |
| arr.input = extras[1]; | |
| matches.push(arr); | |
| }); | |
| return matches.length ? matches : null; | |
| }; | |
| /* --- String interpolating binding provider --- | |
| Allows {{ expr }} and ${ expr } syntax in DOM. | |
| Also allows attribute.bind="property" attribute bindings. | |
| */ | |
| var StringInterpolatingBindingProvider = function () { | |
| function serialize(obj) { | |
| var str = ""; | |
| var names = Object.getOwnPropertyNames(obj); | |
| var length = names.length; | |
| for (let i = 0; i < length; i++) { | |
| var prop = names[i]; | |
| var value = obj[prop]; | |
| str += "'" + prop + "': " + value; | |
| if (i < (length - 1)) { | |
| str += ", "; | |
| } | |
| } | |
| return "{" + str + "}"; | |
| } | |
| this.constructor = StringInterpolatingBindingProvider; | |
| var expressionRegex = /(?:{{([\s\S]+?)}}|\${([^}]*)})/g; | |
| this.preprocessNode = function (node) { | |
| if (node.nodeType === node.TEXT_NODE && node.nodeValue) { | |
| // Preprocess by replacing {{ expr }} with <!-- ko text: expr --><!-- /ko --> nodes | |
| var newNodes = replaceExpressionsInText(node.nodeValue, expressionRegex, function (expressionText) { | |
| return [ | |
| document.createComment("ko text:" + expressionText), | |
| document.createComment("/ko") | |
| ]; | |
| }); | |
| // Insert the resulting nodes into the DOM and remove the original unpreprocessed node | |
| if (newNodes) { | |
| for (var i = 0; i < newNodes.length; i++) { | |
| node.parentNode.insertBefore(newNodes[i], node); | |
| } | |
| node.parentNode.removeChild(node); | |
| return newNodes; | |
| } | |
| } | |
| if (node.nodeType === node.ELEMENT_NODE) { | |
| var attrs = {}; | |
| var attributes = Array.prototype.slice.call(node.attributes); | |
| for (var i = 0; i < attributes.length; i++) { | |
| var attribute = attributes[i]; | |
| var localName = attribute.localName; | |
| var value = attribute.value; | |
| if (!localName.endsWith(".bind") || localName === "data-bind") | |
| continue; | |
| node.removeAttribute(localName); | |
| localName = localName.substr(0, localName.length - 5); | |
| attrs[localName] = value; | |
| } | |
| if (Object.getOwnPropertyNames(attrs).length > 0) { | |
| var previousBindings = node.getAttribute("data-bind"); | |
| node.setAttribute("data-bind", (previousBindings ? previousBindings + ', ' : '') + 'attr:' + serialize(attrs)); | |
| return [node]; | |
| } | |
| } | |
| }; | |
| function replaceExpressionsInText(text, expressionRegex, callback) { | |
| var prevIndex = expressionRegex.lastIndex = 0, | |
| resultNodes = null, | |
| match; | |
| // Find each expression marker, and for each one, invoke the callback | |
| // to get an array of nodes that should replace that part of the text | |
| while (match = expressionRegex.exec(text)) { | |
| var leadingText = text.substring(prevIndex, match.index); | |
| prevIndex = expressionRegex.lastIndex; | |
| resultNodes = resultNodes || []; | |
| // Preserve leading text | |
| if (leadingText) { | |
| resultNodes.push(document.createTextNode(leadingText)); | |
| } | |
| resultNodes.push.apply(resultNodes, callback(match[2] || match[1])); | |
| } | |
| // Preserve trailing text | |
| var trailingText = text.substring(prevIndex); | |
| if (resultNodes && trailingText) { | |
| resultNodes.push(document.createTextNode(trailingText)); | |
| } | |
| return resultNodes; | |
| }; | |
| }; | |
| StringInterpolatingBindingProvider.prototype = ko.bindingProvider.instance; | |
| // set as default binding provider | |
| ko.bindingProvider.instance = new StringInterpolatingBindingProvider(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment