text: 
  ko.subscription = function (target, callback, disposeCallback) {
      this._target = target;
      this.callback = callback;
      this.disposeCallback = disposeCallback;
      this.isDisposed = false;
      ko.exportProperty(this, 'dispose', this.dispose);
  };
  ko.subscription.prototype.dispose = function () {
      this.isDisposed = true;
      this.disposeCallback();
  };
  
  ko.subscribable = function () {
      ko.utils.setPrototypeOfOrExtend(this, ko_subscribable_fn);
      ko_subscribable_fn.init(this);
  }
  
  var defaultEvent = "change";
  
  // Moved out of "limit" to avoid the extra closure
  function limitNotifySubscribers(value, event) {
      if (!event || event === defaultEvent) {
          this._limitChange(value);
      } else if (event === 'beforeChange') {
          this._limitBeforeChange(value);
      } else {
          this._origNotifySubscribers(value, event);
      }
  }
  
  var ko_subscribable_fn = {
      init: function(instance) {
          instance._subscriptions = { "change": [] };
          instance._versionNumber = 1;
      },
  
      subscribe: function (callback, callbackTarget, event) {
          var self = this;
  
          event = event || defaultEvent;
          var boundCallback = callbackTarget ? callback.bind(callbackTarget) : callback;
  
          var subscription = new ko.subscription(self, boundCallback, function () {
              ko.utils.arrayRemoveItem(self._subscriptions[event], subscription);
              if (self.afterSubscriptionRemove)
                  self.afterSubscriptionRemove(event);
          });
  
          if (self.beforeSubscriptionAdd)
              self.beforeSubscriptionAdd(event);
  
          if (!self._subscriptions[event])
              self._subscriptions[event] = [];
          self._subscriptions[event].push(subscription);
  
          return subscription;
      },
  
      "notifySubscribers": function (valueToNotify, event) {
          event = event || defaultEvent;
          if (event === defaultEvent) {
              this.updateVersion();
          }
          if (this.hasSubscriptionsForEvent(event)) {
              var subs = event === defaultEvent && this._changeSubscriptions || this._subscriptions[event].slice(0);
              try {
                  ko.dependencyDetection.begin(); // Begin suppressing dependency detection (by setting the top frame to undefined)
                  for (var i = 0, subscription; subscription = subs[i]; ++i) {
                      // In case a subscription was disposed during the arrayForEach cycle, check
                      // for isDisposed on each subscription before invoking its callback
                      if (!subscription.isDisposed)
                          subscription.callback(valueToNotify);
                  }
              } finally {
                  ko.dependencyDetection.end(); // End suppressing dependency detection
              }
          }
      },
  
      getVersion: function () {
          return this._versionNumber;
      },
  
      hasChanged: function (versionToCheck) {
          return this.getVersion() !== versionToCheck;
      },
  
      updateVersion: function () {
          ++this._versionNumber;
      },
  
      limit: function(limitFunction) {
          var self = this, selfIsObservable = ko.isObservable(self),
              ignoreBeforeChange, notifyNextChange, previousValue, pendingValue, beforeChange = 'beforeChange';
  
          if (!self._origNotifySubscribers) {
              self._origNotifySubscribers = self["notifySubscribers"];
              self["notifySubscribers"] = limitNotifySubscribers;
          }
  
          var finish = limitFunction(function() {
              self._notificationIsPending = false;
  
              // If an observable provided a reference to itself, access it to get the latest value.
              // This allows computed observables to delay calculating their value until needed.
              if (selfIsObservable && pendingValue === self) {
                  pendingValue = self._evalIfChanged ? self._evalIfChanged() : self();
              }
              var shouldNotify = notifyNextChange || self.isDifferent(previousValue, pendingValue);
  
              notifyNextChange = ignoreBeforeChange = false;
  
              if (shouldNotify) {
                  self._origNotifySubscribers(previousValue = pendingValue);
              }
          });
  
          self._limitChange = function(value) {
              self._changeSubscriptions = self._subscriptions[defaultEvent].slice(0);
              self._notificationIsPending = ignoreBeforeChange = true;
              pendingValue = value;
              finish();
          };
          self._limitBeforeChange = function(value) {
              if (!ignoreBeforeChange) {
                  previousValue = value;
                  self._origNotifySubscribers(value, beforeChange);
              }
          };
          self._notifyNextChangeIfValueIsDifferent = function() {
              if (self.isDifferent(previousValue, self.peek(true /*evaluate*/))) {
                  notifyNextChange = true;
              }
          };
      },
  
      hasSubscriptionsForEvent: function(event) {
          return this._subscriptions[event] && this._subscriptions[event].length;
      },
  
      getSubscriptionsCount: function (event) {
          if (event) {
              return this._subscriptions[event] && this._subscriptions[event].length || 0;
          } else {
              var total = 0;
              ko.utils.objectForEach(this._subscriptions, function(eventName, subscriptions) {
                  if (eventName !== 'dirty')
                      total += subscriptions.length;
              });
              return total;
          }
      },
  
      isDifferent: function(oldValue, newValue) {
          return !this['equalityComparer'] || !this['equalityComparer'](oldValue, newValue);
      },
  
      extend: applyExtenders
  };
  
  ko.exportProperty(ko_subscribable_fn, 'subscribe', ko_subscribable_fn.subscribe);
  ko.exportProperty(ko_subscribable_fn, 'extend', ko_subscribable_fn.extend);
  ko.exportProperty(ko_subscribable_fn, 'getSubscriptionsCount', ko_subscribable_fn.getSubscriptionsCount);
  
  // For browsers that support proto assignment, we overwrite the prototype of each
  // observable instance. Since observables are functions, we need Function.prototype
  // to still be in the prototype chain.
  if (ko.utils.canSetPrototype) {
      ko.utils.setPrototypeOf(ko_subscribable_fn, Function.prototype);
  }
  
  ko.subscribable['fn'] = ko_subscribable_fn;
  
  
  ko.isSubscribable = function (instance) {
      return instance != null && typeof instance.subscribe == "function" && typeof instance["notifySubscribers"] == "function";
  };
  
  ko.exportSymbol('subscribable', ko.subscribable);
  ko.exportSymbol('isSubscribable', ko.isSubscribable);
  