docs/backbone.composite-model.html
<!DOCTYPE html>
<html>
<head>
<title>backbone.composite-model.js</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">
<link rel="stylesheet" media="all" href="docco.css" />
</head>
<body>
<div id="container">
<div id="background"></div>
<ul class="sections">
<li id="title">
<div class="annotation">
<h1>backbone.composite-model.js</h1>
</div>
</li>
<li id="section-1">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-1">§</a>
</div>
<p>Backbone.CompositeModel 0.1.7
<a href="https://github.com/prantlf/backbone.composite-model">https://github.com/prantlf/backbone.composite-model</a></p>
<p>Copyright (c) 2015-2017 Ferdinand Prantl
Licensed under the MIT license.</p>
<p>Supports composite Backbone.Model objects which consist of a main model
and child models or collections maintained automatically according to a
composite configuration</p>
</div>
</li>
<li id="section-2">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-2">§</a>
</div>
<h2 id="example">Example</h2>
</div>
</li>
<li id="section-3">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-3">§</a>
</div>
</div>
</li>
<li id="section-4">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-4">§</a>
</div>
<p>Let’s have a versioned file-system model: folders which contain
sub-folders and files, files consisting of file versions. The REST
API resource <code>/files/:id</code> returns the following (simplified) response
about a file:</p>
<pre><code>{
<span class="hljs-attr">id</span>: ..., <span class="hljs-comment">// globally unique ID</span>
<span class="hljs-attr">name</span>: <span class="hljs-string">'...'</span>, <span class="hljs-comment">// file display name</span>
<span class="hljs-attr">parent_expanded</span>: {...}, <span class="hljs-comment">// parent folder object</span>
<span class="hljs-attr">versions</span>: [...] <span class="hljs-comment">// version object array</span>
}
</code></pre>
<p>Let’s declare the following (simplified) models and collections for them:</p>
<pre><code><span class="hljs-keyword">var</span> <span class="hljs-title class_">FolderModel</span> = <span class="hljs-title class_">Backbone</span>.<span class="hljs-property">Model</span>.<span class="hljs-title function_">extend</span>({...});
<span class="hljs-keyword">var</span> <span class="hljs-title class_">VersionCollection</span> = <span class="hljs-title class_">Backbone</span>.<span class="hljs-property">Collection</span>.<span class="hljs-title function_">extend</span>({...});
<span class="hljs-keyword">var</span> <span class="hljs-title class_">FileModel</span> = <span class="hljs-title class_">Backbone</span>.<span class="hljs-property">Model</span>.<span class="hljs-title function_">extend</span>({...})
<span class="hljs-comment">// Declare what attributes from the response back</span>
<span class="hljs-comment">// up what child models and collection stored in</span>
<span class="hljs-comment">// properties on the object instance</span>
<span class="hljs-attr">composite</span>: {
<span class="hljs-attr">parent_expanded</span>: {
<span class="hljs-attr">model</span>: <span class="hljs-title class_">FolderModel</span>,
<span class="hljs-attr">property</span>: <span class="hljs-string">'parent'</span>
},
<span class="hljs-attr">versions</span>: <span class="hljs-title class_">VersionCollection</span>,
},
<span class="hljs-comment">// Override the constructor to see the name `FileModel`</span>
<span class="hljs-comment">// in the debugger and to be able to initialize the</span>
<span class="hljs-comment">// composite model</span>
<span class="hljs-attr">constructor</span>: <span class="hljs-keyword">function</span> <span class="hljs-title function_">FileModel</span>(<span class="hljs-params">attributes, options</span>) {
<span class="hljs-title class_">FileModel</span>.<span class="hljs-property">__super__</span>.<span class="hljs-property">constructor</span>.<span class="hljs-title function_">apply</span>(<span class="hljs-variable language_">this</span>, <span class="hljs-variable language_">arguments</span>);
<span class="hljs-variable language_">this</span>,<span class="hljs-title function_">makeComposite</span>(options);
},
<span class="hljs-comment">// Point to the resource representing the file</span>
<span class="hljs-comment">// on the server</span>
<span class="hljs-attr">urlRoot</span>: <span class="hljs-string">'/files'</span>
});
<span class="hljs-comment">// Extend the function object prototype to become</span>
<span class="hljs-comment">// a composite of child models and collections</span>
<span class="hljs-title class_">Backbone</span>.<span class="hljs-title function_">mixinCompositeModel</span>(<span class="hljs-title class_">FileModel</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>);
</code></pre>
<p>This lets the <code>parent</code> and <code>versions</code> properties maintained automatically
without an additional code.</p>
<pre><code><span class="hljs-keyword">var</span> file = <span class="hljs-keyword">new</span> <span class="hljs-title class_">FileModel</span>({<span class="hljs-attr">id</span>: ...});
file.<span class="hljs-title function_">fetch</span>()
.<span class="hljs-title function_">done</span>(<span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'Name:'</span>, file.<span class="hljs-title function_">get</span>(<span class="hljs-string">'name'</span>));
<span class="hljs-comment">// This does not work.</span>
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'Parent folder:'</span>, file.<span class="hljs-property">parent</span>.<span class="hljs-title function_">get</span>(<span class="hljs-string">'name'</span>));
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'Version count:'</span>, file.<span class="hljs-property">versions</span>.<span class="hljs-property">length</span>);
});
</code></pre>
<p>The <code>parent</code> object and the <code>versions</code> array are be accessible as
<code>Backbone.Model</code> and <code>Backbone.Collection</code> to be able to pass them to
other models and views and listen to their events in the application
using a common Backbone interface.</p>
</div>
</li>
<li id="section-5">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-5">§</a>
</div>
<h2 id="module-factory">Module Factory</h2>
</div>
</li>
<li id="section-6">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-6">§</a>
</div>
</div>
</li>
<li id="section-7">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-7">§</a>
</div>
<p>UMD wrapper to support multiple module dependency loaders</p>
</div>
<div class="content"><div class='highlight'><pre>(<span class="hljs-keyword">function</span> (<span class="hljs-params">root, factory</span>) {
<span class="hljs-string">'use strict'</span>;</pre></div></div>
</li>
<li id="section-8">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-8">§</a>
</div>
<p>Handle AMD modules (RequireJS)</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> define === <span class="hljs-string">'function'</span> && define.<span class="hljs-property">amd</span>) {
<span class="hljs-title function_">define</span>([<span class="hljs-string">'underscore'</span>, <span class="hljs-string">'backbone'</span>], factory);</pre></div></div>
</li>
<li id="section-9">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-9">§</a>
</div>
<p>Handle CommonJS modules (NodeJS)</p>
</div>
<div class="content"><div class='highlight'><pre> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> <span class="hljs-variable language_">module</span> === <span class="hljs-string">'object'</span> && <span class="hljs-variable language_">module</span>.<span class="hljs-property">exports</span>) {
<span class="hljs-variable language_">module</span>.<span class="hljs-property">exports</span> = <span class="hljs-title function_">factory</span>(<span class="hljs-built_in">require</span>(<span class="hljs-string">'underscore'</span>), <span class="hljs-built_in">require</span>(<span class="hljs-string">'backbone'</span>));</pre></div></div>
</li>
<li id="section-10">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-10">§</a>
</div>
<p>Handle global variables setting modules (web browser alone)</p>
</div>
<div class="content"><div class='highlight'><pre> } <span class="hljs-keyword">else</span> {
root.<span class="hljs-property">returnExports</span> = <span class="hljs-title function_">factory</span>(root.<span class="hljs-property">_</span>, root.<span class="hljs-property">Backbone</span>);
}
}(<span class="hljs-variable language_">this</span>, <span class="hljs-keyword">function</span> (<span class="hljs-params">_, Backbone</span>) {
<span class="hljs-string">'use strict'</span>;</pre></div></div>
</li>
<li id="section-11">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-11">§</a>
</div>
<h2 id="mixin-function">Mixin Function</h2>
</div>
</li>
<li id="section-12">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-12">§</a>
</div>
</div>
</li>
<li id="section-13">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-13">§</a>
</div>
<p>Applied on a <code>Backbone.Model</code> prototype, extends it to support model
composites made of child models and collections, which can be
configured by the <code>composite</code> property from the object prototype,
instance or constructor <code>options</code>.</p>
<p>Members of the <code>composite</code> object (map):</p>
<pre><code><span class="hljs-comment">// Maintains a property on the model instance with</span>
<span class="hljs-comment">// the attribute name pointing to an object instance</span>
<span class="hljs-comment">// of the specified type</span>
<attribute name>: <Backbone model or collection>,
// Maintains a property on the model instance backed
// up by the specified attribute overriding the
// default handling
<attribute name>: {
// Model or collection to create for the attribute
// value (required)
type: <Backbone model or collection>,
// Property name to store the sub-object on the
// model with (optional; the attribute name is
// the default)
property: <property name>,
// Additional options to pass to the constructor of
// the sub-object (optional; undefined by default)
options: <object literal>,
// Method to call on the child model or collection
// if updated data are being set (optional; `set` is
// the default for models, `add` for collections)
method: <method name>,
// Function to call before the value is passed to
// child model or collection constructor or `set` /
// `add` method to "massage" the input data
// (optional; `undefined` is the default)
parse: <function (value, options)>,
}
</code></pre>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-title class_">Backbone</span>.<span class="hljs-property">mixinCompositeModel</span> = <span class="hljs-keyword">function</span> (<span class="hljs-params">prototype</span>) {
<span class="hljs-keyword">var</span> originalSet = prototype.<span class="hljs-property">set</span>,
originalToJSON = prototype.<span class="hljs-property">toJSON</span>;
<span class="hljs-keyword">return</span> _.<span class="hljs-title function_">extend</span>(prototype, {</pre></div></div>
</li>
<li id="section-14">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-14">§</a>
</div>
<h2 id="initialization-function">Initialization Function</h2>
</div>
</li>
<li id="section-15">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-15">§</a>
</div>
</div>
</li>
<li id="section-16">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-16">§</a>
</div>
<p>Initializes the composite model support; to be called from the
<code>initialize</code> method or from the overridden constructor, after
the parent constructor has been called</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-attr">makeComposite</span>: <span class="hljs-keyword">function</span> (<span class="hljs-params">options</span>) {</pre></div></div>
</li>
<li id="section-17">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-17">§</a>
</div>
<p>Mark the object creation scenario for the _updateComposite below</p>
</div>
<div class="content"><div class='highlight'><pre> options = _.<span class="hljs-title function_">extend</span>({<span class="hljs-attr">create</span>: <span class="hljs-literal">true</span>}, options);
<span class="hljs-variable language_">this</span>.<span class="hljs-property">_compositeMap</span> = <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">_createCompositeMap</span>(options);</pre></div></div>
</li>
<li id="section-18">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-18">§</a>
</div>
<p>Update properties explicitly, because the <code>set</code> method with
the initial attributes of the model is called in the parent
constructor already, before the <code>_compositeMap</code> has been
called here, thus the composite extension could not apply yet</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">_updateComposite</span>(<span class="hljs-variable language_">this</span>.<span class="hljs-property">attributes</span>, options);
<span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>;
},</pre></div></div>
</li>
<li id="section-19">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-19">§</a>
</div>
<h2 id="overridden-functions">Overridden Functions</h2>
</div>
</li>
<li id="section-20">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-20">§</a>
</div>
</div>
</li>
<li id="section-21">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-21">§</a>
</div>
<p>Overrides the <code>Backbone.Model:set()</code> method to ensure that the
nested attribute values will be propagated to the child models
and collections of this composite</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-attr">set</span>: <span class="hljs-keyword">function</span> (<span class="hljs-params">key, value, options</span>) {
<span class="hljs-keyword">var</span> attributes;</pre></div></div>
</li>
<li id="section-22">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-22">§</a>
</div>
<p>Do nothing if nought has been asked for</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (key == <span class="hljs-literal">null</span>) {
<span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>;
}</pre></div></div>
</li>
<li id="section-23">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-23">§</a>
</div>
<p>Normalize the input parameters to become two object literals:
handle both <code>'key', value</code> and <code>{key: value}</code> -style arguments</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> key === <span class="hljs-string">'object'</span>) {
attributes = key;
options = value;
} <span class="hljs-keyword">else</span> {
(attributes = {})[key] = value;
}
options || (options = {});</pre></div></div>
</li>
<li id="section-24">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-24">§</a>
</div>
<p>Set the common attributes and check the result first</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> result = originalSet.<span class="hljs-title function_">call</span>(<span class="hljs-variable language_">this</span>, attributes, options);</pre></div></div>
</li>
<li id="section-25">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-25">§</a>
</div>
<p>Update the child models and collections if the composite
map has been initialized (after the constructor has finished)</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (result && <span class="hljs-variable language_">this</span>.<span class="hljs-property">_compositeMap</span>) {
<span class="hljs-variable language_">this</span>.<span class="hljs-title function_">_updateComposite</span>(attributes, options);
}</pre></div></div>
</li>
<li id="section-26">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-26">§</a>
</div>
<p>Return the same result as the original <code>set</code> method</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> result;
},</pre></div></div>
</li>
<li id="section-27">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-27">§</a>
</div>
<p>Overrides the <code>Backbone.Model:toJSON()</code> method to ensure that the
up-to-date nested attribute values will be present in the result</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-attr">toJSON</span>: <span class="hljs-keyword">function</span>(<span class="hljs-params">options</span>) {
<span class="hljs-keyword">var</span> result = originalToJSON.<span class="hljs-title function_">call</span>(<span class="hljs-variable language_">this</span>, options);</pre></div></div>
</li>
<li id="section-28">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-28">§</a>
</div>
<p>Update keys maintained by child models and collections only if
the composite map has been initialized (after the constructor
has finished)</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">_compositeMap</span>) {</pre></div></div>
</li>
<li id="section-29">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-29">§</a>
</div>
<p>Process only attributes listed in the composite map</p>
</div>
<div class="content"><div class='highlight'><pre> _.<span class="hljs-title function_">each</span>(<span class="hljs-variable language_">this</span>.<span class="hljs-property">_compositeMap</span>, <span class="hljs-keyword">function</span> (<span class="hljs-params">composite, key</span>) {</pre></div></div>
</li>
<li id="section-30">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-30">§</a>
</div>
<p>Get the nested model or collection for the composite item</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> child = <span class="hljs-variable language_">this</span>[composite.<span class="hljs-property">property</span>];</pre></div></div>
</li>
<li id="section-31">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-31">§</a>
</div>
<p>If the nested model or collection is available, propagate
its current content to the resulting JSON</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (child) {
result[key] = child.<span class="hljs-title function_">toJSON</span>();
}
}, <span class="hljs-variable language_">this</span>);
}
<span class="hljs-keyword">return</span> result;
},</pre></div></div>
</li>
<li id="section-32">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-32">§</a>
</div>
<h2 id="private-functions">Private Functions</h2>
</div>
</li>
<li id="section-33">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-33">§</a>
</div>
</div>
</li>
<li id="section-34">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-34">§</a>
</div>
<p>Merges prototype.composite and options.composite and normalizes
the child model or collection configuration</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-attr">_createCompositeMap</span>: <span class="hljs-keyword">function</span> (<span class="hljs-params">options</span>) {</pre></div></div>
</li>
<li id="section-35">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-35">§</a>
</div>
<p>Allow specifying the composite property as a function returning
the actual configuration object</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> thisComposite = <span class="hljs-variable language_">this</span>.<span class="hljs-property">composite</span>,
optionsComposite = options.<span class="hljs-property">composite</span>;
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> thisComposite === <span class="hljs-string">'function'</span>) {
thisComposite = thisComposite.<span class="hljs-title function_">call</span>(<span class="hljs-variable language_">this</span>, options);
}
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> optionsComposite === <span class="hljs-string">'function'</span>) {
optionsComposite = optionsComposite.<span class="hljs-title function_">call</span>(<span class="hljs-variable language_">this</span>, options);
}
<span class="hljs-keyword">if</span> (thisComposite && <span class="hljs-keyword">typeof</span> thisComposite !== <span class="hljs-string">'object'</span> ||
optionsComposite && <span class="hljs-keyword">typeof</span> optionsComposite !== <span class="hljs-string">'object'</span>) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">'Invalid composite configuration'</span>);
}</pre></div></div>
</li>
<li id="section-36">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-36">§</a>
</div>
<p>Allow the caller to specify additional or override existing
attribute rules defined in the prototype or in the instance</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> composite = _.<span class="hljs-title function_">extend</span>({}, thisComposite, optionsComposite);
<span class="hljs-keyword">return</span> _.<span class="hljs-title function_">reduce</span>(composite, <span class="hljs-keyword">function</span> (<span class="hljs-params">result, model, attribute</span>) {
<span class="hljs-keyword">var</span> property, parse, method;</pre></div></div>
</li>
<li id="section-37">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-37">§</a>
</div>
<p>Just model or collection function object can be used for
convenience</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (model.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span> <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Backbone</span>.<span class="hljs-property">Model</span> ||
model.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span> <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Backbone</span>.<span class="hljs-property">Collection</span>) {
property = attribute;</pre></div></div>
</li>
<li id="section-38">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-38">§</a>
</div>
<p>Otherwise the child model or collection object descriptor
should be an object literal with configuration properties</p>
</div>
<div class="content"><div class='highlight'><pre> } <span class="hljs-keyword">else</span> {
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> model !== <span class="hljs-string">'object'</span>) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">'Invalid composite child descriptor'</span>);
}</pre></div></div>
</li>
<li id="section-39">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-39">§</a>
</div>
<p>Attribute name is the default for the property name</p>
</div>
<div class="content"><div class='highlight'><pre> property = model.<span class="hljs-property">property</span> || attribute;</pre></div></div>
</li>
<li id="section-40">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-40">§</a>
</div>
<p>Make sure that the extra data parsing function is not set
or is a valid function</p>
</div>
<div class="content"><div class='highlight'><pre> parse = model.<span class="hljs-property">parse</span>;
<span class="hljs-keyword">if</span> (parse != <span class="hljs-literal">null</span> && <span class="hljs-keyword">typeof</span> parse !== <span class="hljs-string">'function'</span>) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">'Invalid child model data parse function'</span>);
}
method = model.<span class="hljs-property">method</span>;</pre></div></div>
</li>
<li id="section-41">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-41">§</a>
</div>
<p>Make sure that the child model or collection type is valid</p>
</div>
<div class="content"><div class='highlight'><pre> model = model.<span class="hljs-property">type</span>;
<span class="hljs-keyword">if</span> (!(model.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span> <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Backbone</span>.<span class="hljs-property">Model</span> ||
model.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span> <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Backbone</span>.<span class="hljs-property">Collection</span>)) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">'Invalid composite child model'</span>);
}
}</pre></div></div>
</li>
<li id="section-42">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-42">§</a>
</div>
<p>Avoid replacing an existing prototype member with the child
model or collection instance</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (prototype[property]) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">'Property conflict in the composite prototype'</span>);
}</pre></div></div>
</li>
<li id="section-43">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-43">§</a>
</div>
<p>Use the default data updating method if not specified</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (!method) {
method = model.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span> <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Backbone</span>.<span class="hljs-property">Model</span> ? <span class="hljs-string">'set'</span> : <span class="hljs-string">'add'</span>;
}</pre></div></div>
</li>
<li id="section-44">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-44">§</a>
</div>
<p>Make sure that the data updating method exists in the child
model or collection prototype</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (!model.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>[method]) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">'Invalid chidl model data updating method'</span>);
}</pre></div></div>
</li>
<li id="section-45">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-45">§</a>
</div>
<p>Make every map entry look consistent</p>
</div>
<div class="content"><div class='highlight'><pre> result[attribute] = {
<span class="hljs-attr">model</span>: model,
<span class="hljs-attr">property</span>: property,
<span class="hljs-attr">parse</span>: parse,
<span class="hljs-attr">method</span>: method
};
<span class="hljs-keyword">return</span> result;
}, {});
},</pre></div></div>
</li>
<li id="section-46">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-46">§</a>
</div>
<p>Checks if the changed attributes contained a key, which backs up
a child model or collection and updates the child object
accordingly</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-attr">_updateComposite</span>: <span class="hljs-keyword">function</span> (<span class="hljs-params">attributes, options</span>) {</pre></div></div>
</li>
<li id="section-47">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-47">§</a>
</div>
<p>Creates a new instance of the child model or collection</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">function</span> <span class="hljs-title function_">create</span>(<span class="hljs-params">composite, parameters</span>) {
<span class="hljs-keyword">var</span> createOptions = _.<span class="hljs-title function_">extend</span>({}, composite.<span class="hljs-property">options</span>, options);
<span class="hljs-variable language_">this</span>[composite.<span class="hljs-property">property</span>] = <span class="hljs-keyword">new</span> composite.<span class="hljs-title function_">model</span>(parameters,
createOptions);
}</pre></div></div>
</li>
<li id="section-48">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-48">§</a>
</div>
<p>Ensures that the property with the child model or collection
exists and clears it if</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">function</span> <span class="hljs-title function_">createOrClear</span>(<span class="hljs-params">composite</span>) {
<span class="hljs-keyword">var</span> child = <span class="hljs-variable language_">this</span>[composite.<span class="hljs-property">property</span>];
<span class="hljs-keyword">if</span> (child) {</pre></div></div>
</li>
<li id="section-49">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-49">§</a>
</div>
<p>Clearing an attribute on the main model should clear the
child model or collection; requesting the <code>parse</code> option
gives a hint about re-fetching the entire model, which
should do the same, but not when saving; the server may
respond with incomplete model attributes</p>
<p>TODO: How to handle <code>fetch</code> with suppressed <code>parse</code>?
TODO: How to handle <code>save</code> with suppressed validation?</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (options.<span class="hljs-property">unset</span> || options.<span class="hljs-property">parse</span> && !options.<span class="hljs-property">validate</span>) {
<span class="hljs-keyword">if</span> (child <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Backbone</span>.<span class="hljs-property">Model</span>) {
child.<span class="hljs-title function_">clear</span>(options);
} <span class="hljs-keyword">else</span> {
child.<span class="hljs-title function_">reset</span>(<span class="hljs-literal">undefined</span>, options);
}
}
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (options.<span class="hljs-property">create</span>) {</pre></div></div>
</li>
<li id="section-50">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-50">§</a>
</div>
<p>If called from the constructor or with an undefined or with
an explicit null, force creation of empty child models and
collections, at least</p>
</div>
<div class="content"><div class='highlight'><pre> create.<span class="hljs-title function_">call</span>(<span class="hljs-variable language_">this</span>, composite);
}
}</pre></div></div>
</li>
<li id="section-51">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-51">§</a>
</div>
<p>Propagates the attribute change to the child model ort collection</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">function</span> <span class="hljs-title function_">populate</span>(<span class="hljs-params">composite, value</span>) {</pre></div></div>
</li>
<li id="section-52">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-52">§</a>
</div>
<p>Pre-process the attributes or models before they are
propagated to the child object</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (composite.<span class="hljs-property">parse</span>) {
value = composite.<span class="hljs-property">parse</span>.<span class="hljs-title function_">call</span>(<span class="hljs-variable language_">this</span>, value, options);
}</pre></div></div>
</li>
<li id="section-53">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-53">§</a>
</div>
<p>If called from the constructor, the property will not exist</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">var</span> child = <span class="hljs-variable language_">this</span>[composite.<span class="hljs-property">property</span>];
<span class="hljs-keyword">if</span> (child) {</pre></div></div>
</li>
<li id="section-54">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-54">§</a>
</div>
<p>When the main model is re-fetched, the child models or
collections should be reset; requesting the <code>parse</code> option
gives a hint about it and the <code>validate</code> option discloses
the saving; the server may respond with an incomplete data</p>
<p>TODO: How to handle <code>fetch</code> with suppressed <code>parse</code>?
TODO: How to handle <code>save</code> with suppressed validation?</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (options.<span class="hljs-property">parse</span> && !options.<span class="hljs-property">validate</span>) {
<span class="hljs-keyword">if</span> (child <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Backbone</span>.<span class="hljs-property">Model</span>) {
<span class="hljs-keyword">var</span> missing = _.<span class="hljs-title function_">omit</span>(child.<span class="hljs-property">attributes</span>, _.<span class="hljs-title function_">keys</span>(value));
child.<span class="hljs-title function_">set</span>(missing, {
<span class="hljs-attr">unset</span>: <span class="hljs-literal">true</span>,
<span class="hljs-attr">silent</span>: <span class="hljs-literal">true</span>
});
} <span class="hljs-keyword">else</span> {
child.<span class="hljs-title function_">reset</span>(<span class="hljs-literal">undefined</span>, {<span class="hljs-attr">silent</span>: <span class="hljs-literal">true</span>});
}
}
child[composite.<span class="hljs-property">method</span>](value, options);
} <span class="hljs-keyword">else</span> {
create.<span class="hljs-title function_">call</span>(<span class="hljs-variable language_">this</span>, composite, value);
}
}</pre></div></div>
</li>
<li id="section-55">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-55">§</a>
</div>
<p>Process only attributes listed in the composite map</p>
</div>
<div class="content"><div class='highlight'><pre> _.<span class="hljs-title function_">each</span>(<span class="hljs-variable language_">this</span>.<span class="hljs-property">_compositeMap</span>, <span class="hljs-keyword">function</span> (<span class="hljs-params">composite, key</span>) {</pre></div></div>
</li>
<li id="section-56">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-56">§</a>
</div>
<p>Leave the child model or collection intact if the attributes
do not contain its key</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (_.<span class="hljs-title function_">has</span>(attributes, key)) {
<span class="hljs-keyword">var</span> value = attributes[key];
<span class="hljs-keyword">if</span> (value != <span class="hljs-literal">null</span>) {
populate.<span class="hljs-title function_">call</span>(<span class="hljs-variable language_">this</span>, composite, value);
} <span class="hljs-keyword">else</span> {
createOrClear.<span class="hljs-title function_">call</span>(<span class="hljs-variable language_">this</span>, composite);
}
} <span class="hljs-keyword">else</span> {</pre></div></div>
</li>
<li id="section-57">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-57">§</a>
</div>
<p>If called from the constructor without attributes, force
creation of empty child models and collections, at least</p>
</div>
<div class="content"><div class='highlight'><pre> createOrClear.<span class="hljs-title function_">call</span>(<span class="hljs-variable language_">this</span>, composite);
}
}, <span class="hljs-variable language_">this</span>);
}
});
};</pre></div></div>
</li>
<li id="section-58">
<div class="annotation">
<div class="sswrap ">
<a class="ss" href="#section-58">§</a>
</div>
<p>Export the function to apply the mixin to a prototype either as a result
of this module callback or as a property on the <code>Backbone</code> object</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> <span class="hljs-title class_">Backbone</span>.<span class="hljs-property">mixinCompositeModel</span>;
}));</pre></div></div>
</li>
</ul>
</div>
</body>
</html>