parent
13194704b4
commit
e720094f9c
@ -0,0 +1,174 @@
|
|||||||
|
(function (root, define, require, exports, module, factory, undef) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
if (typeof exports === 'object') {
|
||||||
|
// Node. Does not work with strict CommonJS, but
|
||||||
|
// only CommonJS-like enviroments that support module.exports,
|
||||||
|
// like Node.
|
||||||
|
module.exports = factory(require('underscore'), require('backbone'));
|
||||||
|
} else if (typeof define === 'function' && define.amd) {
|
||||||
|
// AMD. Register as an anonymous module.
|
||||||
|
define(['underscore', 'backbone'], function (_, Backbone) {
|
||||||
|
// Check if we use the AMD branch of Back
|
||||||
|
_ = _ === undef ? root._ : _;
|
||||||
|
Backbone = Backbone === undef ? root.Backbone : Backbone;
|
||||||
|
return (root.returnExportsGlobal = factory(_, Backbone, root));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Browser globals
|
||||||
|
root.returnExportsGlobal = factory(root._, root.Backbone);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage:
|
||||||
|
//
|
||||||
|
// Note: This plugin is UMD compatible, you can use it in node, amd and vanilla js envs
|
||||||
|
//
|
||||||
|
// Vanilla JS:
|
||||||
|
// <script src="underscore.js"></script>
|
||||||
|
// <script src="backbone.js"></script>
|
||||||
|
// <script src="backbone.mutators.js"></script>
|
||||||
|
//
|
||||||
|
// Node:
|
||||||
|
// var _ = require('underscore');
|
||||||
|
// var Backbone = require('backbone');
|
||||||
|
// var Mutators = require('backbone.mutators');
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// AMD:
|
||||||
|
// define(['underscore', 'backbone', 'backbone.mutators'], function (_, Backbone, Mutators) {
|
||||||
|
// // insert sample from below
|
||||||
|
// return User;
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// var User = Backbone.Model.extend({
|
||||||
|
// mutators: {
|
||||||
|
// fullname: function () {
|
||||||
|
// return this.firstname + ' ' + this.lastname;
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
//
|
||||||
|
// defaults: {
|
||||||
|
// firstname: 'Sebastian',
|
||||||
|
// lastname: 'Golasch'
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// var user = new User();
|
||||||
|
// user.get('fullname') // returns 'Sebastian Golasch'
|
||||||
|
// user.toJSON() // return '{firstname: 'Sebastian', lastname: 'Golasch', fullname: 'Sebastian Golasch'}'
|
||||||
|
|
||||||
|
}(this, this.define, this.require, this.exports, this.module, function (_, Backbone, root, undef) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// check if we use the amd branch of backbone and underscore
|
||||||
|
Backbone = Backbone === undef ? root.Backbone : Backbone;
|
||||||
|
_ = _ === undef ? root._ : _;
|
||||||
|
|
||||||
|
// extend backbones model prototype with the mutator functionality
|
||||||
|
var Mutator = function () { },
|
||||||
|
oldGet = Backbone.Model.prototype.get,
|
||||||
|
oldSet = Backbone.Model.prototype.set,
|
||||||
|
oldToJson = Backbone.Model.prototype.toJSON;
|
||||||
|
|
||||||
|
// This is necessary to ensure that Models declared without the mutators object do not throw and error
|
||||||
|
Mutator.prototype.mutators = {};
|
||||||
|
|
||||||
|
// override get functionality to fetch the mutator props
|
||||||
|
Mutator.prototype.get = function (attr) {
|
||||||
|
var isMutator = this.mutators !== undef;
|
||||||
|
|
||||||
|
// check if we have a getter mutation
|
||||||
|
if (isMutator === true && _.isFunction(this.mutators[attr]) === true) {
|
||||||
|
return this.mutators[attr].call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we have a deeper nested getter mutation
|
||||||
|
if (isMutator === true && _.isObject(this.mutators[attr]) === true && _.isFunction(this.mutators[attr].get) === true) {
|
||||||
|
return this.mutators[attr].get.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return oldGet.call(this, attr);
|
||||||
|
};
|
||||||
|
|
||||||
|
// override set functionality to set the mutator props
|
||||||
|
Mutator.prototype.set = function (key, value, options) {
|
||||||
|
var isMutator = this.mutators !== undef,
|
||||||
|
ret = null,
|
||||||
|
attrs = null,
|
||||||
|
attr = null;
|
||||||
|
|
||||||
|
// seamleassly stolen from backbone core
|
||||||
|
// check if the setter action is triggered
|
||||||
|
// using key <-> value or object
|
||||||
|
if (_.isObject(key) || key === null) {
|
||||||
|
attrs = key;
|
||||||
|
options = value;
|
||||||
|
} else {
|
||||||
|
attrs = {};
|
||||||
|
attrs[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we have a deeper nested setter mutation
|
||||||
|
if (isMutator === true && _.isObject(this.mutators[key]) === true) {
|
||||||
|
|
||||||
|
// check if we need to set a single value
|
||||||
|
if (_.isFunction(this.mutators[key].set) === true) {
|
||||||
|
ret = this.mutators[key].set.call(this, key, attrs[key], options, _.bind(oldSet, this));
|
||||||
|
} else if (_.isFunction(this.mutators[key])) {
|
||||||
|
ret = this.mutators[key].call(this, key, attrs[key], options, _.bind(oldSet, this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_.isObject(attrs)) {
|
||||||
|
_.each(attrs, _.bind(function (attr, attrKey) {
|
||||||
|
if (isMutator === true && _.isObject(this.mutators[attrKey]) === true) {
|
||||||
|
// check if we need to set a single value
|
||||||
|
|
||||||
|
var meth = this.mutators[attrKey];
|
||||||
|
if (_.isFunction(meth.set)) {
|
||||||
|
meth = meth.set;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_.isFunction(meth)) {
|
||||||
|
if (options === undef || (_.isObject(options) === true && options.silent !== true && (options.mutators !== undef && options.mutators.silent !== true))) {
|
||||||
|
this.trigger('mutators:set:' + attrKey);
|
||||||
|
}
|
||||||
|
ret = meth.call(this, attrKey, attr, options, _.bind(oldSet, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
//validation purposes
|
||||||
|
if (ret !== null) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return oldSet.call(this, key, value, options);
|
||||||
|
};
|
||||||
|
|
||||||
|
// override toJSON functionality to serialize mutator properties
|
||||||
|
Mutator.prototype.toJSON = function () {
|
||||||
|
// fetch ye olde values
|
||||||
|
var attr = oldToJson.call(this);
|
||||||
|
// iterate over all mutators (if there are some)
|
||||||
|
_.each(this.mutators, _.bind(function (mutator, name) {
|
||||||
|
// check if we have some getter mutations (nested or )
|
||||||
|
if (_.isObject(this.mutators[name]) === true && _.isFunction(this.mutators[name].get)) {
|
||||||
|
attr[name] = _.bind(this.mutators[name].get, this)();
|
||||||
|
} else {
|
||||||
|
attr[name] = _.bind(this.mutators[name], this)();
|
||||||
|
}
|
||||||
|
}, this));
|
||||||
|
|
||||||
|
return attr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// extend the models prototype
|
||||||
|
_.extend(Backbone.Model.prototype, Mutator.prototype);
|
||||||
|
|
||||||
|
// make mutators globally available under the Backbone namespace
|
||||||
|
Backbone.Mutators = Mutator;
|
||||||
|
return Mutator;
|
||||||
|
}));
|
@ -0,0 +1,35 @@
|
|||||||
|
(function() {
|
||||||
|
var Shortcuts;
|
||||||
|
|
||||||
|
Shortcuts = function(options) {
|
||||||
|
this.cid = _.uniqueId("backbone.shortcuts");
|
||||||
|
this.initialize.apply(this, arguments);
|
||||||
|
return this.delegateShortcuts();
|
||||||
|
};
|
||||||
|
|
||||||
|
_.extend(Shortcuts.prototype, Backbone.Events, {
|
||||||
|
initialize: function() {},
|
||||||
|
delegateShortcuts: function() {
|
||||||
|
var callback, match, method, scope, shortcut, shortcutKey, _ref, _results;
|
||||||
|
if (!this.shortcuts) return;
|
||||||
|
_ref = this.shortcuts;
|
||||||
|
_results = [];
|
||||||
|
for (shortcut in _ref) {
|
||||||
|
callback = _ref[shortcut];
|
||||||
|
if (!_.isFunction(callback)) method = this[callback];
|
||||||
|
if (!method) throw new Error("Method " + callback + " does not exist");
|
||||||
|
match = shortcut.match(/^(\S+)\s*(.*)$/);
|
||||||
|
shortcutKey = match[1];
|
||||||
|
scope = match[2] === "" ? "all" : match[2];
|
||||||
|
method = _.bind(method, this);
|
||||||
|
_results.push(key(shortcutKey, scope, method));
|
||||||
|
}
|
||||||
|
return _results;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Backbone.Shortcuts = Shortcuts;
|
||||||
|
|
||||||
|
Backbone.Shortcuts.extend = Backbone.View.extend;
|
||||||
|
|
||||||
|
}).call(this);
|
@ -0,0 +1,212 @@
|
|||||||
|
https://github.com/marionettejs/backbone.marionette/blob/viewswap/docs/marionette.viewswapper.md
|
||||||
|
|
||||||
|
// View Swapper
|
||||||
|
// ------------
|
||||||
|
//
|
||||||
|
// Switch out views based on events that are triggered
|
||||||
|
// by the currently displayed view. Enables easy "edit in
|
||||||
|
// place" features, "loading" screens, and more.
|
||||||
|
|
||||||
|
Marionette.ViewSwapper = Marionette.View.extend({
|
||||||
|
constructor: function (options) {
|
||||||
|
this._swapperViews = {};
|
||||||
|
this._swapperBindings = new Marionette.EventBinder();
|
||||||
|
this._currentViewBindings = new Marionette.EventBinder();
|
||||||
|
|
||||||
|
Marionette.View.prototype.constructor.apply(this, arguments);
|
||||||
|
|
||||||
|
this.views = Marionette.getOption(this, "views");
|
||||||
|
this.swapOn = Marionette.getOption(this, "swapOn");
|
||||||
|
this.initialView = Marionette.getOption(this, "initialView");
|
||||||
|
|
||||||
|
this._setupViewEvents("swapper", this, this._swapperBindings);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Render the current view. If no current view is set, it
|
||||||
|
// will render the `initialView` that was configured.
|
||||||
|
render: function () {
|
||||||
|
// set up the initial view to display, on first render
|
||||||
|
if (!this.currentView) {
|
||||||
|
var initialViewName = Marionette.getOption(this, "initialView");
|
||||||
|
this._swapView(initialViewName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// render and show the new view
|
||||||
|
this.currentView.render();
|
||||||
|
this.$el.append(this.currentView.$el);
|
||||||
|
|
||||||
|
// setup a callback for the showView call to recieve
|
||||||
|
var done = _.bind(function () {
|
||||||
|
// trigger show/onShow on the previous view
|
||||||
|
if (this.currentView) {
|
||||||
|
Marionette.triggerMethod.call(this.currentView, "show");
|
||||||
|
Marionette.triggerMethod.call(this, "swap:in", this.currentView);
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
// show the view, passing it the done callback
|
||||||
|
this.showView(this.currentView, done);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Show a view that is being swapped in. Override this method to
|
||||||
|
// set up your own custom fade in / show method
|
||||||
|
showView: function (view, done) {
|
||||||
|
view.$el.show();
|
||||||
|
done();
|
||||||
|
},
|
||||||
|
|
||||||
|
// Hide a view that is being swapped out. Override this method to
|
||||||
|
// set up your own custom fade out / hide method
|
||||||
|
hideView: function (view, done) {
|
||||||
|
view.$el.hide();
|
||||||
|
done();
|
||||||
|
},
|
||||||
|
|
||||||
|
// Ensure the views that were configured for this view swapper get closed
|
||||||
|
close: function () {
|
||||||
|
|
||||||
|
// Close all of the configured views that we are swapping between
|
||||||
|
_.each(this.views, function (view, name) {
|
||||||
|
view.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
// unbind all the events, and clean up any decorator views
|
||||||
|
this._swapperViews = {};
|
||||||
|
this._currentViewBindings.unbindAll();
|
||||||
|
this._swapperBindings.unbindAll();
|
||||||
|
|
||||||
|
// Close the base view that we extended from
|
||||||
|
Marionette.View.prototype.close.apply(this, arguments);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Get a view by name, throwing an exception if the view instance
|
||||||
|
// is not found.
|
||||||
|
_getView: function (viewName) {
|
||||||
|
var originalView, error, views;
|
||||||
|
var swapperView = this._swapperViews[viewName];
|
||||||
|
|
||||||
|
// Do not allow the name "swapper" to be used as a target view
|
||||||
|
// or initial view. This is reserved for the ViewSwapper instance,
|
||||||
|
// when configuring `swapOn` events
|
||||||
|
if (viewName === "swapper") {
|
||||||
|
error = new Error("Cannot display 'swapper' as a view.");
|
||||||
|
error.name = "InvalidViewName";
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do we have a view with the specified name?
|
||||||
|
if (!swapperView) {
|
||||||
|
originalView = this.views[viewName];
|
||||||
|
|
||||||
|
// No view, so throw an exception
|
||||||
|
if (!originalView) {
|
||||||
|
error = new Error("Cannot show view in ViewSwapper. View '" + viewName + "' not found.");
|
||||||
|
error.name = "ViewNotFoundError";
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Found the view, so build a Decorator around it
|
||||||
|
swapperView = this._buildSwapperView(originalView, viewName);
|
||||||
|
this._swapperViews[viewName] = swapperView;
|
||||||
|
}
|
||||||
|
|
||||||
|
return swapperView;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Decorate the configured view with information that the view swapper
|
||||||
|
// needs, to keep track of the view's current state.
|
||||||
|
_buildSwapperView: function (originalView, viewName) {
|
||||||
|
var swapperView = Marionette.createObject(originalView);
|
||||||
|
_.extend(swapperView, {
|
||||||
|
|
||||||
|
viewName: viewName,
|
||||||
|
originalView: originalView,
|
||||||
|
|
||||||
|
// Prevent the underlying view from being rendered more than once
|
||||||
|
render: function () {
|
||||||
|
var value;
|
||||||
|
|
||||||
|
if (this._hasBeenRendered) {
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// prevent any more rendering
|
||||||
|
this._hasBeenRendered = true;
|
||||||
|
|
||||||
|
// do the render
|
||||||
|
value = originalView.render.apply(originalView, arguments);
|
||||||
|
|
||||||
|
// trigger render/onRender
|
||||||
|
Marionette.triggerMethod.call(this, "render");
|
||||||
|
|
||||||
|
// return whatever was sent back to us
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
return swapperView;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Set up the event handlers for the individual views, so that the
|
||||||
|
// swapping can happen when a view event is triggered
|
||||||
|
_setupViewEvents: function (viewName, view, bindings) {
|
||||||
|
if (!this.swapOn || !this.swapOn[viewName]) { return; }
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
// default to current view bindings, unless otherwise specified
|
||||||
|
if (!bindings) {
|
||||||
|
bindings = this._currentViewBindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
// close the previous event bindings
|
||||||
|
bindings.unbindAll();
|
||||||
|
|
||||||
|
// set up the new view's event bindings
|
||||||
|
_.each(this.swapOn[viewName], function (targetViewName, eventName) {
|
||||||
|
|
||||||
|
bindings.bindTo(view, eventName, function () {
|
||||||
|
that._swapView(targetViewName);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Do the swapping of the views to the new view, by name
|
||||||
|
_swapView: function (viewName) {
|
||||||
|
|
||||||
|
// only swap views if the target view is not the same
|
||||||
|
// as the current view
|
||||||
|
var view = this._getView(viewName);
|
||||||
|
if (view === this.currentView) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provide a callback function that will switch over to
|
||||||
|
// the new view, when called
|
||||||
|
var done = _.bind(function () {
|
||||||
|
|
||||||
|
// trigger hide/onHide on the previous view
|
||||||
|
if (this.currentView) {
|
||||||
|
Marionette.triggerMethod.call(this.currentView, "hide");
|
||||||
|
Marionette.triggerMethod.call(this, "swap:out", this.currentView);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the next view, configure it's events and render it
|
||||||
|
this._setupViewEvents(viewName, view);
|
||||||
|
this.currentView = view;
|
||||||
|
this.render();
|
||||||
|
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
if (this.currentView) {
|
||||||
|
// if we have a current view, hide it so that the new
|
||||||
|
// view can be show in it's place
|
||||||
|
this.hideView(this.currentView, done);
|
||||||
|
} else {
|
||||||
|
// no current view, so just switch to the new view
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue