{"id":11403,"date":"2016-04-13T13:18:48","date_gmt":"2016-04-13T18:18:48","guid":{"rendered":"http:\/\/jianmingli.com\/wp\/?p=11403"},"modified":"2016-07-11T15:01:10","modified_gmt":"2016-07-11T20:01:10","slug":"knockout-js","status":"publish","type":"post","link":"https:\/\/jianmingli.com\/wp\/?p=11403","title":{"rendered":"Knockout.js"},"content":{"rendered":"<div class='toc wptoc'>\n<h2>Contents<\/h2>\n<ol class='toc-odd level-1'>\n\t<li>\n\t\t<a href=\"#Overview\">Overview<\/a>\n\t<\/li>\n\t<li>\n\t\t<a href=\"#Data_Bindings\">Data Bindings<\/a>\n\t\t<ol class='toc-even level-2'>\n\t\t\t<li>\n\t\t\t\t<a href=\"#Data_Binding_Syntax\">Data Binding Syntax<\/a>\n\t\t\t<\/li>\n\t\t\t<li>\n\t\t\t\t<a href=\"#Binding_Multiple_View_Models_on_a_Single_Page\">Binding Multiple View Models on a Single Page<\/a>\n\t\t\t<\/li>\n\t\t\t<li>\n\t\t\t\t<a href=\"#Text_text:_Binding\">Text (<em>text:<\/em>) Binding<\/a>\n\t\t\t<\/li>\n\t\t\t<li>\n\t\t\t\t<a href=\"#HTML_html:_Binding\">HTML (<em>html:<\/em>) Binding<\/a>\n\t\t\t<\/li>\n\t\t\t<li>\n\t\t\t\t<a href=\"#CSS_css:_and_Style_style:_Binding\">CSS (<em>css:<\/em>) and Style (<em>style:<\/em>) Binding<\/a>\n\t\t\t<\/li>\n\t\t\t<li>\n\t\t\t\t<a href=\"#Attribute_attr:_Binding\">Attribute (<em>attr:<\/em>) Binding<\/a>\n\t\t\t<\/li>\n\t\t\t<li>\n\t\t\t\t<a href=\"#Conditional_if:_and_ifnot:_Binding\">Conditional (<em>if: <\/em>and <em>ifnot:<\/em>) Binding<\/a>\n\t\t\t<\/li>\n\t\t\t<li>\n\t\t\t\t<a href=\"#foreach:_Binding\"><em>foreach: <\/em>Binding<\/a>\n\t\t\t<\/li>\n\t\t\t<li>\n\t\t\t\t<a href=\"#with:_Binding\"><em>with: <\/em>Binding<\/a>\n\t\t\t<\/li>\n\t\t\t<li>\n\t\t\t\t<a href=\"#template:_Binding\"><em>template:<\/em> Binding<\/a>\n\t\t\t<\/li>\n\t\t\t<li>\n\t\t\t\t<a href=\"#Form_Data_Binding\">Form Data Binding<\/a>\n\t\t\t<\/li>\n\t\t\t<li>\n\t\t\t\t<a href=\"#Event_Data_Binding\">Event Data Binding<\/a>\n\t\t\t<\/li>\n\t\t<\/ol>\n\t<li>\n\t\t<a href=\"#Form_Processing\">Form Processing<\/a>\n\t\t<ol class='toc-even level-2'>\n\t\t\t<li>\n\t\t\t\t<a href=\"#Form_Validation\">Form Validation<\/a>\n\t\t\t<\/li>\n\t\t\t<li>\n\t\t\t\t<a href=\"#Form_Posting_with_AJAX\">Form Posting with AJAX<\/a>\n\t\t\t<\/li>\n\t\t<\/ol>\n\t<li>\n\t\t<a href=\"#Variables\">Variables<\/a>\n\t\t<ol class='toc-even level-2'>\n\t\t\t<li>\n\t\t\t\t<a href=\"#Built-in_Variables\">Built-in Variables<\/a>\n\t\t\t<\/li>\n\t\t\t<li>\n\t\t\t\t<a href=\"#Observables_Variables\"><em>Observables <\/em>Variables<\/a>\n\t\t\t\t<ol class='toc-odd level-3'>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<a href=\"#Define_Observables\">Define Observables<\/a>\n\t\t\t\t\t<\/li>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<a href=\"#Access_Observables\">Access Observables<\/a>\n\t\t\t\t\t<\/li>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<a href=\"#Extend_Observables\">Extend Observables<\/a>\n\t\t\t\t\t<\/li>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<a href=\"#Add_Custom_Functions_to_Observables\">Add Custom Functions to Observables<\/a>\n\t\t\t\t\t<\/li>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<a href=\"#Limit_Notification_Rate_for_Observables\">Limit Notification Rate for Observables<\/a>\n\t\t\t\t\t<\/li>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<a href=\"#Subscribe_to_Observable_Events\">Subscribe to Observable Events<\/a>\n\t\t\t\t\t<\/li>\n\t\t\t\t<\/ol>\n<\/ol>\n\t\t\t<li>\n\t\t\t\t<a href=\"#Knowout_Mapping_Plugin\">Knowout Mapping Plugin<\/a>\n\t\t\t<\/li>\n\t\t\t<li>\n\t\t\t\t<a href=\"#Examples\">Examples<\/a>\n\t\t\t<\/li>\n\t\t\t<li>\n\t\t\t\t<a href=\"#References\">References<\/a>\n\t\t\t<\/li>\n<\/ol>\n<\/ol>\n<\/div>\n<div class='wptoc-end'>&nbsp;<\/div>\n<span id=\"Overview\"><h2>Overview<\/h2><\/span>\n<p>* Open source JavaScript library.<br \/>\n* Light weight, no other dependencies.<br \/>\n* Implements <em>MVVM <\/em>pattern (<em>Model-View-ViewModel<\/em>)<br \/>\n&#8211; Model: binds view model to view, e.g.:<\/p>\n<pre lang=\"javascript\">\r\n        ko.applyBindings(viewModel);\r\n<\/pre>\n<p>&#8211; View: contains HTML\/CSS elements for data to bind to, e.g.<\/p>\n<pre lang=\"html\">\r\n    <span id=\"Hello_\"><h1>Hello <span data-bind=\"text: name\"><\/span>!<\/h1><\/span>\r\n<\/pre>\n<p>&#8211; View Model: contains data to be bound to the view, e.g.<\/p>\n<pre lang=\"html\">\r\n        function ViewModel() {\r\n            this.name = 'John Doe';\r\n        };\r\n        var vm = new ViewModel();\r\n<\/pre>\n<p>* Framework can be extended using Knockout plugins.<\/p>\n<span id=\"Data_Bindings\"><h2>Data Bindings<\/h2><\/span>\n<span id=\"Data_Binding_Syntax\"><h3>Data Binding Syntax<\/h3><\/span>\n<p>* Data binding is achieved by using HTML attribute: <strong><em>data-bind<\/em><\/strong><\/p>\n<pre lang=\"xml\">\r\n    <span id=\"Hello__1\"><h1>Hello <span data-bind=\"text: name\"><\/span>!<\/h1><\/span>\r\n<\/pre>\n<p>* Data binding can also be done with:<\/p>\n<pre lang=\"xml\">\r\n<!-- ko -->\r\n<!-- \/ko -->\r\n<\/pre>\n<p>e.g.<\/p>\n<pre lang=\"xml\">\r\n    <!-- ko foreach: books --> \r\n        <li data-bind=\"text: $data\"><\/li> \r\n    <!-- \/ko --> \r\n<\/pre>\n<p>* Can bind almost any:<br \/>\n&#8211; HTML attribute<br \/>\n&#8211; CSS class<br \/>\n&#8211; CSS style<\/p>\n<span id=\"Binding_Multiple_View_Models_on_a_Single_Page\"><h3>Binding Multiple View Models on a Single Page<\/h3><\/span>\n<p>* You can bind different view models to different elements on a single page.<br \/>\n* Syntax:<\/p>\n<pre lang=\"javascript\">\r\n    <div id=\"greeting1\">\r\n        <h1 data-bind=\"text: greetings()\"><\/h1>\r\n    <\/div>\r\n    <div id=\"greeting2\">\r\n        <h1 data-bind=\"text: greetings()\"><\/h1>\r\n    <\/div>\r\n\r\n    <script>\r\n        var vm1 = new Greeting('John');\r\n        ko.applyBindings(vm1, document.getElementById('greeting1'));\r\n\r\n        var vm2 = new Greeting('Jane');\r\n        ko.applyBindings(vm2, document.getElementById('greeting2'));\r\n    <\/script>\r\n<\/pre>\n<span id=\"Text_text:_Binding\"><h3>Text (<em>text:<\/em>) Binding<\/h3><\/span>\n<pre lang=\"xml\">\r\n    <span id=\"Hello__2\"><h1>Hello <span data-bind=\"text: name\"><\/span>!<\/h1><\/span>\r\n<\/pre>\n<span id=\"HTML_html:_Binding\"><h3>HTML (<em>html:<\/em>) Binding<\/h3><\/span>\n<pre lang=\"xml\">\r\n    <span id=\"Hello__with_HTML_binding\"><h1>Hello <span data-bind=\"html: getName()\"><\/span> with HTML binding!<\/h1><\/span>\r\n<\/pre>\n<span id=\"CSS_css:_and_Style_style:_Binding\"><h3>CSS (<em>css:<\/em>) and Style (<em>style:<\/em>) Binding<\/h3><\/span>\n<pre lang=\"xml\">\r\n    <p data-bind=\"\r\n        style: { marginBottom: 0, paddingBottom: '1em' },\r\n        css: 'myClass'\">\r\n        Hello css binding!.\r\n    <\/p>\r\n\r\n    <div class=\"myHeader\"\r\n         data-bind=\"css: {myHeaderOne: $data.isHeaderOne, myHeaderTwo: $data.isHeaderTwo}\">\r\n<\/pre>\n<span id=\"Attribute_attr:_Binding\"><h3>Attribute (<em>attr:<\/em>) Binding<\/h3><\/span>\n<pre lang=\"xml\">\r\n    <p data-bind=\"attr: { id: 'myId' }\">\r\n        Hello attribute binding.\r\n    <\/p>\r\n<\/pre>\n<span id=\"Conditional_if:_and_ifnot:_Binding\"><h3>Conditional (<em>if: <\/em>and <em>ifnot:<\/em>) Binding<\/h3><\/span>\n<p>* if binding:<\/p>\n<pre lang=\"xml\">\r\n    <div id=\"summary\" class=\"section panel panel-primary\"\r\n         data-bind=\"if: model.displaySummary\">\r\n    <\/div>\r\n<\/pre>\n<p>* ifnot binding:<\/p>\n<pre lang=\"xml\">\r\n    <div id=\"editor\" class=\"section panel panel-primary\"\r\n         data-bind=\"ifnot: model.displaySummary\">\r\n    <\/div>\r\n<\/pre>\n<p>* Tertiary expression<\/p>\n<pre lang=\"xml\">\r\n<button type=\"submit\" data-bind=\"text: (!hasName()) ? 'Create' : 'Update'\">\r\n<\/pre>\n<span id=\"foreach:_Binding\"><h3><em>foreach: <\/em>Binding<\/h3><\/span>\n<p>* Works with multiple objects and repeats the HTML in the binding.<br \/>\n* Example:<\/p>\n<pre lang=\"xml\">\r\n    <ul>\r\n        <!-- ko foreach: friends -->\r\n        <li data-bind=\"text: $data\"><\/li>\r\n        <!-- \/ko-->\r\n    <\/ul>\r\n<\/pre>\n<p>* foreach binding callback events:<br \/>\n&#8211; afterRender<br \/>\n&#8211; adterAdd<br \/>\n&#8211; beforeRemvoe<br \/>\n&#8211; beforeMove<br \/>\n&#8211; afterMove<\/p>\n<span id=\"with:_Binding\"><h3><em>with: <\/em>Binding<\/h3><\/span>\n<p>* Similar to <em>foreach <\/em>but:<br \/>\n&#8211; works with a single object with multiple properties<br \/>\n* Example:<\/p>\n<pre lang=\"xml\">\r\n    <div id=\"address\" data-bind=\"with: address\">\r\n        <p data-bind=\"text: street\"><\/p>\r\n        <p data-bind=\"text: zip\"><\/p>\r\n        <p data-bind=\"text: state\"><\/p>\r\n    <\/div> \r\n<\/pre>\n<span id=\"template:_Binding\"><h3><em>template:<\/em> Binding<\/h3><\/span>\n<p>* Use <em>template <\/em>binding to reuse templates.<\/p>\n<pre lang=\"xml\">\r\n<div data-bind=\"template: {name: 'greeting-template2', foreach: greetings}\">\r\n<script type=\"text\/html\" id=\"greeting-template2\">\r\n    <span data-bind=\"text: greet\"><\/span> \r\n    <span data-bind=\"text: solute\"><\/span> \r\n    <span data-bind=\"text: $parent.name\"><\/span><br\/>\r\n<\/script>\r\n<\/pre>\n<span id=\"Form_Data_Binding\"><h3>Form Data Binding<\/h3><\/span>\n<p>* <em>value <\/em>: used with input, select and textarea<br \/>\n&#8211; observables updated when field value changes<br \/>\n* <em>textInput <\/em>: used with input and textarea<br \/>\n&#8211; observables updated when field loses focus<br \/>\n* <em>checked <\/em>: used with checkboxes and radio buttons<br \/>\n* <em>options <\/em>: used with select<br \/>\n* <em>selectedOptions <\/em>: used with the multiple select list<br \/>\n* <em>enabled <\/em>and <em>disable <\/em>: enable\/disable whole form<\/p>\n<span id=\"Event_Data_Binding\"><h3>Event Data Binding<\/h3><\/span>\n<p>* <em>submit<\/em><br \/>\n* <em>click<\/em><br \/>\n* <em>hasFocus<\/em><br \/>\n* <em>event<\/em><br \/>\n* For example:<\/p>\n<pre lang=\"xml\">\r\n    <textarea data-bind=\"value: myText, event: { mouseover: clearText() }\"><\/textarea>\r\n<\/pre>\n<span id=\"Form_Processing\"><h2>Form Processing<\/h2><\/span>\n<span id=\"Form_Validation\"><h3>Form Validation<\/h3><\/span>\n<p>* Use <em>jQuery validate<\/em> plugin<\/p>\n<pre lang=\"bash\">\r\nInstall-Package jQuery.Validation\r\n<\/pre>\n<span id=\"Form_Posting_with_AJAX\"><h3>Form Posting with AJAX<\/h3><\/span>\n<p>* Form data can be posted via Ajax using either JavaScript or JSON formats:<\/p>\n<pre lang=\"javascript\">\r\n    data: ko.toJS(self.person),\r\n    data: ko.toJSON(self.person),\r\n<\/pre>\n<span id=\"Variables\"><h2>Variables<\/h2><\/span>\n<span id=\"Built-in_Variables\"><h3>Built-in Variables<\/h3><\/span>\n<p><em>$root<\/em><br \/>\n<em>$parent<\/em><br \/>\n<em>$parents<\/em><br \/>\n<em>$data<\/em><br \/>\n<em>$index<\/em><\/p>\n<span id=\"Observables_Variables\"><h3><em>Observables <\/em>Variables<\/h3><\/span>\n<p>* Used to handle dynamically changing properties<br \/>\n* Don&#8217;t use for static non-changing values as observables have performance costs<\/p>\n<span id=\"Define_Observables\"><h4>Define Observables<\/h4><\/span>\n<pre lang=\"javascript\">\r\n\/\/ observable\r\nvar myobs1 = ko.observable();\r\n\r\n\/\/ observable array\r\nvar myobs2 = ko.observableArray([]);\r\n\r\n\/\/ computed observable\r\nvar myobs3 = ko.computed(function() {\r\n  return \/\/computed value here\r\n});\r\n\r\n\/\/ pureComputed observables (for version 3.2 and later)\r\n\/\/ lazy instantiation to improve performance\r\nvar myobs4 = ko.pureComputed(function() {\r\n  return \/\/computed value here\r\n});\r\n<\/pre>\n<span id=\"Access_Observables\"><h4>Access Observables<\/h4><\/span>\n<p>* In JavaScript, Observables must be accessed like a function, i.e. with <strong>()<\/strong>.<\/p>\n<pre lang=\"javascript\">\r\nmyobs1('Hello');\r\nalert(myobs1());\r\n\r\nmyobs2.push('Hello');\r\nalert(myobs2());\r\n<\/pre>\n<p>* Observables can <em>also<\/em> be access by name in <em>data-bind<\/em> attributes (Knockout converts it to function call).<\/p>\n<pre lang=\"xml\">\r\n        <span id=\"Hello__3\"><h1>Hello <span data-bind=\"text: computedFullName\"><\/span>!<\/h1><\/span>\r\n<\/pre>\n<span id=\"Extend_Observables\"><h4>Extend Observables<\/h4><\/span>\n<p>* Obersables can be extended, e.g.:<\/p>\n<pre lang=\"javascript\">\r\n        ko.extenders.maxCharacters = function (target, max) {\r\n            var result = ko.computed({\r\n                read: target,\r\n                write: function (newValue) {\r\n                    alert(newValue);\r\n                    var current = target();\r\n                    if (newValue.length <= max) {\r\n                        target(newValue);\r\n                    } else {\r\n                        target(current);\r\n                        target.notifySubscribers(current);\r\n                    };\r\n                }\r\n            }).extend({ notify: 'always' });\r\n\r\n            return result;\r\n        };\r\n<\/pre>\n<span id=\"Add_Custom_Functions_to_Observables\"><h4>Add Custom Functions to Observables<\/h4><\/span>\n<p>* You can also add custom functions to observables, e.g.:<\/p>\n<pre lang=\"javascript\">\r\n        ko.observableArray.fn.booksOwned = function (property, value) {\r\n            return ko.computed(function () {\r\n                var allItems = this();\r\n                var machingItems = [];\r\n\r\n                for (var i = 0; i < allItems.length; i++) {\r\n                    var current = allItems[i];\r\n                    if (ko.unwrap(current[property]) === value) {\r\n                        machingItems.push(current);\r\n                    };\r\n                };\r\n\r\n                return machingItems;\r\n            }, this);\r\n        };\r\n\r\n<\/pre>\n<span id=\"Limit_Notification_Rate_for_Observables\"><h4>Limit Notification Rate for Observables<\/h4><\/span>\n<p>* You can also limit how often observable notifies subscribers:<\/p>\n<pre lang=\"xml\">\r\nmyObservable.extend({ rateLimit: 1000 });\r\n\r\nmyObservable.extend( {\r\n    rateLimit: {\r\n\t    timeout: 1000,\r\n\t    method: \"notifyWhenChangesStop\"\r\n    }\r\n});\r\n<\/pre>\n<span id=\"Subscribe_to_Observable_Events\"><h4>Subscribe to Observable Events<\/h4><\/span>\n<p>* By implementing the <em>subscribe <\/em>function.<\/p>\n<pre lang=\"xml\">\r\n    self.myText.subscribe(function () {\r\n        self.msg('myText changed to: ' + self.myText());\r\n    });\r\n<\/pre>\n<span id=\"Knowout_Mapping_Plugin\"><h2>Knowout Mapping Plugin<\/h2><\/span>\n<p>* Used to automatically map a standard JavaScript object or JSON data to a new objects with each property being an observable object.<br \/>\n* Plugin file: <em>knockout.mapping.js<\/em><br \/>\n* Install plugin from NuGet:<br \/>\n<em>Install-Package Knockout.Mapping<\/em><br \/>\n* Map from JavaScript object<br \/>\n- use mapping to ignore certain properties<\/p>\n<pre lang=\"xml\">\r\n    <div data-bind=\"with: book1\">\r\n        <h1 data-bind=\"text: title\"><\/h1>\r\n        <h2 data-bind=\"text: author\"><\/h2>\r\n        <h2 data-bind=\"text: isbn\"><\/h2>\r\n    <\/div>\r\n    <script>\r\n        function ViewModel() {\r\n            var self = this;\r\n\r\n            var mapping = {\r\n                'observe': ['title', 'author'],\r\n                'ignore' : ['isbn']\r\n            };\r\n\r\n            self.book1 = ko.mapping.fromJS(book, mapping);\r\n        };\r\n\r\n        var book = {\r\n            title: 'Book One',\r\n            author: 'John Doe',\r\n            isbn: '12345'\r\n        };\r\n\r\n        var vm = new ViewModel();\r\n        ko.applyBindings(vm);\r\n\r\n    <\/script>\r\n<\/pre>\n<span id=\"Examples\"><h2>Examples<\/h2><\/span>\n<p>* See <a href=\"?p=11421\">here <\/a>for examples.<\/p>\n<span id=\"References\"><h2>References<\/h2><\/span>\n<p>* Knockout.js by Jamie Munro Published by O'Reilly Media, Inc., 2014<br \/>\n* <a href=\"http:\/\/knockoutjs.com\/index.html\">Knockout home page<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Overview * Open source JavaScript library. * Light weight, no other dependencies. * Implements MVVM pattern (Model-View-ViewModel) &#8211; Model: binds view model to view, e.g.: ko.applyBindings(viewModel); &#8211; View: contains HTML\/CSS elements for data to bind to, e.g. Hello ! &#8211; &hellip; <a href=\"https:\/\/jianmingli.com\/wp\/?p=11403\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_exactmetrics_skip_tracking":false,"_exactmetrics_sitenote_active":false,"_exactmetrics_sitenote_note":"","_exactmetrics_sitenote_category":0,"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[18,687],"tags":[496,688],"class_list":["post-11403","post","type-post","status-publish","format-standard","hentry","category-javascript","category-knockout","tag-javascript-2","tag-knockout"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p8cRUO-2XV","_links":{"self":[{"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=\/wp\/v2\/posts\/11403","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=11403"}],"version-history":[{"count":21,"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=\/wp\/v2\/posts\/11403\/revisions"}],"predecessor-version":[{"id":11516,"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=\/wp\/v2\/posts\/11403\/revisions\/11516"}],"wp:attachment":[{"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=11403"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=11403"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=11403"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}