diff --git a/backbone.trackit.js b/backbone.trackit.js index 2dc6f4d..b630c53 100644 --- a/backbone.trackit.js +++ b/backbone.trackit.js @@ -41,23 +41,62 @@ // Wrap Backbone.History.navigate so that in-app routing // (`router.navigate('/path')`) can be intercepted with a // confirmation if there are any unsaved models. + + //Keep the un wrapped method so we can call it directly in the checkUrl method + var originalNavigate = Backbone.History.prototype.navigate; + Backbone.History.prototype.navigate = _.wrap(Backbone.History.prototype.navigate, function(oldNav, fragment, options) { var prompt = getPrompt('unloadRouterPrompt', fragment, options); if (prompt) { if (confirm(prompt + ' \n\nAre you sure you want to leave this page?')) { - oldNav.call(this, fragment, options); + return oldNav.call(this, fragment, options); + } + } else { + return oldNav.call(this, fragment, options); + } + }); + + + //To detect back button press with pushState navigation (not page loads) + //We need to intercept the Backbone.history internal popstate events + + //First remove binding to the handler so we can update it (it doesn't update otherwise) + Backbone.$(window).off('popstate', Backbone.history.checkUrl).off('hashchange', Backbone.history.checkUrl); + + //Now wrap the handler with out check + Backbone.history.checkUrl = _.wrap(Backbone.history.checkUrl, function(oldUrl, e) { + //at this point history.fragment is the fragment we are leaving, and history.getFragment is the fragment we are going to. + var currentFragment = Backbone.history.fragment; + + var prompt = getPrompt('unloadRouterPrompt', currentFragment); + if (prompt) { + if (confirm(prompt + ' \n\nAre you sure you want to leave this page?')) { + return oldUrl.call(Backbone.history, e); + } else { + //As the fragment is the one we are leaving, calling the original navigate will do nothing, as the fragments match + //So we need to change the history fragment to the one in the URL, and then we can swap it back again + Backbone.history.fragment = Backbone.history.getFragment(); + return originalNavigate.call(Backbone.history, currentFragment, {trigger:false}); } } else { - oldNav.call(this, fragment, options); + return oldUrl.call(Backbone.history, e); } }); + //Now Re bind to the wrapped event + Backbone.$(window).on('popstate', Backbone.history.checkUrl).on('hashchange', Backbone.history.checkUrl); + + + + // Create a browser unload handler which is triggered // on the refresh, back, or forward button. window.onbeforeunload = function(e) { return getPrompt('unloadWindowPrompt', e); }; + + // Backbone.Model API // ------------------ @@ -103,11 +142,23 @@ // Restores this model's attributes to // their original values since tracking // started, the last save, or last restart. - resetAttributes: function() { + resetAttributes: function(attrs) { + if (!this._trackingChanges) return; - this.attributes = this._originalAttrs; + + if(!attrs) { + this.set(this._originalAttrs); + } else { + for (var i in attrs) { + var key=attrs[i]; + delete this._unsavedChanges[key]; + this.set(key, this._originalAttrs[key]); + } + } + this._resetTracking(); this._triggerUnsavedChanges(); + return this; }, @@ -189,4 +240,4 @@ return oldSync(method, model, options); }); -})(); \ No newline at end of file +})();