Пример #1
0
 clone: function(n1, n2) {
   if (Util._.isUndefined(n1)) {
     n1 = this.node1;
   }
   if (Util._.isUndefined(n2)) {
     n2 = this.node2;
   }
   return new Creates(n1, n2, this.spec);
 },
Пример #2
0
 return function(relation) {
   var otherNode = relation.node1.isDescendantOf(clone) ? relation.node2 : relation.node1;
   var isAreRelated = Util._.reduce(
     Util._.map(areRelatedNodes, function(node) { return otherNode.isDescendantOf(node)} ),
     function(memo, result) { return result || memo },
     false
   );
   return (! isAreRelated);
 };
Пример #3
0
 optsFor: function(node) {
   var toRet = {};
   Util._.extend(toRet, this.defaultOpts);
   if (this.node1 === node) {
     if (this.spec && this.spec.selectionSpec1) {
       Util._.extend(toRet, this.spec.selectionSpec1.props);
     }
   } else if (this.node2 == node) {
     if (this.spec && this.spec.selectionSpec1) {
       Util._.extend(toRet, this.spec.selectionSpec2.props);
     }
   }
   return toRet;
 },
Пример #4
0
 addSpecs: function(specs) {
   var self = this;
   var promises = Util._.map(specs, function(spec) {
     return self.addSpec(spec);
   });
   return Util.Promise.all(promises);
 },
Пример #5
0
 Util._.each(this.forrestSpecs, function(fs) {
   Util._.each(fs.dependencySpecs, function(ds) {
     if (!(ds.loaded === true)) {
       ds.load();
     }
   });
 });
Пример #6
0
Файл: node.js Проект: cts/model
 getIterables: function(opts) {
   var kids = this.getChildren();
   opts = opts || {};
   var prefix = 0;
   var suffix = 0;
   if (opts.prefix) {
     prefix = opts.prefix;
   }
   if (opts.suffix) {
     suffix = opts.suffix;
   }
   var iterables = kids.slice(prefix, kids.length - suffix);
   if (opts.item) {
     if (Util._.isUndefined(parseInt(opts.item))) {
       if (opts.item.toLowerCase() == 'random') {
         var item = iterables[Math.floor(Math.random()*iterables.length)];
         iterables = [item];
       }
     } else {
       // We're one-indexed
       var index = parseInt(opts.item)
       iterables = iterables.slice(index, 1);
     }
   }
   if (opts.limit) {
     iterables = iterables.slice(0, limit);
   }
   return iterables;
 },
Пример #7
0
Файл: node.js Проект: cts/model
  removeChild: function(node, throwEvent) {
    var index = Util._.indexOf(this.children, node);
    if (index > -1) {

      Util.Helper.arrDelete(this.children, index, index);

      if (this._subclass_removeChild) {
        this._subclass_removeChild(node, index);
      }

      if (throwEvent) {
        var t = new Transform({
          operation: 'node-removed',
          treeName: this.tree.name,
          treeUrl: this.tree.spec.url,
          node: this,
          value: node,
          args: {
            index: index
          }
        });
        this.announceTransform(t);
      }        
    } else {
      CTS.Log.Error("Tried to remove child that wasn't in parent.");
    }
  },
Пример #8
0
 truthyOrFalsy: function(node) {
   if ((node == NonexistentNode) || (node == null) || Util._.isUndefined(node)) {
     return false;
   }
   var val = node.getIfExistValue();
   return Util.Helper.truthyOrFalsy(val);
 },
Пример #9
0
Файл: node.js Проект: cts/model
 function(forrestSpecs) {
   Util._.each(forrestSpecs, function(forrestSpec) {
     if (typeof forrestSpec.relationSpecs != 'undefined') {
       self.inlineRelationSpecs = forrestSpec.relationSpecs;
     }
   });
   deferred.resolve();
 },
Пример #10
0
Файл: node.js Проект: cts/model
 setProvenance: function(tree, node) {
   this.provenance = {
     tree: tree
   }
   if (! Util._.isUndefined(node)) {
     this.provenance.node = node;
   }
 },
Пример #11
0
Файл: node.js Проект: cts/model
 unregisterRelation: function(relation, dontRefresh) {
   var events = relation.eventInterestFor(this);
   for (var i = 0; i < events.length; i++) {
     this.off(events[i], relation.handleEventFromNode, relation);
   }
   if (dontRefresh) return;
   this.relations = Util._.filter(this.relations,
     function(r) { return r != relation; });
 },
Пример #12
0
var GColumnNode = function(value, columns, tree, opts) {
  opts = opts || {};
  this.initializeNodeBase(tree, opts);
  this.value = value;
  this.columnNum = null;
  this.columns = columns;
  this.ctsId = Util._.uniqueId().toString();
  this.kind = 'GColumn';
};
Пример #13
0
Файл: node.js Проект: cts/model
 function() {
   Util.Promise.all(Util._.map(self.children, function(kid) {
      return kid.parseInlineRelationSpecsRecursive();
   })).then(function() {
     d.resolve();
   }, function(reason) {
     d.reject(reason);
   });
 },
Пример #14
0
 setVisibility: function(val, opts, relation) {
   if (val) {
     if (Util._.isUndefined(opts) || Util._.isUndefined(opts.attribute)) {
       this.value.show();
     } else {
       if (this.value.attr('cts-hidden-' + opts.attribute)) {
         this.value.attr(opts.attribute, this.value.attr('cts-hidden-' + opts.attribute));
       }
     }
   } else {
     if (Util._.isUndefined(opts) || Util._.isUndefined(opts.attribute)) {
       this.value.hide();
     } else {
       this.value.attr('cts-hidden-' + opts.attribute, this.value.attr(opts.attribute));   
       this.value.attr(opts.attribute, null);
     }
   }
 },
Пример #15
0
var GCellFeedNode = function(spec, tree, opts) {
  opts = opts || {};
  this.initializeNodeBase(tree, opts);
  this.spec = spec;
  this.ctsId = Util._.uniqueId().toString();
  this.kind = 'GCellFeed';
  this.on('received-is', function() {
    this.value.trigger('cts-received-is');
  });
};
Пример #16
0
 realizeTrees: function() {
   var promises = [];
   Util._.each(this.treeSpecs, function(treeSpec, name, list) {
     if (! Util._.has(this.trees, name)) {
       Util.Log.Info("Promising to realize tree", treeSpec);
       promises.push(this.realizeTree(treeSpec));
     }
   }, this);
   return Util.Promise.all(promises);
 },
Пример #17
0
Файл: node.js Проект: cts/model
  getSubtreeRelations: function() {
    return Util._.union(this.getRelations(), Util._.flatten(
      Util._.map(this.getChildren(), function(kid) {
        return kid.getSubtreeRelations();
      }))
    );
    /*
       var deferred = Q.defer();

    this.getRelations().then(function(relations) {
      var kidPromises = Util._.map(this.getChildren(), function(kid) {
        return kid.getSubtreeRelations();
      });
      if (kidPromises.length == 0) {
        deferred.resolve(relations);
      } else {
        Q.allSettled(kidPromises).then(function(results) {
          var rejected = false
          var kidRelations = [];
          results.forEach(function(result) {
            if (result.state == "fulfilled") {
              kidRelations.push(result.value);
            } else {
              rejected = true;
              Util.Log.Error(result.reason);
              deferred.reject(result.reason);
            }
          });
          if (!rejected) {
            var allR = Util._.union(relations, Util._.flatten(kidRelations));
            deferred.resolve(allR);
          }
        });
      }
    }, function(reason) {
      deferred.reject(reason);
    });

    return deferred.promise;
    */
  },
Пример #18
0
  realizeDependencies: function() {
    Util._.each(this.forrestSpecs, function(fs) {
      Util._.each(fs.dependencySpecs, function(ds) {
        if (!(ds.loaded === true)) {
          ds.load();
        }
      });
    });

    // A no-op, just to fit in with boot and later potential deps.
    return Util.Promise.resolve();
  },
Пример #19
0
var GColumnCellNode = function(row, spec, tree, opts) {
  opts = opts || {};
  this.initializeNodeBase(tree, opts);
  this.row = row;
  this.spec = spec;
  this.value = spec.content;
  this.colNum = spec.colNum;
  this.ctsId = Util._.uniqueId().toString();
  this.kind = 'GColumnCell';
  this.shouldReceiveEvents = true;
  this.shouldThrowEvents = true;
};
Пример #20
0
Файл: node.js Проект: cts/model
 function() {
   var promises = Util._.map(self.children, function(child) {
     return child.realizeChildren();
   });
   Util.Promise.all(promises).then(
     function() {
       deferred.resolve();
     },
     function(reason) {
       deferred.reject(reason);
     }
   ).done();
 },
Пример #21
0
Файл: node.js Проект: cts/model
 containsRelation: function(relation) {
   if (! this.relations) {
     return false;
   }
   if (Util._.contains(this.relations, relation)) {
     return true;
   }
   for (var i = 0; i < this.relations.length; i++) {
     if (this.relations[i].equals(relation)) {
       return true;
     }
   }
   return false;
 },
Пример #22
0
Файл: node.js Проект: cts/model
  getIterableLineage: function(myParent, myIndex, ret) {
    var self = this;
    if (typeof ret == 'undefined') {
      ret = [];
    }

    if (typeof myParent == 'undefined') {
      myParent = self.parentNode;
    }

    if (myParent == null) {
      return ret;
    }

    var ares = Util._.filter(myParent.relations, function(r) {
      return (r.name == 'are');
    });

    if (ares.length > 0) {
      for (var i = 0; i < ares.length; i++) {
        var are = ares[i];
        var relatedContainer = (are.node1 == myParent) ? are.node2 : are.node1;
        if (typeof myIndex == 'undefined') {
          myIndex = Util._.indexOf(myParent.getIterables(are.optsFor(myParent)), self);
        }
        var iterables = relatedContainer.getIterables(are.optsFor(relatedContainer));
        if ((myIndex < iterables.length) && (myIndex > -1)) {
          var iterable = iterables[myIndex];
          ret.push([relatedContainer, iterable]);          
        } else {
          ret.push([relatedContainer, undefined]);
        }
      }
    }

    return myParent.getIterableLineage(undefined, undefined, ret);
  },
Пример #23
0
Файл: node.js Проект: cts/model
  makeIterableLineageFilter: function(myParent, myIndex) {
    var iterableLineage = this.getIterableLineage(myParent, myIndex);

    var filterFns = Util._.map(iterableLineage, function(tup) {
      return Util.Helper.rejectUnless(tup[0], tup[1]);
    });

    var filterFn = function(r) {
      var passed = true;
      for (var i = 0; i < filterFns.length; i++) {
        passed = passed && filterFns[i](r);
      }
      return passed;
    }
    return filterFn;
  },
Пример #24
0
 parseAndAddSpecsFromLinks: function(links) {
   var self = this;
   var promises = Util._.map(links, function(block) {
     var deferred = Util.Promise.defer();
     if (block.type == 'link') {
       Util.Net.fetchString(block).then(
         function(content) {
           var url = block.url;
           self.parseAndAddSpec(content, block.format, url).then(
             function() {
               deferred.resolve();
            },
            function(reason) {
              Util.Log.Error("Could not parse and add spec", content, block);
              deferred.resolve();
            }
          ).done();
        },
        function(reason) {
          Util.Log.Error("Could not fetch CTS link:", block);
          deferred.resolve();
        });
     } else if (block.type == 'block') {
       var url = window.location;
       self.parseAndAddSpec(block.content, block.format, url).then(
         function() {
           deferred.resolve();
         },
         function(reason) {
           Util.Log.Error("Could not parse and add spec", content, block);
           deferred.resolve();
         }
       ).done();
     } else {
       Util.Log.Error("Could not load CTS: did not understand block type", block.block, block);
       deferred.resolve();
     }
     return deferred.promise;
   });
   return promises;
 },
Пример #25
0
 function(specs) {
   console.log(specs);
   if (fromUrl != 'undefined') {
     Util._.each(specs, function(spec) {
       for (i = 0; i < spec.dependencySpecs.length; i++) {
         spec.dependencySpecs[i].loadedFrom = fromUrl;
       }
       for (i = 0; i < spec.treeSpecs.length; i++) {
         spec.treeSpecs[i].loadedFrom = fromUrl;
       }
     });
   }
   self.addSpecs(specs).then(
     function() {
       deferred.resolve(specs);
     },
     function(reason) {
       deferred.reject(reason);
     }
   ).done();
 },
Пример #26
0
// Instance Methods
// ----------------
Util._.extend(GSpreadsheetTree.prototype, Model.Tree.Base, {
  setRoot: function(node) {
    this.root = node;
    this.root.setProvenance(this);
  },

  find: function(spec) {
    if (spec.inline) {
      return [spec.inlineObject];
    } else {
      // This passes in the SPEC rather than the selector.
      var results = this.root.find(spec);
      return results;
    }
  },

  listenForNodeInsertions: function(new_val) {
  },

  open: function() {
    var base = 'https://docs.google.com/spreadsheets/d/';
    var url = base + this.spec.sskey;
    window.open(url);
  }
});

module.exports = GSpreadsheetTree;
Пример #27
0
Util._.extend(Transform.prototype, {
  toJson: function() {
    var t = {
      operation: this.operation,
      appContext: this.appContext,
      treeName: this.treeName,
      nodeIdentifier: this.nodeIdentifier,
      value: this.value,
      args: this.args,
      guid: this.guid
    };
    return t;
  },

  registerMimic: function(transform) {
    if (this.mimicsOfMe == null) {
      this.mimicsOfMe = [];
    }
    this.mimicsOfMe.push(transform);
    transform.mimicOf = this;
  },

  changeState: function(newState) {
    if (! (newState === this.state)) {
      this.state = newState;
      if (this.node && this.node.transformStateChanged) {
        this.node.transformStateChanged(this);
      }
      if (this.mimicOf) {
        this.mimicOf.changeState(newState);
      }
      if (this.mimicsOfMe && this.mimicsOfMe.length) {
        for (var i = 0; i < this.mimicsOfMe.length; i++) {
          this.mimicsOfMe[i].changeState(newState);
        }
      }
    }
  },

  relayFor: function(node) {
    if (this.mimicOf && this.mimicOf.node && (this.mimicOf.node == node)) {
      return null;
    }
    if (this.mimicsOfMe && this.mimicsOfMe.length) {
      for (var i = 0; i < this.mimicsOfMe.length; i++) {
        if (this.mimicsOfMe[i] && this.mimicsOfMe[i].node && (this.mimicsOfMe[i].node == node)) {
          return null;
        }
      }
    }
    var t = new Transform(this);
    t.node = node;
    t.treeName = node.tree.name;
    t.nodeIdentifier = null;
    this.registerMimic(t);
    return t;
  }

});
Пример #28
0
  _runCreation: function(newItemContainer, newItemOpts, intoList, intoListOpts) {
    // Step 1: Assume iterable on FROM side.
    var self = this;
    var deferred = Util.Promise.defer();

    // 2. Mark all ARE-related relations as for graft.
    var areRelatedNodes = Util._.filter(intoList.relations, function(relation) {
      return (relation.name == 'are');
    }).map(function(r) { return r.opposite(intoList)});

    var filterFn = function(clone) {
      return function(relation) {
        var otherNode = relation.node1.isDescendantOf(clone) ? relation.node2 : relation.node1;
        var isAreRelated = Util._.reduce(
          Util._.map(areRelatedNodes, function(node) { return otherNode.isDescendantOf(node)} ),
          function(memo, result) { return result || memo },
          false
        );
        return (! isAreRelated);
      };
    };

    var beforePersistence = function(clone) {

    };

    // Step 1: Clone an iterable from the GRAFT-related container.
                     // list      cloneSource           dest(last) event  beforePFn  cloneRelns
    var iterables = intoList.getIterables(this.optsFor(intoList));

    // var nextTimeFn = function() {
    //   return intoList.cloneIterable(iterables.length - 1, undefined, false, beforePersistence, true, undefined, self.optsFor(intoList)); 
    // };
    // var firstTimeFn = function() {
    //   return intoList.cloneIterable(iterables.length - 1, undefined, false, beforePersistence, true, undefined, self.optsFor(intoList)); 
    // };
    // var cloneFn = (iterables.length > 0) ? nextTimeFn : firstTimeFn;
    // var promise = cloneFn();

    var promise = intoList.cloneIterable(iterables.length - 1, undefined, false, undefined, true, undefined, self.optsFor(intoList));

    promise.then(
      function(clone) {
        if (!clone) {
          debugger;
          return deferred.reject("Clone null.");
        }

        // Step 3: Filter relations
        
        var otherFilterFn = clone.makeIterableLineageFilter(intoList, iterables.length);

        clone.pruneRelations(undefined, undefined, otherFilterFn);

        var filterFnInst = filterFn(clone);
        filterFnInst.deletionPolicy = 'mark';
        filterFnInst.passPolicy = 'mark';
        clone.pruneRelations(undefined, undefined, filterFnInst);

        var graftsToMe = Util._.filter(intoList.getRelations(), function(r) {
          return ((r.name == 'creates') && (r.node1 != newItemContainer) && (r.node2 != newItemContainer))
        });
        var graftFilterFn = function(r) {
          // FALSE if a node is inside a subtree which grafts to me.
          for (var i = 0; i < graftsToMe.length; i++) {
            var relation = graftsToMe[i];
            var otherParent = (relation.node1 == newItemContainer) ? relation.node2 : relation.node1;
            if (r.node1.isDescendantOf(otherParent) || r.node2.isDescendantOf(otherParent)) {
              return false;
            }
          }
          return true;
        };

        clone.pruneRelations(undefined, undefined, graftFilterFn);

        // clone.pruneRelations(clone.parentNode, clone);
        // Step 4: Process Incoming Relations
        clone._processIncoming(undefined, {disableRemote: true, directional: false}, clone).then(
          function() {
            // Step 5: Reverse Filter
            var oppFilterFn = function(relation) {
              return (!filterFnInst(relation));
            };
            oppFilterFn.deletionPolicy = 'mark';
            clone.pruneRelations(undefined, undefined, oppFilterFn);
            self.markNodeRelationsAsForGraft(newItemContainer, true, true);

            var t = new Model.Transform({
              operation: 'node-inserted',
              treeName: intoList.tree.spec.name,
              treeUrl: intoList.tree.spec.url,
              node: intoList,
              value: clone,          
              args: {
                index: iterables.length
              }
            });
            intoList.announceTransform(t);

            deferred.resolve(clone);
          },
          function(err) {
            deferred.reject(err);
          }
        ).done();

        var fn = function(r) {
          return false;
        };
        fn.deletionPolicy = 'mark';
        for (var i = 0; i < newItemContainer.children.length; i++) {
          var child = newItemContainer.children[i];
          child.pruneRelations(undefined, undefined, fn);
        }

        // Step 6: Call childInserted on the receiving container
        // intoList.trigger("ChildInserted", {
        //   eventName: "ChildInserted",
        //   ctsNode: clone,
        //   sourceNode: intoList,
        //   sourceTree: intoList.tree,
        //   afterIndex: iterables.length - 1
        // });
        // intoList.insertChild(clone, iterables.length - 1, true, true);
      },
      function(err) {
        console.log(err);
        deferred.reject(err);
      }
    ).done();

    return deferred.promise;
  },
