1; (function (factory) { 2 //CommonJS 3 if (typeof require === "function" && typeof exports === "object" && typeof module === "object") { 4 factory(require("knockout"), exports); 5 //AMD 6 } else if (typeof define === "function" && define.amd) { 7 define(["knockout", "exports"], factory); 8 //normal script tag 9 } else { 10 factory(ko, ko.postbox = {}); 11 } 12}(function (ko, exports, undefined) { 13 var disposeTopicSubscription, existingSubscribe, 14 subscriptions = {}, 15 subId = 1; 16 17 exports.subscriptions = subscriptions; 18 19 //create a global postbox that supports subscribing/publishing 20 ko.subscribable.call(exports); 21 22 //keep a cache of the latest value and subscribers 23 exports.topicCache = {}; 24 25 //allow customization of the function used to serialize values for the topic cache 26 exports.serializer = ko.toJSON; 27 28 //wrap notifySubscribers passing topic first and caching latest value 29 exports.publish = function (topic, value) { 30 if (topic) { 31 //keep the value and a serialized version for comparison 32 exports.topicCache[topic] = { 33 value: value, 34 serialized: exports.serializer(value) 35 }; 36 exports.notifySubscribers(value, topic); 37 } 38 }; 39 40 //provide a subscribe API for the postbox that takes in the topic as first arg 41 existingSubscribe = exports.subscribe; 42 exports.subscribe = function (topic, action, target, initializeWithLatestValue) { 43 var subscription, current, existingDispose; 44 45 if (topic) { 46 if (typeof target === "boolean") { 47 initializeWithLatestValue = target; 48 target = undefined; 49 } 50 51 subscription = existingSubscribe.call(exports, action, target, topic); 52 subscription.subId = ++subId; 53 subscriptions[subId] = subscription; 54 55 if (initializeWithLatestValue) { 56 current = exports.topicCache[topic]; 57 58 if (current !== undefined) { 59 action.call(target, current.value); 60 } 61 } 62 63 existingDispose = subscription.dispose; 64 subscription.dispose = function () { 65 delete subscriptions[subscription.subId]; 66 existingDispose.call(subscription); 67 }; 68 69 return subscription; 70 } 71 }; 72 73 //clean up all subscriptions and references 74 exports.reset = function () { 75 var subscription; 76 77 for (var id in subscriptions) { 78 if (subscriptions.hasOwnProperty(id)) { 79 subscription = subscriptions[id]; 80 81 if (subscription && typeof subscription.dispose === "function") { 82 subscription.dispose(); 83 } 84 } 85 } 86 87 exports.topicCache = {}; 88 }; 89 90 //by default publish when the previous cached value does not equal the new value 91 exports.defaultComparer = function (newValue, cacheItem) { 92 return cacheItem && exports.serializer(newValue) === cacheItem.serialized; 93 }; 94 95 //augment observables/computeds with the ability to automatically publish updates on a topic 96 ko.subscribable.fn.publishOn = function (topic, skipInitialOrEqualityComparer, equalityComparer) { 97 var skipInitialPublish, subscription, existingDispose; 98 99 if (topic) { 100 //allow passing the equalityComparer as the second argument 101 if (typeof skipInitialOrEqualityComparer === "function") { 102 equalityComparer = skipInitialOrEqualityComparer; 103 } else { 104 skipInitialPublish = skipInitialOrEqualityComparer; 105 } 106 107 equalityComparer = equalityComparer || exports.defaultComparer; 108 109 //remove any existing subs 110 disposeTopicSubscription.call(this, topic, "publishOn"); 111 112 //keep a reference to the subscription, so we can stop publishing 113 subscription = this.subscribe(function (newValue) { 114 if (!equalityComparer.call(this, newValue, exports.topicCache[topic])) { 115 exports.publish(topic, newValue); 116 } 117 }, this); 118 119 //track the subscription in case of a reset 120 subscription.id = ++subId; 121 subscriptions[subId] = subscription; 122 123 //ensure that we cleanup pointers to subscription on dispose 124 existingDispose = subscription.dispose; 125 subscription.dispose = function () { 126 delete this.postboxSubs[topic].publishOn; 127 delete subscriptions[subscription.id]; 128 129 existingDispose.call(subscription); 130 }.bind(this); 131 132 this.postboxSubs[topic].publishOn = subscription; 133 134 //do an initial publish 135 if (!skipInitialPublish) { 136 exports.publish(topic, this()); 137 } 138 } 139 140 return this; 141 }; 142 143 //handle disposing a subscription used to publish or subscribe to a topic 144 disposeTopicSubscription = function (topic, type) { 145 var subs = this.postboxSubs = this.postboxSubs || {}; 146 subs[topic] = subs[topic] || {}; 147 148 if (subs[topic][type]) { 149 subs[topic][type].dispose(); 150 } 151 }; 152 153 //discontinue automatically publishing on a topic 154 ko.subscribable.fn.stopPublishingOn = function (topic) { 155 disposeTopicSubscription.call(this, topic, "publishOn"); 156 157 return this; 158 }; 159 160 //augment observables/computeds to automatically be updated by notifications on a topic 161 ko.subscribable.fn.subscribeTo = function (topic, initializeWithLatestValueOrTransform, transform) { 162 var initializeWithLatestValue, current, callback, subscription, existingDispose, 163 self = this; 164 165 //allow passing the filter as the second argument 166 if (typeof initializeWithLatestValueOrTransform === "function") { 167 transform = initializeWithLatestValueOrTransform; 168 } else { 169 initializeWithLatestValue = initializeWithLatestValueOrTransform; 170 } 171 172 if (topic && ko.isWriteableObservable(this)) { 173 //remove any existing subs 174 disposeTopicSubscription.call(this, topic, "subscribeTo"); 175 176 //if specified, apply a filter function in the subscription 177 callback = function (newValue) { 178 self(transform ? transform.call(self, newValue) : newValue); 179 }; 180 181 ////keep a reference to the subscription, so we can unsubscribe, if necessary 182 subscription = exports.subscribe(topic, callback); 183 this.postboxSubs[topic].subscribeTo = subscription; 184 185 //ensure that we cleanup pointers to subscription on dispose 186 existingDispose = subscription.dispose; 187 subscription.dispose = function () { 188 delete this.postboxSubs[topic].subscribeTo; 189 existingDispose.call(subscription); 190 }.bind(this); 191 192 if (initializeWithLatestValue) { 193 current = exports.topicCache[topic]; 194 195 if (current !== undefined) { 196 callback(current.value); 197 } 198 } 199 } 200 201 return this; 202 }; 203 204 //discontinue receiving updates on a topic 205 ko.subscribable.fn.unsubscribeFrom = function (topic) { 206 disposeTopicSubscription.call(this, topic, "subscribeTo"); 207 208 return this; 209 }; 210 211 // both subscribe and publish on the same topic 212 // -allows the ability to sync an observable/writeable computed/observableArray between view models
213 // -subscribeTo should really not use a filter function, as it would likely cause infinite recursion 214 ko.subscribable.fn.syncWith = function (topic, initializeWithLatestValue, skipInitialOrEqualityComparer, equalityComparer) { 215 this.subscribeTo(topic, initializeWithLatestValue).publishOn(topic, skipInitialOrEqualityComparer, equalityComparer); 216 217 return this; 218 }; 219 220 ko.postbox = exports; 221}));
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.