comindware.core.ui
Version:
Comindware Core UI provides the basic components like editors, lists, dropdowns, popups that we so desperately need while creating Marionette-based single-page applications.
164 lines (147 loc) • 5.87 kB
JavaScript
/**
* Caution: this is a modified version of Backbone.Trackit. You should not replace it with git version as it is buggy and not supported by its creator anymore
*/
(function() {
// Unsaved Record Keeping
// ----------------------
// Collection of all models in an app that have unsaved changes.
let unsavedModels = [];
// If the given model has unsaved changes then add it to
// the `unsavedModels` collection, otherwise remove it.
const updateUnsavedModels = function(model) {
if (!_.isEmpty(model._unsavedChanges)) {
if (!_.findWhere(unsavedModels, { cid: model.cid })) unsavedModels.push(model);
} else {
unsavedModels = _.filter(unsavedModels, m => model.cid != m.cid);
}
};
// Backbone.Model API
// ------------------
_.extend(Backbone.Model.prototype, {
unsaved: {},
_trackingChanges: false,
_originalAttrs: {},
_unsavedChanges: {},
// Opt in to tracking attribute changes
// between saves.
startTracking() {
this._unsavedConfig = _.extend(
{},
{
prompt: 'You have unsaved changes!',
unloadRouterPrompt: false,
unloadWindowPrompt: false
},
this.unsaved || {}
);
this._trackingChanges = true;
this._resetTracking();
this._triggerUnsavedChanges();
return this;
},
// Resets the default tracking values
// and stops tracking attribute changes.
stopTracking() {
this._trackingChanges = false;
this._originalAttrs = {};
this._unsavedChanges = {};
this._triggerUnsavedChanges();
return this;
},
// Gets rid of accrued changes and
// resets state.
restartTracking() {
this._resetTracking();
this._triggerUnsavedChanges();
return this;
},
// Restores this model's attributes to
// their original values since tracking
// started, the last save, or last restart.
resetAttributes() {
if (!this._trackingChanges) return;
this.attributes = this._originalAttrs;
this._resetTracking();
this._triggerUnsavedChanges();
return this;
},
// Symmetric to Backbone's `model.changedAttributes()`,
// except that this returns a hash of the model's attributes that
// have changed since the last save, or `false` if there are none.
// Like `changedAttributes`, an external attributes hash can be
// passed in, returning the attributes in that hash which differ
// from the model.
unsavedAttributes(attrs) {
if (!attrs) return _.isEmpty(this._unsavedChanges) ? false : Object.assign({}, this._unsavedChanges);
let val,
changed = false,
old = this._unsavedChanges;
for (const attr in attrs) {
if (_.isEqual(old[attr], (val = attrs[attr]))) continue;
(changed || (changed = {}))[attr] = val;
}
return changed;
},
_resetTracking() {
this._originalAttrs = Object.assign({}, this.attributes);
this._unsavedChanges = {};
},
// Trigger an `unsavedChanges` event on this model,
// supplying the result of whether there are unsaved
// changes and a changed attributes hash.
_triggerUnsavedChanges() {
this.trigger('unsavedChanges', !_.isEmpty(this._unsavedChanges), Object.assign({}, this._unsavedChanges));
if (this.unsaved) updateUnsavedModels(this);
}
});
// Wrap `model.set()` and update the internal
// unsaved changes record keeping.
Backbone.Model.prototype.set = _.wrap(Backbone.Model.prototype.set, function(oldSet, key, val, options) {
let attrs, ret;
if (key == null) return this;
// Handle both `"key", value` and `{key: value}` -style arguments.
if (typeof key === 'object') {
attrs = key;
options = val;
} else {
(attrs = {})[key] = val;
}
options || (options = {});
// Delegate to Backbone's set.
ret = oldSet.call(this, attrs, Object.assign({}, options));
if (this._trackingChanges && !options.silent && !options.trackit_silent) {
_.each(
attrs,
_.bind(function(val, key) {
if (_.isEqual(this._originalAttrs[key], val)) {
delete this._unsavedChanges[key];
} else {
this._unsavedChanges[key] = val;
}
}, this)
);
this._triggerUnsavedChanges();
}
return ret;
});
// Intercept `model.save()` and reset tracking/unsaved
// changes if it was successful.
Backbone.sync = _.wrap(Backbone.sync, function(oldSync, method, model, options) {
options || (options = {});
if (method == 'update' || method == 'patch') {
options.success = _.wrap(
options.success,
_.bind(function(oldSuccess, data, textStatus, jqXHR) {
let ret;
if (oldSuccess) ret = oldSuccess.call(this, data, textStatus, jqXHR);
if (model._trackingChanges) {
model._resetTracking();
model._triggerUnsavedChanges();
}
return ret;
}, this)
);
}
return oldSync(method, model, options);
});
})();