Пример #29
0
      function(clone) {
        if (!clone) {
          debugger;
          return deferred.reject("Clone null.");
        }

        // Step 3: Filter relations
        
        var otherFilterFn = clone.makeIterableLineageFilter(intoList, iterables.length);

        clone.pruneRelations(undefined, undefined, otherFilterFn);

        var filterFnInst = filterFn(clone);
        filterFnInst.deletionPolicy = 'mark';
        filterFnInst.passPolicy = 'mark';
        clone.pruneRelations(undefined, undefined, filterFnInst);

        var graftsToMe = Util._.filter(intoList.getRelations(), function(r) {
          return ((r.name == 'creates') && (r.node1 != newItemContainer) && (r.node2 != newItemContainer))
        });
        var graftFilterFn = function(r) {
          // FALSE if a node is inside a subtree which grafts to me.
          for (var i = 0; i < graftsToMe.length; i++) {
            var relation = graftsToMe[i];
            var otherParent = (relation.node1 == newItemContainer) ? relation.node2 : relation.node1;
            if (r.node1.isDescendantOf(otherParent) || r.node2.isDescendantOf(otherParent)) {
              return false;
            }
          }
          return true;
        };

        clone.pruneRelations(undefined, undefined, graftFilterFn);

        // clone.pruneRelations(clone.parentNode, clone);
        // Step 4: Process Incoming Relations
        clone._processIncoming(undefined, {disableRemote: true, directional: false}, clone).then(
          function() {
            // Step 5: Reverse Filter
            var oppFilterFn = function(relation) {
              return (!filterFnInst(relation));
            };
            oppFilterFn.deletionPolicy = 'mark';
            clone.pruneRelations(undefined, undefined, oppFilterFn);
            self.markNodeRelationsAsForGraft(newItemContainer, true, true);

            var t = new Model.Transform({
              operation: 'node-inserted',
              treeName: intoList.tree.spec.name,
              treeUrl: intoList.tree.spec.url,
              node: intoList,
              value: clone,          
              args: {
                index: iterables.length
              }
            });
            intoList.announceTransform(t);

            deferred.resolve(clone);
          },
          function(err) {
            deferred.reject(err);
          }
        ).done();

        var fn = function(r) {
          return false;
        };
        fn.deletionPolicy = 'mark';
        for (var i = 0; i < newItemContainer.children.length; i++) {
          var child = newItemContainer.children[i];
          child.pruneRelations(undefined, undefined, fn);
        }

        // Step 6: Call childInserted on the receiving container
        // intoList.trigger("ChildInserted", {
        //   eventName: "ChildInserted",
        //   ctsNode: clone,
        //   sourceNode: intoList,
        //   sourceTree: intoList.tree,
        //   afterIndex: iterables.length - 1
        // });
        // intoList.insertChild(clone, iterables.length - 1, true, true);
      },
