Remoto - VFS: nodeProperties.js Source File
Remoto - VFS
nodeCanvas/nodeProperties.js
Go to the documentation of this file.
1 
2 define( [
3  'require',
4  'remoto!stdlib:js/panes/pane.js',
5  'remoto!stdlib:js/panes/panes/form.js',
6  'remoto!stdlib:js/paneManager/paneManager.js',
7  'remoto!stdlib:js/include/objectRegistry.js',
8  'remoto!stdlib:js/widgets/widgets/colorWidget.js',
9  'remoto!stdlib:js/panes/panes/nodeCanvas/node.js',
10  'include/modal/modal',
11  'remoto!stdlib:js/include/preferences.js',
12  'remoto!stdlib:js/include/utils.js',
13  'remoto!stdlib:js/panes/panes/paneLoader.svg',
14  'remoto!stdlib:js/panes/panes/nodeCanvas/nodeProperties.css',
15  'jquery/ui/jquery.ui.sortable',
16  ],
17  function(require,pane,form,paneManager,objectRegistry,colorWidget,node,modal,preferences,utils,spinner)
18  {
19  'use strict';
20 
41  nodeProperties.prototype = new pane;
42  function nodeProperties(layout)
43  {
44  pane.call(this, layout);
45 
46  if (!layout) return;
47 
48  this._type = "nodeProperties";
49  //this._maxItems = layout.maxItems || 10;
50  this._context = layout.metadata ? (layout.metadata.context || {}) : {};
51  this._nodeMenu = layout.nodeMenu || "";
52  this._enabledFilter = layout.enabledFilter || null;
53  //this._widgets = {}; //for the header
54 
55  //this._saveFields = { maxItems:"_maxItems", nodeMenu:"_nodeMenu" };
56  this._saveFields = { nodeMenu:"_nodeMenu", enabledFilter:"_enabledFilter" };
57 
58  this._context.preferencesBase = preferences.fetch("preferencesBase");
59  //console.log("node properties",layout);
60 
61  this._forms = {}; //the forms/nodes we've subscribed to
62 
63  this._objectLoader = null;
64  this._selection = {};
65 
66  this.createHTML(); //FIXME: it seems like all panes should createHTML in their constructor!
67  }
68 
78  nodeProperties.prototype.createHTML = function()
79  {
80  if (this._html) return this._html;
81 
82  pane.prototype.createHTML.call(this);
83 
84  this._content.addClass("nodeProperties");
85 
86  return this._html;
87  }
88 
98  /*
99  nodeProperties.prototype.createHeader = function()
100  {
101  if (this._header) return this._header;
102 
103  pane.prototype.createHeader.call(this);
104 
105  var b;
106  var THIS = this;
107  var h = this._header;
108 
109  //h.append("header!");
110 
111  b = $("<button style='margin-right:3px;'>").text("Clear").appendTo(h);
112 
113  b = this._widgets["mi"] = $("<input title='Max Items' type='number' name='mi' value='10' step='1' min='1' max='10'/>").appendTo(h);
114  b.bind("change", function(e) {
115  var v = $(this).val();
116  THIS._maxItems = v;
117  paneManager.saveLayout();
118  } );
119  b.val( this._maxItems );
120 
121  return this._header;
122  }
123  */
124 
141  nodeProperties.prototype.applySubscription = function(data,metadata)
142  {
143  //console.log("nodeProperties apply subscription",arguments);
144  //console.log( this._forms );
145 
146  //var base = this._path;
147 
148  if ("context" in metadata)
149  { this._context = metadata.context;
150  //this._context = utils.resolveContextValues(metadata.context,this._context);
151  }
152 
153  if (!this._objectLoader)
154  this._objectLoader = new objectRegistry.objectLoader(this._nodeMenu || "");
155 
156  var FF = Object.keys(this._forms);
157 
158  var __p;
159  for (var p in data)
160  {
161  //console.log(p);
162  __p = this._path+"/"+p;
163  //console.warn("SUB: "+__p);
164 
165  if (!(__p in this._forms))
166  {
167  this._forms[__p] = new nodePropertiesLoader( __p, this );
168  }
169  else
170  { //console.error("path "+__p+" already exists.");
171  FF = FF.filter(function(e) { return e !== __p });
172  }
173  }
174 
175  //FIXME: remove items that were removed while disconnected
176  if (FF.length)
177  {
178  //console.error("Should remove items during subscribe",FF,this._forms);
179  var DD = {};
180  FF.map( function(key) { DD[key] = null; } );
181  this.applyDiff(DD);
182  }
183 
184  //this._subscribed = true;
185  }
186 
197  nodeProperties.prototype.applyDiff = function(diff,user)
198  {
199  //console.log("nodeProperties apply diff",diff,this,objectRegistry);
200 
201  //hide all the showing forms
202  this._content.children(".nodePropertiesForm").hide();
203 
204  var d,__p,f;
205  for (var p in diff)
206  {
207  d = diff[p];
208  //console.log( d );
209 
210  __p = p.substr(0,this._path.length) === this._path ? p : this._path+"/"+p;
211 
212  if (__p in this._forms)
213  {
214  if (d === null)
215  {
216  //console.log("delete form: "+p+" / "+__p);
217  //console.log(this._forms);
218 
219  f = this._forms[__p];
220  f.destroy();
221  objectRegistry.unregisterObject(__p,f,true); //have to manually do this because normally the pane manager would handle it
222  delete this._forms[__p];
223  delete this._selection[__p];
224  }
225  else if (typeof d != "boolean")//if ("_nodeClass" in d) //we assume it's a fully formed object, not a diff indicating a node to create or destroy
226  {
227  //console.log("show: "+p);
228 
229  //show only the selected forms
230  if ("_wrapper" in this._forms[__p])
231  { this._forms[__p]._wrapper.wrapper.show();
232  this._selection[__p] = this._forms[__p];
233  }
234  }
235  else if (d === false)
236  {
237  delete this._selection[__p];
238  }
239  }
240  else if ( d instanceof node )
241  {
242  console.log("was node!",__p,d);
243 
244  //this._forms[__p] = new nodePropertiesLoader( __p, this, d._attributes, d._icon );
245  this._forms[__p] = new nodePropertiesLoader( __p, this );
246  }
247  else if ( d !== null && d !== false )// if (!(__p in this._forms)) //does it need to be created?
248  {
249  //console.error("Creating: "+__p);
250  //console.error("For: "+p);
251  //console.error("Path: "+this._path);
252  //console.log("new node properties loader",d);
253 
254  //create or delete forms here...
255  this._forms[__p] = new nodePropertiesLoader( __p, this, d.diff );//, d.diff.icon );
256  }
257  }
258 
259  for (var s in this._selection)
260  {
261  this._selection[s]._wrapper.wrapper.show();
262  }
263  }
264 
273  nodeProperties.prototype.applySettings = function(settings)
274  {
275  /*
276  if (!("maxItems" in settings))
277  settings.maxItems = 10
278 
279  if (settings.maxItems && this._widgets["mi"])
280  { this._widgets["mi"].val(settings.maxItems);
281  this._maxItems = settings.maxItems;
282  }
283  else
284  { console.error("No maxItems in settings, or widgets['mi'] hasn't been created!");
285  console.log(settings);
286  console.log(this._widgets);
287  console.log(this._header);
288  }
289  */
290  }
291 
299  nodeProperties.prototype.destroy = function()
300  {
301  pane.prototype.destroy.call(this);
302 
303  //FIXME: destroy forms here first?
304  this._forms = null;
305  this._widgets = null;
306 
307  this._objectLoader = null;
308  this._selection = null;
309  }
310 
321  nodeProperties.prototype.addForm = function(path,form)
322  {
323  this._forms[path] = form;
324 
325  var html = form.createHTML();
326 
327  var f = $( "<div class='nodePropertiesForm'>" ).appendTo( this._content );
328  var h = $( "<div class='nodePropertiesFormHeader'>").appendTo(f);
329  var c = $( "<div class='nodePropertiesFormContent'>").appendTo(f);
330 
331  var hi = $("<img class='nodePropertiesFormHeaderIcon'/>").attr("src",spinner).appendTo(h);
332 
333  var ht = $("<div class='nodePropertiesFormHeaderTitle'>").appendTo(h);
334  ht.text("Loading '"+form._path+"'");
335 
336  var b = $( "<div class='nodePropertiesFormHeaderButtons'>").appendTo(h);
337  var bh = $("<div class='nodePropertiesFormHeaderButton' title='Help Text'>?</div>").appendTo(b);
338  var bm = $("<div class='nodePropertiesFormHeaderButton' title='Collapse / Expand'>&boxH;</div>").appendTo(b);
339  var bc = $("<div class='nodePropertiesFormHeaderButton' title='Close'>&#x2715;</div>").appendTo(b);
340 
341  form._formJq.appendTo(c);
342 
343  //console.log("ADD FORM:",form);
344 
345  //var ccv = form._attributes.node;//.value;//.color.value;
346  //console.log(ccv);
347  var cch = {
348  value: "FF0000",
349  options: {
350  hideLabel: true,
351  class: "nodePropertiesFormHeaderColor",
352  },
353  change: function(variable,value,widget)
354  {
355  //console.log("color change!",arguments);
356  //console.log(form);
357 
358  //var od = form._diffing;
359  //console.log("OD diffing: "+od);
360 
361  var a = form._attributes;
362  if (a && a.node && a.node.value && a.node.value.color)
363  a.node.immediateValue = { color:{value:value} };
364  //a.node.value = { color:{value:value} };
365  //else
366  // console.log("missing attr");
367  }
368  }
369 
370  var ch = new colorWidget( cch );
371  var wch = ch.createWidget();
372  var cvw = ch.visibleWidget;
373  cvw.prependTo(b);
374  cvw.attr("title","Node Background Color");
375  //cvw.bind("mousedown touchstart", function(e)
376  cvw.bind("pointerdown", function(e)
377  {
378  cvw.focus();
379  return false;
380  } );
381  ch.activate();
382 
383  //bh.bind("mousedown touchstart",false);
384  bh.bind("pointerdown",false);
385  bh.bind("click", function()
386  {
387  modal.alert( form._help, null, "Help" );
388  } );
389 
390  bh.hover( function() //put in hover because the help won't be known until the form is fully loaded
391  {
392  //bh.attr("title", form._help);
393  bh.attr("title", form._help.replace(/<br>/i,"\n") );
394  } );
395  //bh.attr("title", form._help.replace(/<br>/i,"\n") );
396 
397  function toggler()
398  {
399  c.toggle();
400  if ( c.is(":visible") ) bm.html("&boxH;");
401  else bm.html("&plus;");
402  }
403 
404  h.bind("dblclick", toggler );
405  h.bind("mousedown", function() { h.addClass("nodePropertiesFormHeaderActive"); } );
406  h.bind("mouseup", function() { h.removeClass("nodePropertiesFormHeaderActive"); } );
407 
408  //bm.bind("mousedown touchstart",false);
409  bm.bind("pointerdown",false);
410  bm.bind("click", toggler );
411 
412  //bc.bind("mousedown touchstart",false);
413  bc.bind("pointerdown",false);
414  bc.bind("click", function()
415  {
416  f.toggle();
417  } );
418 
419  form._wrapper = {
420  wrapper: f,
421  header: h,
422  content: c,
423  icon: hi,
424  title: ht,
425  buttons: b,
426  color: ch,
427  help: bh,
428  minimize: bm,
429  close: bc,
430  }
431 
432  //console.log(html);
433 
434  this.makeSortable();
435  }
436 
445  nodeProperties.prototype.makeSortable = function()
446  {
447  this._content.sortable( {
448  handle: '.nodePropertiesFormHeader',
449  axis: 'y',
450  containment: 'parent',
451  cursor: 'move',
452  items: '.nodePropertiesForm',
453  opacity: 0.7,
454  } );
455  }
456 
458 // nodePropertiesLoader
460 
483  function nodePropertiesLoader(path,_nodeProperties,diff,icon)
484  {
485  //console.log("New nodePropertiesLoader: "+path);
486  //console.log(diff);
487 
489 
490  this._path = path;
491  this._nodeProperties = _nodeProperties;
492  this._icon = icon || null;
493 
494  this._diffList = [];
495 
497 
498  if (diff)
499  return this.applyDiff(diff); //returning different object than the 'new' call would indicate!!!
500  }
501 
502  nodePropertiesLoader.prototype = {
503 
514  applySubscription: function(data,metadata)
515  {
516  //console.log("form loader apply subscription",arguments,this);
517  //console.trace("form loader apply subscription",this._path);
518 
519  //if (this._subscribed)
520  // return;
521 
522  if ("icon" in metadata) //as a failover
523  {
524  //this._form._wrapper.icon.attr( "src", metadata.icon );
525  this._icon = metadata.icon;
526  }
527 
528  //FIXME: add loader spinner
529  //...
530 
531  //load definition data...
532  var base = data.base;
533  return this._nodeProperties._objectLoader.fetchDefinition( base, {}, this.createForm.bind(this,data) );
534 
535  //this.applyDiff(data);
536 
537  //this._subscribed = true;
538  },
539 
550  applyDiff: function(diff,user)
551  {
552  //console.log("form loader apply diff (queued)",arguments);
553 
554  this._diffList.push( diff );
555  },
556 
569  applyRequestSuccess: function(command,id,data,metadata)
570  {
571  //console.log("nodePropertiesLoader applyRequestSuccess",arguments);
572  },
573 
584  createForm: function( diff, definition )
585  {
586  //console.log("CREATE NODE FORM!",arguments);
587 
588  if ( this._nodeProperties._forms[this._path] instanceof form )
589  { //console.log("already created "+this._path,this._nodeProperties._forms);
590  return this._nodeProperties._forms[this._path];
591  }
592 
593  var spec = this._nodeProperties._objectLoader.applyValues( diff, definition );
594  //console.log("CREATE FORM SPEC",spec);
595 
596  spec = utils.resolveContextValues(spec,this._nodeProperties._context);
597 
598  if (spec.icon)
599  this._icon = spec.icon;
600 
601  //console.log("new form: "+this._path);
602  var f = new form( { path:this._path, parent:this._nodeProperties, enabledFilter:this._nodeProperties._enabledFilter } );
603  this._nodeProperties.addForm( this._path, f );
604 
605  //hide the newly created form
606  //f._html.children(".nodePropertiesForm").hide();
607  f._wrapper.wrapper.hide();
608 
609  if (this._icon)
610  { var ii = this._icon;
611  if (ii.startsWith("data:image/svg+xml;utf8,"))
612  {
613  ii = $(ii.replace("data:image/svg+xml;utf8,",""));
614  //ii.addClass("paneMenuIcon");
615  ii.addClass("nodePropertiesFormHeaderIcon");
616  f._wrapper.icon.replaceWith(ii);
617  f._wrapper.icon = ii;
618  }
619  else if (ii.startsWith("data:image/"))
620  {
621  f._wrapper.icon.attr( "src", this._icon );
622  //ii = $("<img>").attr("src",ii).addClass("paneMenuIcon");
623  }
624  }
625 
626  //monkey patch for now... just to get it working.
627 
628  /* var oAS = f.applySubscription;
629  f.applySubscription = function(data,metadata)
630  {
631  console.log("monkey subscribe");
632 
633  return oAS.call(this,data);
634  };
635  */
636 
637  var oAD = f.applyDiff;
638  var nF = false;
639  f.applyDiff = function(diff,user)
640  {
641  if (diff === null)
642  {
643  console.log("node properties loader diff was null. returning.");
644  return;
645  }
646 
647  if ("help" in diff)
648  f._help = diff.help;
649 
650  if ("attributes" in diff)
651  {
652  var r = oAD.call(this,diff.attributes,user);
653  //return r;
654 
655  f._diffing = true;
656 
657  if ("name" in diff.attributes)
658  {
659  f._wrapper.title.text( f._attributes.name.value );
660  if (!nF)
661  {
662  nF = true;
663  var oc = f._attributes.name._change;
664  f._attributes.name._change = function(variable,value,widget)
665  {
666  //console.log("name change.");
667  f._wrapper.title.text( value );
668  //oc.call(f,variable,value,widget);
669  oc.call(f._attributes.name,variable,value,widget);
670  }
671  }
672  }
673 
674  if ( "node" in diff.attributes &&
675  "value" in diff.attributes.node &&
676  "color" in diff.attributes.node.value
677  )
678  {
679  // console.log("update color!",diff);
680  f._wrapper.color.value = diff.attributes.node.value.color.value || diff.attributes.node.value.color;
681  // f._wrapper.color.immediateValue = diff.attributes.node.value.color.value;
682  }
683 
684  f._diffing = false;
685 
686  return r;
687  }
688  //else
689  // return oAD.call(this,diff,user);
690  };
691 
692  /*
693  var oCF = f.changeField;
694  f.changeField = function(variable, value, field)
695  {
696  var v = {};
697  v[variable] = value;
698 
699  //return oCF.call(this, "attributes", v, field);
700  return oCF.call(this, "attributes."+variable, value, field);
701  };
702  */
703 
704  var oPU = f.postUpdate;
705  f.postUpdate = function(u)
706  {
707  u = { attributes: u };
708 
709  //console.log(u);
710 
711  //this should already be in the form postUpdate code!
712  //objectRegistry.applyDiff( this._path, u, null, this ); //propagate to any other instance of this form or node
713  objectRegistry.applyDiff( this._path, u, null, f ); //propagate to any other instance of this form or node
714 
715  return oPU.call(this, u);
716  };
717 
718  //must be after monkey patch
719  f.applyDiff(spec);
720 
721  while (this._diffList.length)
722  {
723  var d = this._diffList.shift();
724  f.applyDiff(d);
725  }
726 
727  objectRegistry.registerObject( this._path, f, true );
728  f._subscribed = true;
729 
730  this.destroy();
731 
732  return f;
733  },
734 
743  destroy: function()
744  {
745  this._diffList = [];
746 
747  objectRegistry.unregisterObject( this._path, this, true, true );
748  },
749  };
750 
751  return nodeProperties;
752  }
753 );
applySubscription(data, metadata)
setter color
a setter DOCME
getter html
Get the html color representation of this object.
A form widget for selecting an RGB color.
setter value
a setter DOCME
getter id
returns the number of milliseconds since midnight January 1, 1970 UTC
setter user
a setter DOCME
Form renderer.
form(layout)
createHTML()
Create the jquery content object, or return it if it exists.
createForm(diff, definition)
setter widget
a setter DOCME
getter nodeMenu
a getter DOCME
addForm(path, form)
applySettings(settings)
nodeProperties(layout)
applyDiff(diff, user)
createHTML()
Create the jquery html content object for this pane. If it already exists, it will be returned.
applySubscription(data, metadata)
Load a form from the VFS, including any base form references.
nodePropertiesLoader(path, _nodeProperties, diff, icon)
applyDiff(id, diff, user, except)
registerObject(id, o, nosubscribe)
Register an object in the registry, and call the pathAddedCallback.
unregisterObject(id, o, silent, nounsubscribe, now)
Unregister an object from the registry.
Create a pane which will be mounted into a paneManager layout.
getter help
A getter that will return a pane object's help message, or a default message indicating that no help ...
destroy()
DOCME.
createHTML()
pane(layout, object)
The paneManager manages panes in a user layout.
hideLabel()
getter path
a getter DOCME
applyRequestSuccess()
If an adminstrator has deleted his own preferences, this function will be called on success.
fetch(v, _default)
applyDiff(diff, user)
createHTML()
Create the HTML contents of the pane.
Utility functions for javascript clients.
cleanPath(path)
Clean and normalize a resource path.
resolveContextValues(o, ctx)
Recursively resolve any context values in an object.
metadata(paths)
Base class for GUI form widgets.