Пример #30
0
Util._.extend(Creates.prototype, Model.Relation.Base, {
  execute: function(toward) {
    if (this.forGraftOnly()) {
      return;
    }

    var opp = this.opposite(toward);

    // A node that participates as a graft soure
    // Should have all its relations removed, as only clones of it will
    // ever actually do anything.
    // opp.pruneRelations(null, null);

    var towardOpts = this.optsFor(toward);
    var fromOpts   = this.optsFor(opp);
    var res;

    var invoker = toward;
    var invokerOpts = towardOpts;
    var template = opp;
    var templateOpts = fromOpts;

    var d = Util.Promise.defer();
    var self = this;

    opp.addGrafter(toward);

    // Now we need to mark all the relations as for a graft so that we don't
    // accidentally execute them during a render.
    // We also need to mark the nodes in case *other* relations
    // bind in after we're done here. E.g., if this template is at the
    // top of the document and others bind in from below.
    for (var i = 0; i < invoker.children.length; i++) {
      var child = invoker.children[i];
      this.markNodeRelationsAsForGraft(child, true, true);
      child.forGraftOnly(true, true);
    }

    invoker.value.on('submit', function(e) {
      // Throw a transform.
      e.preventDefault();
      e.stopPropagation();
      self._runCreation(invoker, invokerOpts, template, templateOpts).then(
        function(newElem) {
          invoker.value.find('input[type="email"], input[type="text"], textarea').val('');

          // Maybe there is a callback on the options for the receiver.
          if (invokerOpts && invokerOpts.after) {
            invoker.tree.forrest.afterDepsLoaded(function() {
              if (window && (typeof window[invokerOpts.after] == 'function')) {
                window[invokerOpts.after](invoker, template, self);
              } else {
                console.log("Creates could not invoke callback.");
              }                
            })
          }
        },
        function(err) {
          if (invokerOpts && invokerOpts.fail) {
            invoker.tree.forrest.afterDepsLoaded(function() {
              if (window && (typeof window[invokerOpts.fail] == 'function')) {
                window[invokerOpts.fail](invoker, template, self);
              } else {
                console.log("Creates could not invoke error callback.");
              }                
            })
          }

        }


      ).done();
    });

    d.resolve();
    return d.promise;
  },

  _runCreation: function(newItemContainer, newItemOpts, intoList, intoListOpts) {
    // Step 1: Assume iterable on FROM side.
    var self = this;
    var deferred = Util.Promise.defer();

    // 2. Mark all ARE-related relations as for graft.
    var areRelatedNodes = Util._.filter(intoList.relations, function(relation) {
      return (relation.name == 'are');
    }).map(function(r) { return r.opposite(intoList)});

    var filterFn = function(clone) {
      return function(relation) {
        var otherNode = relation.node1.isDescendantOf(clone) ? relation.node2 : relation.node1;
        var isAreRelated = Util._.reduce(
          Util._.map(areRelatedNodes, function(node) { return otherNode.isDescendantOf(node)} ),
          function(memo, result) { return result || memo },
          false
        );
        return (! isAreRelated);
      };
    };

    var beforePersistence = function(clone) {

    };

    // Step 1: Clone an iterable from the GRAFT-related container.
                     // list      cloneSource           dest(last) event  beforePFn  cloneRelns
    var iterables = intoList.getIterables(this.optsFor(intoList));

    // var nextTimeFn = function() {
    //   return intoList.cloneIterable(iterables.length - 1, undefined, false, beforePersistence, true, undefined, self.optsFor(intoList)); 
    // };
    // var firstTimeFn = function() {
    //   return intoList.cloneIterable(iterables.length - 1, undefined, false, beforePersistence, true, undefined, self.optsFor(intoList)); 
    // };
    // var cloneFn = (iterables.length > 0) ? nextTimeFn : firstTimeFn;
    // var promise = cloneFn();

    var promise = intoList.cloneIterable(iterables.length - 1, undefined, false, undefined, true, undefined, self.optsFor(intoList));

    promise.then(
      function(clone) {
        if (!clone) {
          debugger;
          return deferred.reject("Clone null.");
        }

        // Step 3: Filter relations
        
        var otherFilterFn = clone.makeIterableLineageFilter(intoList, iterables.length);

        clone.pruneRelations(undefined, undefined, otherFilterFn);

        var filterFnInst = filterFn(clone);
        filterFnInst.deletionPolicy = 'mark';
        filterFnInst.passPolicy = 'mark';
        clone.pruneRelations(undefined, undefined, filterFnInst);

        var graftsToMe = Util._.filter(intoList.getRelations(), function(r) {
          return ((r.name == 'creates') && (r.node1 != newItemContainer) && (r.node2 != newItemContainer))
        });
        var graftFilterFn = function(r) {
          // FALSE if a node is inside a subtree which grafts to me.
          for (var i = 0; i < graftsToMe.length; i++) {
            var relation = graftsToMe[i];
            var otherParent = (relation.node1 == newItemContainer) ? relation.node2 : relation.node1;
            if (r.node1.isDescendantOf(otherParent) || r.node2.isDescendantOf(otherParent)) {
              return false;
            }
          }
          return true;
        };

        clone.pruneRelations(undefined, undefined, graftFilterFn);

        // clone.pruneRelations(clone.parentNode, clone);
        // Step 4: Process Incoming Relations
        clone._processIncoming(undefined, {disableRemote: true, directional: false}, clone).then(
          function() {
            // Step 5: Reverse Filter
            var oppFilterFn = function(relation) {
              return (!filterFnInst(relation));
            };
            oppFilterFn.deletionPolicy = 'mark';
            clone.pruneRelations(undefined, undefined, oppFilterFn);
            self.markNodeRelationsAsForGraft(newItemContainer, true, true);

            var t = new Model.Transform({
              operation: 'node-inserted',
              treeName: intoList.tree.spec.name,
              treeUrl: intoList.tree.spec.url,
              node: intoList,
              value: clone,          
              args: {
                index: iterables.length
              }
            });
            intoList.announceTransform(t);

            deferred.resolve(clone);
          },
          function(err) {
            deferred.reject(err);
          }
        ).done();

        var fn = function(r) {
          return false;
        };
        fn.deletionPolicy = 'mark';
        for (var i = 0; i < newItemContainer.children.length; i++) {
          var child = newItemContainer.children[i];
          child.pruneRelations(undefined, undefined, fn);
        }

        // Step 6: Call childInserted on the receiving container
        // intoList.trigger("ChildInserted", {
        //   eventName: "ChildInserted",
        //   ctsNode: clone,
        //   sourceNode: intoList,
        //   sourceTree: intoList.tree,
        //   afterIndex: iterables.length - 1
        // });
        // intoList.insertChild(clone, iterables.length - 1, true, true);
      },
      function(err) {
        console.log(err);
        deferred.reject(err);
      }
    ).done();

    return deferred.promise;
  },

  clone: function(n1, n2) {
    if (Util._.isUndefined(n1)) {
      n1 = this.node1;
    }
    if (Util._.isUndefined(n2)) {
      n2 = this.node2;
    }
    return new Creates(n1, n2, this.spec);
  },

  eventInterestFor: function(n) {
    return ['transform'];
  },

  _subclass_handleEventFromNode: function(evt, thrownFrom, thrownTo) {
    if (evt.operation) {
      if (evt.operation == 'node-inserted') {    
        this.relayTransform(evt, thrownFrom, thrownTo);
      }
    } 
  },   

  markNodeRelationsAsForGraft: function(node, val, recurse, insideOtherSubtree) {
    var rs = node.getRelations();
    if (typeof insideOtherSubtree == 'undefined') {
      insideOtherSubtree = false;
    }
    for (var i = 0; i < rs.length; i++) {
      if (insideOtherSubtree) {
        if ((rs[i].node1 == node) && (rs[i].node2.isDescendantOf(insideOtherSubtree))) {
          rs[i].forGraftOnly(val);          
        } else if ((rs[i].node2 == node) && (rs[i].node1.isDescendantOf(insideOtherSubtree))) {
          rs[i].forGraftOnly(val);
        }
      } else {
        rs[i].forGraftOnly(val);
      }
    }
    if (recurse) {
      for (var i = 0; i < node.children.length; i++) {
        this.markNodeRelationsAsForGraft(node.children[i], val, recurse, insideOtherSubtree);
      }
    }
  }

});