<commit_before>var VDOMPatch = require('virtual-dom/patch');
var VDOMDiff = require('virtual-dom/diff');

var createElement = require('virtual-dom/create-element');
const cloneVNode = require('../utils/clone-vnode');

module.exports = class VDOMWidget {
    cloneVNode(vNode, pruneNullNodes=true, childWidgets=[]) {
        if (Array.isArray(vNode)) {
            var arr = vNode.map(child => this.cloneVNode(child, pruneNullNodes, childWidgets));

            if (pruneNullNodes) {
                arr = arr.reduce((newArr, child, ii) => {
                    if (child && child.type === 'Widget') {
                        if ((!pruneNullNodes || child.vTree)) {
                            newArr.push(child);
                        }
                    } else if (!pruneNullNodes || child != null) {
                        newArr.push(child);
                    }
                    return newArr;
                }, []);
            }
            return arr;
        } else if (!vNode) {
            return vNode;
        } else if (vNode.type === 'Widget') {
            var widget = (vNode.component._widgetIsDirty ? vNode.component.makeNewWidget() : vNode.component._widget);
            childWidgets.push(widget);
            return widget
        } else if (!vNode.tagName) {
            return vNode;
        } else {
            return cloneVNode(vNode, this.cloneVNode(vNode.children, pruneNullNodes, childWidgets));
        }
    }

    constructor({component=null, vTree=component._vTree}) {
        this.type = 'Widget';
        this.component = component;
        this.childWidgets = [];
        this._callbacks = [];
        this.vTree = this.cloneVNode(vTree, true, this.childWidgets);
    }

    bindChildren(parent) {
        this._callbacks = this.childWidgets.map(childWidget => childWidget.component.on('widgetdirty', () => {
            parent.markWidgetDirty()
        }))
    }
    unbindChildren() {
        this._callbacks.forEach(callback => callback());
    }
    init() {
        if (!this.vTree) {
            throw "Component has no VTree to init with";
        }
        var el = createElement(this.vTree);

        this.fireDomEvents(this.component.el, this.component._el = el);

        return el;
    }

    fireDomEvents(prevEl, el) {
        if (!prevEl) {
            this.component.trigger('domcreate', {el});
            this.component.onDOMCreate.call(this.component, {el});
            this.component.trigger('domcreateorchange', {newEl: el, prevEl});
            this.component.onDOMCreateOrChange.call(this.component, {newEl: el, prevEl});
        } else if (prevEl !== el) {
            this.component.trigger('domchange', {el});
            this.component.onDOMChange.call(this.component, { newEl: el, prevEl });
            this.component.trigger('domcreateorchange', {newEl: el, prevEl});
            this.component.onDOMCreateOrChange.call(this.component, { newEl: el, prevEl });

            var positionComparison = prevEl.compareDocumentPosition(el);
            if (positionComparison !== 0) {
                this.component.trigger('dommove', );
                //@TODO atm this pretty much always fires. maybe that is circumstantial, but we may need to be more selective about which bits constitute a "move"
                this.component.onDOMMove.call(this.component, { newEl: el, prevEl });
            }
        }
    }

    update(previousWidget, prevDOMNode) {
        var patches = VDOMDiff(previousWidget.vTree, this.vTree);
        var el = VDOMPatch(prevDOMNode, patches);

        this.fireDomEvents(this.component.el, this.component._el = el);

        return el;
    }

    destroy(el) {
        if (el === this.component.el) {
            this.component._el = null;
            this.component.onDOMDestroy.call(this.component, {el});
        }
    }
}<commit_msg>check that vdom widgets were created by weddell<commit_after>var VDOMPatch = require('virtual-dom/patch');
var VDOMDiff = require('virtual-dom/diff');

var createElement = require('virtual-dom/create-element');
const cloneVNode = require('../utils/clone-vnode');

module.exports = class WeddellVDOMWidget {

    cloneVNode(vNode, pruneNullNodes=true, childWidgets=[]) {
        if (Array.isArray(vNode)) {
            var arr = vNode.map(child => this.cloneVNode(child, pruneNullNodes, childWidgets));

            if (pruneNullNodes) {
                arr = arr.reduce((newArr, child, ii) => {
                    if (child && child instanceof WeddellVDOMWidget) {
                        if ((!pruneNullNodes || child.vTree)) {
                            newArr.push(child);
                        }
                    } else if (!pruneNullNodes || child != null) {
                        newArr.push(child);
                    }
                    return newArr;
                }, []);
            }
            return arr;
        } else if (!vNode) {
            return vNode;
        } else if (vNode instanceof WeddellVDOMWidget) {
            var widget = (vNode.component._widgetIsDirty ? vNode.component.makeNewWidget() : vNode.component._widget);
            childWidgets.push(widget);
            return widget
        } else if (!vNode.tagName) {
            return vNode;
        } else {
            return cloneVNode(vNode, this.cloneVNode(vNode.children, pruneNullNodes, childWidgets));
        }
    }

    get type() {
        return 'Widget';
    }

    constructor({component=null, vTree=component._vTree}) {
        this.component = component;
        this.childWidgets = [];
        this._callbacks = [];
        this.vTree = this.cloneVNode(vTree, true, this.childWidgets);
    }

    bindChildren(parent) {
        this._callbacks = this.childWidgets.map(childWidget => childWidget.component.on('widgetdirty', () => {
            parent.markWidgetDirty()
        }))
    }
    unbindChildren() {
        this._callbacks.forEach(callback => callback());
    }
    init() {
        if (!this.vTree) {
            throw "Component has no VTree to init with";
        }
        var el = createElement(this.vTree);

        this.fireDomEvents(this.component.el, this.component._el = el);

        return el;
    }

    fireDomEvents(prevEl, el) {
        if (!prevEl) {
            this.component.trigger('domcreate', {el});
            this.component.onDOMCreate.call(this.component, {el});
            this.component.trigger('domcreateorchange', {newEl: el, prevEl});
            this.component.onDOMCreateOrChange.call(this.component, {newEl: el, prevEl});
        } else if (prevEl !== el) {
            this.component.trigger('domchange', {el});
            this.component.onDOMChange.call(this.component, { newEl: el, prevEl });
            this.component.trigger('domcreateorchange', {newEl: el, prevEl});
            this.component.onDOMCreateOrChange.call(this.component, { newEl: el, prevEl });

            var positionComparison = prevEl.compareDocumentPosition(el);
            if (positionComparison !== 0) {
                this.component.trigger('dommove', );
                //@TODO atm this pretty much always fires. maybe that is circumstantial, but we may need to be more selective about which bits constitute a "move"
                this.component.onDOMMove.call(this.component, { newEl: el, prevEl });
            }
        }
    }

    update(previousWidget, prevDOMNode) {
        if (previousWidget instanceof WeddellVDOMWidget) {
            var patches = VDOMDiff(previousWidget.vTree, this.vTree);
            var el = VDOMPatch(prevDOMNode, patches);

            this.fireDomEvents(this.component.el, this.component._el = el);
            return el;
        }
        return this.init();
    }

    destroy(el) {
        if (el === this.component.el) {
            this.component._el = null;
            this.component.onDOMDestroy.call(this.component, {el});
        }
    }
}<|endoftext|>


<commit_before>// SortedLayout Container
// Original Author: Daniel Plemmons
// Created: Nov. 3, 2014
//
// This is a container for InteractablePlanes that:
// - Allows the planes to be interactively layed out on screen
//   (via sorting) by given properties
// - Allows users to determine the behavior of the on-screen
//   sorting based on the position of their hands
// - Allows the user to manually layout the items in the
//   container and persists that layout in application state
//   (for the duration of the application runtime)
//
// For the moment, the container will make all calculations for
// the plane locations in world space. Eventually this class
// should be modified to give each container it's own local
// coordinate space.
//
// The container currently handles it's own list of child planes.
// This means that multiple containers could reasonably contain
// the same plane. This class should eventually be refactored to
// have the plane's parent determine its container. ( this goes along
// with the proposed coordinate space refactor. )

(function () {
  //The list of valid states for the manner of sorting.
  var validSortStates = [
    "USER_SORTED",
    "DYNAMIC_SORTED"
  ];
  // SortedLayoutContainer Constructor
  // Optional argument "planeList" is a list of InteractivePlanes to be added to the container on creation
  // Optional argument "sortState" is a string given one of the validSortStates listed above.
  window.SortedLayoutContainer = function(planeList, sortState){
    this.planeList = planeList || [];

    if (sortState !== undefined && validSortStates.indexOf(sortState) != -1) {
      this.sortState = sortState;
    }
    else {
      this.sortState = "DYNAMIC_SORTED";
    }
  };

  // Pubic Methods
  window.SortedLayoutContainer.prototype = {
    // Takes two hand positions
    // if the container's current state allows programatic layout (as opposed to manual user layout )
    // will sort the InteractablePlane(s) in the container accordingly (and return true).
    // Update will return false if the current container state does not support
    // programatic layout
    update: function(hand1Position, hand2Position) {
      if ( this.sortState == "DYNAMIC_SORTED" ) {
        var diff = new THREE.Vector3().subVectors(hand2Position, hand1Position);
        var weightX = diff.x / (diff.x + diff.y);
        var weightY = diff.y / (diff.x + diff.y);
        var alphabeticalLayout = listLayout(hand1Position, new THREE.Vector3(hand1Position.x + diff.x, hand1Position.y, hand1Position.z)).alphabetical();
        var chronologicalLayout = listLayout(hand1Position, new THREE.Vector3(hand1Position.x, hand1Position.y + diff.y, hand1Position.z)).alphabetical();
        var blendedLayout = blendedLayoutList([alphabeticalLayout, chronologicalLayout], [weightX, weightY]);
        applyLayoutList(blendedLayout);
        return true;
      }
      else {
        return false;
      }
    },

    // Adds the given plane to list of planes managed by the container.
    // Returns true if the plane is successfully added.
    // Returns false if the plane is a duplicate and cannot be added.
    addPlane: function(newPlane) {
      if ( this.planeList.indexOf(newPlane) == -1 ) {
        this.planeList.push(newPlane);
        return true;
      }
      else {
        return false;
      }
    },

    // Removes the given plane from the list of planes.
    // Returns true if the plane was successfully removed.
    // Returns false if the plane could not be found and was not removed.
    removePlane: function(toRemove) {
      var planeIndex;
      if ( (planeIndex = this.planeList.indexOf(toRemove)) != -1 ) {
        this.planeList.splice(planeIndex, 1);
        return true;
      }
      else {
        return false;
      }
    }
  };

  // Generates a LayoutNode object which is just a
  // simple structure to hold a plane and the position
  // generated for it.
  function LayoutNode(plane, position) {
    var returnNode = {
      "plane": plane,
      "position": position
    };
    return returnNode;
  }

  // If called with a sort comparitor, returns an array of layoutNodes specifying the InteractivePlane and the
  // location in space according to the supplied sort comparitor function.
  // Locations are given in a 0-1 space if no positions are specified.
  //
  // if called with just start and end position, returns an object that containts the relevant
  // calls to generate the layout with different sorting properties.
  function listLayout(startPosition, endPosition, sortComparitor) {
    // If only start and end position are given, utilize partial evaluation
    // to allow a call formated like: listLayout(startPosition, endPosition).alphabetical();
    if (arguments.length == 2) {
      return {
        alphabetical: function() {
          listLayout(startPosition, endPosition, function(a,b) {
            if ( plane1.mesh.name <= plane2.mesh.name ) { return -1; }
            else { return 1; }
          });
        },
        chronological: function() {
          listLayout(startPosition, endPosition, function(a,b) {
            if ( plane1.uid <= plane2.uid ) { return -1; }
            else { return 1; }
          });
        }
      };
    }
    else if (arguments.length == 3) {
      var layoutList = [];
      var ittr = 0;
      startPosition || (startPosition = 0);
      endPosition || (endPosition = 0);
      reverse || (reverse = false);

      // Sort the planelist alphabetically by "plane.mesh.name"
      this.planeList.sort(sortComparitor);

      // Generate the layout list full of layout nodes
      for ( var plane in this.planelist ) {
        var listPercentage = (ittr*1.0) / (this.planelist.length*1.0); // force double division
        var position = startPosition.lerp(endPosition, listPercentage);
        layoutList.push(returnNode(plane, position));
        ittr += 1;
      }

      return layoutList;
    }
    else {
      return false;
    }
  }

  // - Retruns a weighted mean of the given layout node lists as an array of layout nodes
  // - Assumes that each index of each list in layout lists referes to the
  //   same node.
  // - The length of the weightList and the layoutList must be equal.
  function blendLayouts(layoutLists, weightList) {
    var ittr = 0;
    var weightSumList = [];
    var vectorSumList = [];
    var blendedLayoutList = [];

    // Confirm arguments are valid(ish)
    if( layoutList === undefined || weightList === undefined) { return false; }
    else if ( layoutList.length != weightList.length ) { return false; }
    else if ( layoutList.length === 0 ) { return false; }

    // For each node, calculate the weighted sum of vectors
    // along with the sum of weights.
    for( var layoutList in layoutLists ) {
      var weight = weightList[ittr];
      for ( var layoutNode in layoutList ) {
        if (vectorSumList[ittr] === undefined) { vectorSumList[ittr] = layoutNode.position; }
        else { vectorSumList[ittr].add(layoutNode.position.multiplyScalar(weight)); }
        weightSum += weight;
      }
      ittr+=1;
    }

    ittr = 0; // reset itterator

    //Calculate the weighted mean for each node
    for ( var vecSum in sumList ) {
      var plane = layoutLists[0][ittr].plane;
      var weightSum = weightSumList[ittr];
      blendedLayoutList[ittr] = LayoutNode(plane, vecSum.divideScalar(weightSum));
      ittr += 1;
    }

    return weightedMeanList;
  }

  // Itterate through a layout list and move the given elements to the
  // given positions.
  function applyLayoutList(layoutList) {
    for(var node in layoutList) {
      var plane = node.plane;
      var localSpace = plane.worldToLocal(node.position); // Convert our global coordinates to local space.
      plane.position = localSpace; // Set the local space position of the plane.
    }
  }
}).call(this);<commit_msg>Pass planeList to listLayout rather than get it via this.<commit_after>// SortedLayout Container
// Original Author: Daniel Plemmons
// Created: Nov. 3, 2014
//
// This is a container for InteractablePlanes that:
// - Allows the planes to be interactively layed out on screen
//   (via sorting) by given properties
// - Allows users to determine the behavior of the on-screen
//   sorting based on the position of their hands
// - Allows the user to manually layout the items in the
//   container and persists that layout in application state
//   (for the duration of the application runtime)
//
// For the moment, the container will make all calculations for
// the plane locations in world space. Eventually this class
// should be modified to give each container it's own local
// coordinate space.
//
// The container currently handles it's own list of child planes.
// This means that multiple containers could reasonably contain
// the same plane. This class should eventually be refactored to
// have the plane's parent determine its container. ( this goes along
// with the proposed coordinate space refactor. )

(function () {
  //The list of valid states for the manner of sorting.
  var validSortStates = [
    "USER_SORTED",
    "DYNAMIC_SORTED"
  ];
  // SortedLayoutContainer Constructor
  // Optional argument "planeList" is a list of InteractivePlanes to be added to the container on creation
  // Optional argument "sortState" is a string given one of the validSortStates listed above.
  window.SortedLayoutContainer = function(planeList, sortState){
    this.planeList = planeList || [];

    if (sortState !== undefined && validSortStates.indexOf(sortState) != -1) {
      this.sortState = sortState;
    }
    else {
      this.sortState = "DYNAMIC_SORTED";
    }
  };

  // Pubic Methods
  window.SortedLayoutContainer.prototype = {
    // Takes two hand positions
    // if the container's current state allows programatic layout (as opposed to manual user layout )
    // will sort the InteractablePlane(s) in the container accordingly (and return true).
    // Update will return false if the current container state does not support
    // programatic layout
    update: function(hand1Position, hand2Position) {
      if ( this.sortState == "DYNAMIC_SORTED" ) {
        var diff = new THREE.Vector3().subVectors(hand2Position, hand1Position);
        var weightX = diff.x / (diff.x + diff.y);
        var weightY = diff.y / (diff.x + diff.y);
        var alphabeticalLayout = listLayout(this.planeList, hand1Position, new THREE.Vector3(hand1Position.x + diff.x, hand1Position.y, hand1Position.z)).alphabetical();
        var chronologicalLayout = listLayout(this.planeList, hand1Position, new THREE.Vector3(hand1Position.x, hand1Position.y + diff.y, hand1Position.z)).alphabetical();
        var blendedLayout = blendLayouts([alphabeticalLayout, chronologicalLayout], [weightX, weightY]);
        applyLayoutList(blendedLayout);
        return true;
      }
      else {
        return false;
      }
    },

    // Adds the given plane to list of planes managed by the container.
    // Returns true if the plane is successfully added.
    // Returns false if the plane is a duplicate and cannot be added.
    addPlane: function(newPlane) {
      if ( this.planeList.indexOf(newPlane) == -1 ) {
        this.planeList.push(newPlane);
        return true;
      }
      else {
        return false;
      }
    },

    // Removes the given plane from the list of planes.
    // Returns true if the plane was successfully removed.
    // Returns false if the plane could not be found and was not removed.
    removePlane: function(toRemove) {
      var planeIndex;
      if ( (planeIndex = this.planeList.indexOf(toRemove)) != -1 ) {
        this.planeList.splice(planeIndex, 1);
        return true;
      }
      else {
        return false;
      }
    }
  };

  // Generates a LayoutNode object which is just a
  // simple structure to hold a plane and the position
  // generated for it.
  function LayoutNode(plane, position) {
    var returnNode = {
      "plane": plane,
      "position": position
    };
    return returnNode;
  }

  // If called with a sort comparitor, returns an array of layoutNodes specifying the InteractivePlane and the
  // location in space according to the supplied sort comparitor function.
  // Locations are given in a 0-1 space if no positions are specified.
  //
  // if called with just start and end position, returns an object that containts the relevant
  // calls to generate the layout with different sorting properties.
  function listLayout(planeList, startPosition, endPosition, sortComparitor) {
    // If only start and end position are given, utilize partial evaluation
    // to allow a call formated like: listLayout(startPosition, endPosition).alphabetical();
    if (arguments.length == 3) {
      return {
        alphabetical: (function() {
          listLayout(planeList, startPosition, endPosition, function(a,b) {
            if ( a.mesh.name <= b.mesh.name ) { return -1; }
            else { return 1; }
          });
        }),
        chronological: (function() {
          listLayout(planeList, startPosition, endPosition, function(a,b) {
            if ( a.uid <= b.uid ) { return -1; }
            else { return 1; }
          });
        })
      };
    }
    else if (arguments.length == 4) {
      var layoutList = [];
      var ittr = 0;
      startPosition || (startPosition = 0);
      endPosition || (endPosition = 0);
      reverse || (reverse = false);

      // Sort the planelist alphabetically by "plane.mesh.name"
      planeList.sort(sortComparitor);

      // Generate the layout list full of layout nodes
      for ( var plane in planeList ) {
        var listPercentage = (ittr*1.0) / (planeList.length*1.0); // force double division
        var position = startPosition.lerp(endPosition, listPercentage);
        layoutList.push(LayoutNode(plane, position));
        ittr += 1;
      }

      return layoutList;
    }
    else {
      return false;
    }
  }

  // - Retruns a weighted mean of the given layout node lists as an array of layout nodes
  // - Assumes that each index of each list in layout lists referes to the
  //   same node.
  // - The length of the weightList and the layoutList must be equal.
  function blendLayouts(layoutLists, weightList) {
    var ittr = 0;
    var weightSumList = [];
    var vectorSumList = [];
    var blendedLayoutList = [];

    // Confirm arguments are valid(ish)
    if( layoutList === undefined || weightList === undefined) { return false; }
    else if ( layoutList.length != weightList.length ) { return false; }
    else if ( layoutList.length === 0 ) { return false; }

    // For each node, calculate the weighted sum of vectors
    // along with the sum of weights.
    for( var layoutList in layoutLists ) {
      var weight = weightList[ittr];
      for ( var layoutNode in layoutList ) {
        if (vectorSumList[ittr] === undefined) { vectorSumList[ittr] = layoutNode.position; }
        else { vectorSumList[ittr].add(layoutNode.position.multiplyScalar(weight)); }
        weightSum += weight;
      }
      ittr+=1;
    }

    ittr = 0; // reset itterator

    //Calculate the weighted mean for each node
    for ( var vecSum in sumList ) {
      var plane = layoutLists[0][ittr].plane;
      var weightSum = weightSumList[ittr];
      blendedLayoutList[ittr] = LayoutNode(plane, vecSum.divideScalar(weightSum));
      ittr += 1;
    }

    return weightedMeanList;
  }

  // Itterate through a layout list and move the given elements to the
  // given positions.
  function applyLayoutList(layoutList) {
    for(var node in layoutList) {
      var plane = node.plane;
      var localSpace = plane.worldToLocal(node.position); // Convert our global coordinates to local space.
      plane.position = localSpace; // Set the local space position of the plane.
    }
  }
}).call(this);<|endoftext|>


<commit_before>(function (window, document, undefined) {
  /* helper functions */
  function createElement (element, options) {
    var node = document.createElement(element);

    if (options !== undefined) {
      for (var option in options) {
        switch (option) {
        case "class":
          node.className = options[option];
          break;
        case "value":
          if (node.value !== undefined) node.value = options[option];
          break;
        default:
          node.setAttribute(option, options[option]);
          break;
        }
      }
    }

    return node;
  }

  function extend (out) {
    out = out || {};

    for (var i = 1; i < arguments.length; i++) {
      if (!arguments[i]) continue;

      for (var key in arguments[i]) {
        if (arguments[i].hasOwnProperty(key))
          out[key] = arguments[i][key];
      }
    }

    return out;
  }

  /* constants */

  var MONTHS = [
    "January",
    "February",
    "March",
    "April",
    "May",
    "June",
    "July",
    "August",
    "September",
    "October",
    "November",
    "December"
  ];
  var WEEKDAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
  var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

  window.Datepicker = Datepicker;

  function Datepicker (element, config) {
    this.date = new Date();
    this.state = {
      picker: "date",
      year: this.date.getFullYear(),
      month: this.date.getMonth(),
      day: this.date.getDate()
    };

    this.config = {
      firstDay: 0
    };

    this.config = extend(this.config, config);

    var container = createElement("div", {class: "datepicker"});
    element.appendChild(container);
  }

  Datepicker.prototype = {
    _render: render
  };

  // var datepicker = new Datepicker(document.querySelector("main"));

  var firstDay = 1;
  var date = new Date();

  var state = {
    picker: "date",
    year: 0,
    month: 0,
    day: 0
  };
  state.year = date.getFullYear();
  state.month = date.getMonth();
  state.day = date.getDate();

  var container = createElement("div", {class: "datepicker"});
  document.querySelector("main").appendChild(container);
  render("date");

  function render (picker) {
    var fragment = document.createDocumentFragment();
    fragment.appendChild(renderHead(picker));

    switch (picker) {
    case "date":
      fragment.appendChild(renderDatePicker());
      break;
    case "month":
      fragment.appendChild(renderMonthPicker());
      break;
    case "year":
      fragment.appendChild(renderYearPicker());
      break;
    }

    container.innerHTML = "";
    container.appendChild(fragment);
  }

  function getDaysInMonth (year, month) {
    return month === 1 && year % 4 === 0 &&
      (year % 100 !== 0 || year % 400 === 0) ? 29 : DAYS_IN_MONTH[month];
  }

  function getWeekday (i) {
    i += firstDay;
    while (i >= 7) {
      i -= 7;
    }
    return WEEKDAYS[i];
  }

  function callback () {
    alert(date);
  }

  function setDate (d) {
    d = d.split("-");
    date = new Date(d[0], d[1], d[2]);
    state.day = date.getDate();
    state.month = date.getMonth();
    state.year = date.getFullYear();
    state.picker = "date";

    render(state.picker);

    callback();
    return;
  }

  function setMonth (month) {
    month = parseInt(month);
    if (month < 0)  {
      state.month = 11;
      return setYear(state.year - 1);
    } else if (month > 11) {
      state.month = 0;
      return setYear(state.year + 1);
    } else {
      state.month = month;
    }
    state.picker = "date";

    render(state.picker);
    return;
  }

  function setYear (year) {
    state.year = parseInt(year) > 0 ? parseInt(year) : 0;

    state.picker = "month";
    render(state.picker);
    return;
  }

  function getStartYear (year, range) {
    return parseInt((year - 1) / range) * range + 1;
  }

  function renderHead (view) {
    var prev, change, next;

    //

    var head = createElement("div", {class: "pick-head"});

    // <button type="button" class="pick-btn pick-prev">
    prev = createElement("button", {
      type: "button",
      class: "pick-btn pick-prev"
    });
    // text
    prev.innerHTML = "&#9666";

    // bind event
    prev.addEventListener("click", function (e) {
      e.preventDefault();
      switch (view) {
      case "date":
        setMonth(state.month - 1);
        break;
      case "month":
        setYear(state.year - 1);
        break;
      case "year":
        setYear(state.year - 25);
        break;
      }
    });
    head.appendChild(prev);
    // </button>

    // <button type="button" class="pick-btn pick-change">
    change = createElement("button", {
      type: "button",
      class: "pick-btn pick-change"
    });
    // text
    switch (view) {
    case "date":
      change.innerHTML = MONTHS[state.month] + " " + state.year;
      change.value = "month";
      break;
    case "month":
      change.innerHTML = state.year;
      change.value = "year";
      break;
    case "year":
      var start = getStartYear(state.year, 25);
      var end = start + 24;
      change.innerHTML = start + " - " + end;
      change.className += " disabled";
      break;
    }
    // add event
    change.addEventListener("click", function (e) {
      e.preventDefault();
      render(this.value);
    });
    head.appendChild(change);
    // </button>

    // <button type="button" class="pick-btn pick-next">
    next = createElement("button", {
      type: "button",
      class: "pick-btn pick-next"
    });
    // text
    next.innerHTML = "&#9656";

    // bind event
    next.addEventListener("click", function (e) {
      e.preventDefault();
      switch (view) {
      case "date":
        setMonth(state.month + 1);
        break;
      case "month":
        setYear(state.year + 1);
        break;
      case "year":
        setYear(state.year + 25);
        break;
      }
    });
    head.appendChild(next);
    // </button>
    //

    return head;
  }

  function renderYearPicker () {
    var row, col, btn;
    var year = getStartYear(state.year, 25);

    //

    var yearpicker = createElement("table");
    //

    yearpicker.appendChild(createElement("tbody"));

    //

    row = createElement("tr");

    for (var r = 0, c = 0; r < 25; r++) {
      //
      col = createElement("td");
      // <button type="button" class="pick-btn pick-year" value={year}>
      btn = createElement("button", {
        type: "button",
        class: "pick-btn pick-year",
        value: year
      });
      // text
      btn.innerHTML = year;

      // active if selected year
      if (date.getFullYear() === year) {
        btn.className += " active";
      }

      // add event
      btn.addEventListener("click", function (e) {
        e.preventDefault();
        setYear(this.value);
      });

      col.appendChild(btn);
      // </button>
      row.appendChild(col);
      //

      year++;
      //

      if (++c === 5) {
        yearpicker.firstChild.appendChild(row);
        row = createElement("tr");
        c = 0;
      }
    }
    //

    // yearpick.innerHTML = "";
    // yearpick.appendChild(renderHead("year"));
    // yearpick.appendChild(table);
    //

    return yearpicker;
  }

  function renderMonthPicker () {
    var row, col, btn;
    var month = 0;

    //

    var monthpicker = createElement("table");
    //

    monthpicker.appendChild(createElement("tbody"));

    //

    row = createElement("tr");

    for (var r = 0, c = 0; r < 12; r++) {
      //
      col = createElement("td");
      // <button type="button" class="pick-btn pick-month" value={month}>
      btn = createElement("button", {
        type: "button",
        class: "pick-btn pick-month",
        value: month
      });
      // text
      btn.innerHTML = MONTHS[month];

      // set active if selected month
      if (date.getMonth() === month &&
      date.getFullYear() === state.year) {
        btn.classList.add("active");
      }

      // bind event
      btn.addEventListener("click", function (e) {
        e.preventDefault();
        setMonth(this.value);
      });

      col.appendChild(btn);
      // </button>

      row.appendChild(col);
      //

      month++;

      if (++c === 3) {
        monthpicker.firstChild.appendChild(row);
        row = createElement("tr");
        c = 0;
      }
      //

    }
    //

    // monthpick.innerHTML = "";
    // monthpick.appendChild(renderHead("month"));
    // monthpick.appendChild(table);
    //

    return monthpicker;
  }

  function renderDatePicker () {
    var row, col, btn;
    var day = 0, month = 0, year = 0;
    var start = 0;
    var weekday;
    var daysInMonth = getDaysInMonth(state.year, state.month);
    var before = new Date(state.year, state.month, 1).getDay() - firstDay;

    /*
    If week doesnt start on day 0
    check for starting weekday of selected month
    and push all days to fit the selected start day

    If month starts on start day add a additional week
    to preserve the 6 weeks calendar
    */
    if (before < 0) {
      before += 7;
    } else if (before === 0) {
      start -= 7;
    }

    /* weekdays row */

    //

    var datepicker = createElement("table");
    //

    datepicker.appendChild(createElement("thead"));
    //

    var head = createElement("tr");

    //
    for (var w = 0; w < 7; w++) {
      weekday = createElement("th");
      weekday.innerHTML = getWeekday(w);
      head.appendChild(weekday);
    }
    //
    datepicker.firstChild.appendChild(head);
    //

    //

    row = createElement("tr");

    //
    for (var i = start, c = 0; i < (42 + start); i++) {
      col = createElement("td");

      day = 1 + (i - before);
      month = state.month;
      year = state.year;

      // <button class="pick-btn pick-day">
      btn = createElement("button", {
        type: "button",
        class: "pick-btn pick-day"
      });

      /*
      if day is is before selected month
      set button to out
      */
      if (i < before) {
        if (state.month === 0) {
          month = 11;
          year = state.year - 1;
        } else {
          month = state.month - 1;
        }
        day = getDaysInMonth(year, month) + day;
        btn.classList.add("out");

      /*
      if day is after selected month
      set button to out
      */
      } else if (i >= (daysInMonth + before)) {
        if (state.month === 11) {
          month = 0;
          year = state.year + 1;
        } else {
          month = state.month + 1;
        }
        day = day - daysInMonth;
        btn.classList.add("out");
      }

      // set active if selected day
      if (date.getDate() === day &&
      date.getMonth() === month &&
      date.getFullYear() === year) {
        btn.classList.add("active");
      }

      // text
      btn.innerHTML = day;
      // value
      btn.value = year + "-" + month + "-" + day;

      // add event
      btn.addEventListener("click", function (e) {
        e.preventDefault();
        setDate(this.value);
      });

      col.appendChild(btn);
      // </button>

      row.appendChild(col);
      //

      if (++c === 7) {
        datepicker.firstChild.appendChild(row);
        row = createElement("tr");
        c = 0;
      }
      //

    }
    //

    // datepick.innerHTML = "";
    // datepick.appendChild(renderHead("day"));
    // datepick.appendChild(table);
    //

    return datepicker;
  }
})(window, document);<commit_msg>fixed render bug<commit_after>(function (window, document, undefined) {
  /* helper functions */
  function createElement (element, options) {
    var node = document.createElement(element);

    if (options !== undefined) {
      for (var option in options) {
        switch (option) {
        case "class":
          node.className = options[option];
          break;
        case "value":
          if (node.value !== undefined) node.value = options[option];
          break;
        default:
          node.setAttribute(option, options[option]);
          break;
        }
      }
    }

    return node;
  }

  function extend (out) {
    out = out || {};

    for (var i = 1; i < arguments.length; i++) {
      if (!arguments[i]) continue;

      for (var key in arguments[i]) {
        if (arguments[i].hasOwnProperty(key))
          out[key] = arguments[i][key];
      }
    }

    return out;
  }

  /* constants */

  var MONTHS = [
    "January",
    "February",
    "March",
    "April",
    "May",
    "June",
    "July",
    "August",
    "September",
    "October",
    "November",
    "December"
  ];
  var WEEKDAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
  var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

  window.Datepicker = Datepicker;

  function Datepicker (element, config) {
    this.date = new Date();
    this.state = {
      picker: "date",
      year: this.date.getFullYear(),
      month: this.date.getMonth(),
      day: this.date.getDate()
    };

    this.config = {
      firstDay: 0
    };

    this.config = extend(this.config, config);

    var container = createElement("div", {class: "datepicker"});
    element.appendChild(container);
  }

  Datepicker.prototype = {
    _render: render,
    setDate: setDate
  };

  // var datepicker = new Datepicker(document.querySelector("main"));

  var firstDay = 1;
  var date = new Date();

  var state = {
    picker: "date",
    year: 0,
    month: 0,
    day: 0
  };
  state.year = date.getFullYear();
  state.month = date.getMonth();
  state.day = date.getDate();

  var container = createElement("div", {class: "datepicker"});
  document.querySelector("main").appendChild(container);
  render("date");

  function render (picker) {
    var fragment = document.createDocumentFragment();
    fragment.appendChild(renderHead(picker));

    switch (picker) {
    case "date":
      fragment.appendChild(renderDatePicker());
      break;
    case "month":
      fragment.appendChild(renderMonthPicker());
      break;
    case "year":
      fragment.appendChild(renderYearPicker());
      break;
    }

    container.innerHTML = "";
    container.appendChild(fragment);
  }

  function getDaysInMonth (year, month) {
    return month === 1 && year % 4 === 0 &&
      (year % 100 !== 0 || year % 400 === 0) ? 29 : DAYS_IN_MONTH[month];
  }

  function getWeekday (i) {
    i += firstDay;
    while (i >= 7) {
      i -= 7;
    }
    return WEEKDAYS[i];
  }

  function callback () {}

  // function setState (type, value) {
  //   switch (type) {
  //   case "date":
  //     setDate(value);
  //     break;
  //   case "month":
  //     break;
  //   case "year":
  //     break;
  //   }
  // }

  function setDate (d) {
    d = d.split("-");
    date = new Date(parseInt(d[0]), parseInt(d[1]), parseInt(d[2]));
    state.day = date.getDate();
    state.month = date.getMonth();
    state.year = date.getFullYear();
    // state.picker = "date";

    // render(state.picker);

    callback();
    return;
  }

  function setMonth (month) {
    month = parseInt(month);
    if (month < 0)  {
      state.month = 11;
      return setYear(state.year - 1);
    } else if (month > 11) {
      state.month = 0;
      return setYear(state.year + 1);
    } else {
      state.month = month;
    }
    // state.picker = "date";

    // render(state.picker);
    return;
  }

  function setYear (year) {
    state.year = parseInt(year) > 0 ? parseInt(year) : 0;

    // state.picker = "month";
    // render(state.picker);
    return;
  }

  function getStartYear (year, range) {
    return parseInt((year - 1) / range) * range + 1;
  }

  function renderHead (view) {
    var prev, change, next;

    //

    var head = createElement("div", {class: "pick-head"});

    // <button type="button" class="pick-btn pick-prev">
    prev = createElement("button", {
      type: "button",
      class: "pick-btn pick-prev"
    });
    // text
    prev.innerHTML = "&#9666";

    // bind event
    prev.addEventListener("click", function (e) {
      e.preventDefault();
      switch (view) {
      case "date":
        setMonth(state.month - 1);
        render("date");
        break;
      case "month":
        setYear(state.year - 1);
        render("month");
        break;
      case "year":
        setYear(state.year - 25);
        render("year");
        break;
      }
    });
    head.appendChild(prev);
    // </button>

    // <button type="button" class="pick-btn pick-change">
    change = createElement("button", {
      type: "button",
      class: "pick-btn pick-change"
    });
    // text
    switch (view) {
    case "date":
      change.innerHTML = MONTHS[state.month] + " " + state.year;
      change.value = "month";
      break;
    case "month":
      change.innerHTML = state.year;
      change.value = "year";
      break;
    case "year":
      var start = getStartYear(state.year, 25);
      var end = start + 24;
      change.innerHTML = start + " - " + end;
      change.className += " disabled";
      break;
    }
    // add event
    change.addEventListener("click", function (e) {
      e.preventDefault();
      render(this.value);
    });
    head.appendChild(change);
    // </button>

    // <button type="button" class="pick-btn pick-next">
    next = createElement("button", {
      type: "button",
      class: "pick-btn pick-next"
    });
    // text
    next.innerHTML = "&#9656";

    // bind event
    next.addEventListener("click", function (e) {
      e.preventDefault();
      switch (view) {
      case "date":
        setMonth(state.month + 1);
        render("date");
        break;
      case "month":
        setYear(state.year + 1);
        render("month");
        break;
      case "year":
        setYear(state.year + 25);
        render("year");
        break;
      }
    });
    head.appendChild(next);
    // </button>
    //

    return head;
  }

  function renderYearPicker () {
    var row, col, btn;
    var year = getStartYear(state.year, 25);

    //

    var yearpicker = createElement("table");
    //

    yearpicker.appendChild(createElement("tbody"));

    //

    row = createElement("tr");

    for (var r = 0, c = 0; r < 25; r++) {
      //
      col = createElement("td");
      // <button type="button" class="pick-btn pick-year" value={year}>
      btn = createElement("button", {
        type: "button",
        class: "pick-btn pick-year",
        value: year
      });
      // text
      btn.innerHTML = year;

      // active if selected year
      if (date.getFullYear() === year) {
        btn.className += " active";
      }

      // add event
      btn.addEventListener("click", function (e) {
        e.preventDefault();
        setYear(this.value);
        render("date");
      });

      col.appendChild(btn);
      // </button>
      row.appendChild(col);
      //

      year++;
      //

      if (++c === 5) {
        yearpicker.firstChild.appendChild(row);
        row = createElement("tr");
        c = 0;
      }
    }
    //

    // yearpick.innerHTML = "";
    // yearpick.appendChild(renderHead("year"));
    // yearpick.appendChild(table);
    //

    return yearpicker;
  }

  function renderMonthPicker () {
    var row, col, btn;
    var month = 0;

    //

    var monthpicker = createElement("table");
    //

    monthpicker.appendChild(createElement("tbody"));

    //

    row = createElement("tr");

    for (var r = 0, c = 0; r < 12; r++) {
      //
      col = createElement("td");
      // <button type="button" class="pick-btn pick-month" value={month}>
      btn = createElement("button", {
        type: "button",
        class: "pick-btn pick-month",
        value: month
      });
      // text
      btn.innerHTML = MONTHS[month];

      // set active if selected month
      if (date.getMonth() === month &&
      date.getFullYear() === state.year) {
        btn.classList.add("active");
      }

      // bind event
      btn.addEventListener("click", function (e) {
        e.preventDefault();
        setMonth(this.value);
        render("date");
      });

      col.appendChild(btn);
      // </button>

      row.appendChild(col);
      //

      month++;

      if (++c === 3) {
        monthpicker.firstChild.appendChild(row);
        row = createElement("tr");
        c = 0;
      }
      //

    }
    //

    // monthpick.innerHTML = "";
    // monthpick.appendChild(renderHead("month"));
    // monthpick.appendChild(table);
    //

    return monthpicker;
  }

  function renderDatePicker () {
    var row, col, btn;
    var day = 0, month = 0, year = 0;
    var start = 0;
    var weekday;
    var daysInMonth = getDaysInMonth(state.year, state.month);
    var before = new Date(state.year, state.month, 1).getDay() - firstDay;

    /*
    If week doesnt start on day 0
    check for starting weekday of selected month
    and push all days to fit the selected start day

    If month starts on start day add a additional week
    to preserve the 6 weeks calendar
    */
    if (before < 0) {
      before += 7;
    } else if (before === 0) {
      start -= 7;
    }

    /* weekdays row */

    //

    var datepicker = createElement("table");
    //

    datepicker.appendChild(createElement("thead"));
    //

    var head = createElement("tr");

    //
    for (var w = 0; w < 7; w++) {
      weekday = createElement("th");
      weekday.innerHTML = getWeekday(w);
      head.appendChild(weekday);
    }
    //
    datepicker.firstChild.appendChild(head);
    //

    //

    row = createElement("tr");

    //
    for (var i = start, c = 0; i < (42 + start); i++) {
      col = createElement("td");

      day = 1 + (i - before);
      month = state.month;
      year = state.year;

      // <button class="pick-btn pick-day">
      btn = createElement("button", {
        type: "button",
        class: "pick-btn pick-day"
      });

      /*
      if day is is before selected month
      set button to out
      */
      if (i < before) {
        if (state.month === 0) {
          month = 11;
          year = state.year - 1;
        } else {
          month = state.month - 1;
        }
        day = getDaysInMonth(year, month) + day;
        btn.classList.add("out");

      /*
      if day is after selected month
      set button to out
      */
      } else if (i >= (daysInMonth + before)) {
        if (state.month === 11) {
          month = 0;
          year = state.year + 1;
        } else {
          month = state.month + 1;
        }
        day = day - daysInMonth;
        btn.classList.add("out");
      }

      // set active if selected day
      if (date.getDate() === day &&
      date.getMonth() === month &&
      date.getFullYear() === year) {
        btn.classList.add("active");
      }

      // text
      btn.innerHTML = day;
      // value
      btn.value = year + "-" + month + "-" + day;

      // add event
      btn.addEventListener("click", function (e) {
        e.preventDefault();
        setDate(this.value);
        render("date");
      });

      col.appendChild(btn);
      // </button>

      row.appendChild(col);
      //

      if (++c === 7) {
        datepicker.firstChild.appendChild(row);
        row = createElement("tr");
        c = 0;
      }
      //

    }
    //

    // datepick.innerHTML = "";
    // datepick.appendChild(renderHead("day"));
    // datepick.appendChild(table);
    //

    return datepicker;
  }
})(window, document);<|endoftext|>


<commit_before>var makeAckable = require('./ackable');

// versioned events, acks, retries, resync
lichess.StrongSocket = function(url, version, settings) {

  var now = Date.now;

  var settings = $.extend(true, {}, lichess.StrongSocket.defaults, settings);
  var url = url;
  var version = version;
  var versioned = version !== false;
  var options = settings.options;
  var ws = null;
  var pingSchedule = null;
  var connectSchedule = null;
  var ackable = makeAckable(function(t, d) { send(t, d) });
  var lastPingTime = now();
  var pongCount = 0;
  var averageLag = 0;
  var tryOtherUrl = false;
  var autoReconnect = true;
  var nbConnects = 0;
  var storage = lichess.storage.make(options.baseUrlKey);

  var connect = function() {
    destroy();
    autoReconnect = true;
    var fullUrl = options.protocol + "//" + baseUrl() + url + "?" + $.param(settings.params);
    debug("connection attempt to " + fullUrl);
    try {
      ws = new WebSocket(fullUrl);
      ws.onerror = function(e) {
        onError(e);
      };
      ws.onclose = function(e) {
        if (autoReconnect) {
          debug('Will autoreconnect in ' + options.autoReconnectDelay);
          scheduleConnect(options.autoReconnectDelay);
        }
      };
      ws.onopen = function() {
        debug("connected to " + fullUrl);
        onSuccess();
        $('body').removeClass('offline');
        pingNow();
        lichess.pubsub.emit('socket.open')();
        ackable.resend();
      };
      ws.onmessage = function(e) {
        var m = JSON.parse(e.data);
        // if (Math.random() > 0.5) {
        //   console.log(m, 'skip');
        //   return;
        // }
        if (m.t === 'n') pong();
        // else debug(e.data);
        if (m.t === 'b') m.d.forEach(handle);
        else handle(m);
      };
    } catch (e) {
      onError(e);
    }
    scheduleConnect(options.pingMaxLag);
  };

  var send = function(t, d, o, noRetry) {
    o = o || {};
    var msg = { t: t };
    if (d !== undefined) {
      if (o.withLag) d.l = Math.round(averageLag);
      if (o.millis >= 0) d.s = Math.round(o.millis * 0.1).toString(36);
      msg.d = d;
    }
    if (o.ackable) ackable.register(t, d);
    var message = JSON.stringify(msg);
    debug("send " + message);
    try {
      ws.send(message);
    } catch (e) {
      // maybe sent before socket opens,
      // try again a second later.
      if (!noRetry) setTimeout(function() {
        send(t, d, o, true);
      }, 1000);
    }
  };
  lichess.pubsub.on('socket.send', send);

  var scheduleConnect = function(delay) {
    if (options.idle) delay = 10 * 1000 + Math.random() * 10 * 1000;
    // debug('schedule connect ' + delay);
    clearTimeout(pingSchedule);
    clearTimeout(connectSchedule);
    connectSchedule = setTimeout(function() {
      $('body').addClass('offline');
      tryOtherUrl = true;
      connect();
    }, delay);
  };

  var schedulePing = function(delay) {
    clearTimeout(pingSchedule);
    pingSchedule = setTimeout(pingNow, delay);
  };

  var pingNow = function() {
    clearTimeout(pingSchedule);
    clearTimeout(connectSchedule);
    try {
      ws.send(pingData());
      lastPingTime = now();
    } catch (e) {
      debug(e, true);
    }
    scheduleConnect(options.pingMaxLag);
  };

  var computePingDelay = function() {
    return options.pingDelay + (options.idle ? 1000 : 0);
  };

  var pong = function() {
    clearTimeout(connectSchedule);
    schedulePing(computePingDelay());
    var currentLag = Math.min(now() - lastPingTime, 10000);
    pongCount++;

    // Average first 4 pings, then switch to decaying average.
    var mix = pongCount > 4 ? 0.1 : 1 / pongCount;
    averageLag += mix * (currentLag - averageLag);

    lichess.pubsub.emit('socket.lag')(averageLag);
  };

  var pingData = function() {
    if (!versioned) return '{"t":"p"}';
    var data = {
      t: "p",
      v: version
    };
    if (pongCount % 8 === 2) data.l = Math.round(0.1 * averageLag);
    return JSON.stringify(data);
  };

  var handle = function(m) {
    if (m.v) {
      if (m.v <= version) {
        debug("already has event " + m.v);
        return;
      }
      if (m.v > version + 1) {
        debug("event gap detected from " + version + " to " + m.v);
        return;
      }
      version = m.v;
    }
    switch (m.t || false) {
      case false:
        break;
      case 'resync':
        lichess.reload();
        break;
      case 'ack':
        ackable.gotAck();
        break;
      default:
        lichess.pubsub.emit('socket.in.' + m.t)(m.d);
        var processed = settings.receive && settings.receive(m.t, m.d);
        if (!processed && settings.events[m.t]) settings.events[m.t](m.d || null, m);
    }
  };

  var debug = function(msg, always) {
    if (always || options.debug) {
      console.debug("[" + options.name + " " + settings.params.sri + "]", msg);
    }
  };

  var destroy = function() {
    clearTimeout(pingSchedule);
    clearTimeout(connectSchedule);
    disconnect();
    ws = null;
  };

  var disconnect = function(onNextConnect) {
    if (ws) {
      debug("Disconnect");
      autoReconnect = false;
      ws.onerror = ws.onclose = ws.onopen = ws.onmessage = $.noop;
      ws.close();
    }
    if (onNextConnect) options.onNextConnect = onNextConnect;
  };

  var onError = function(e) {
    options.debug = true;
    debug('error: ' + JSON.stringify(e));
    tryOtherUrl = true;
    setTimeout(function() {
      if (!$('#network_error').length) {
        $('#top').append('Network error');
      }
    }, 1000);
    clearTimeout(pingSchedule);
  };

  var onSuccess = function() {
    $('#network_error').remove();
    nbConnects++;
    if (nbConnects === 1) {
      options.onFirstConnect();
      var disconnectTimeout;
      lichess.idleTimer(10 * 60 * 1000, function() {
        options.idle = true;
        disconnectTimeout = setTimeout(lichess.socket.destroy, 2 * 60 * 60 * 1000);
      }, function() {
        options.idle = false;
        if (ws) clearTimeout(disconnectTimeout);
        else location.reload();
      });
    }
    if (options.onNextConnect) {
      options.onNextConnect();
      delete options.onNextConnect;
    }
  };

  var baseUrl = function() {
    var urls = options.baseUrls;
    var url = storage.get();
    if (!url) {
      url = urls[0];
      storage.set(url);
    } else if (tryOtherUrl) {
      tryOtherUrl = false;
      url = urls[(urls.indexOf(url) + 1) % urls.length];
      storage.set(url);
    }
    return url;
  };

  connect();
  window.addEventListener('unload', destroy);

  return {
    connect: connect,
    disconnect: disconnect,
    send: send,
    destroy: destroy,
    options: options,
    pingInterval: function() {
      return computePingDelay() + averageLag;
    },
    averageLag: function() {
      return averageLag;
    },
    getVersion: function() {
      return version;
    }
  };
};
lichess.StrongSocket.sri = Math.random().toString(36).slice(2, 12);
lichess.StrongSocket.available = window.WebSocket || window.MozWebSocket;
lichess.StrongSocket.defaults = {
  events: {
    fen: function(e) {
      $('.live_' + e.id).each(function() {
        lichess.parseFen($(this).data("fen", e.fen).data("lastmove", e.lm));
      });
    },
    challenges: function(d) {
      lichess.challengeApp.update(d);
    },
    notifications: function(d) {
      lichess.notifyApp.update(d, true);
    }
  },
  params: {
    sri: lichess.StrongSocket.sri
  },
  options: {
    name: "unnamed",
    idle: false,
    pingMaxLag: 8000, // time to wait for pong before reseting the connection
    pingDelay: 2000, // time between pong and ping
    autoReconnectDelay: 2000,
    protocol: location.protocol === 'https:' ? 'wss:' : 'ws:',
    baseUrls: (function(domain) {
      var main = 'socket.' + domain;
      var isProduction = /lichess\.org/.test(main);
      var extraPorts = [];
      if (isProduction) {
        if (location.protocol === 'https:') extraPorts = [9025, 9026, 9027, 9028, 9029];
        else extraPorts = [9021, 9022, 9023, 9024];
      }
      return [main].concat(extraPorts.map(function(port) {
        return main + ':' + port;
      }));
    })(document.domain),
    onFirstConnect: $.noop,
    baseUrlKey: 'surl5'
  }
};
<commit_msg>initialize JS vars as undefined<commit_after>var makeAckable = require('./ackable');

// versioned events, acks, retries, resync
lichess.StrongSocket = function(url, version, settings) {

  var now = Date.now;

  var settings = $.extend(true, {}, lichess.StrongSocket.defaults, settings);
  var url = url;
  var version = version;
  var versioned = version !== false;
  var options = settings.options;
  var ws;
  var pingSchedule;
  var connectSchedule;
  var ackable = makeAckable(function(t, d) { send(t, d) });
  var lastPingTime = now();
  var pongCount = 0;
  var averageLag = 0;
  var tryOtherUrl = false;
  var autoReconnect = true;
  var nbConnects = 0;
  var storage = lichess.storage.make(options.baseUrlKey);

  var connect = function() {
    destroy();
    autoReconnect = true;
    var fullUrl = options.protocol + "//" + baseUrl() + url + "?" + $.param(settings.params);
    debug("connection attempt to " + fullUrl);
    try {
      ws = new WebSocket(fullUrl);
      ws.onerror = function(e) {
        onError(e);
      };
      ws.onclose = function(e) {
        if (autoReconnect) {
          debug('Will autoreconnect in ' + options.autoReconnectDelay);
          scheduleConnect(options.autoReconnectDelay);
        }
      };
      ws.onopen = function() {
        debug("connected to " + fullUrl);
        onSuccess();
        $('body').removeClass('offline');
        pingNow();
        lichess.pubsub.emit('socket.open')();
        ackable.resend();
      };
      ws.onmessage = function(e) {
        var m = JSON.parse(e.data);
        // if (Math.random() > 0.5) {
        //   console.log(m, 'skip');
        //   return;
        // }
        if (m.t === 'n') pong();
        // else debug(e.data);
        if (m.t === 'b') m.d.forEach(handle);
        else handle(m);
      };
    } catch (e) {
      onError(e);
    }
    scheduleConnect(options.pingMaxLag);
  };

  var send = function(t, d, o, noRetry) {
    o = o || {};
    var msg = { t: t };
    if (d !== undefined) {
      if (o.withLag) d.l = Math.round(averageLag);
      if (o.millis >= 0) d.s = Math.round(o.millis * 0.1).toString(36);
      msg.d = d;
    }
    if (o.ackable) ackable.register(t, d);
    var message = JSON.stringify(msg);
    debug("send " + message);
    try {
      ws.send(message);
    } catch (e) {
      // maybe sent before socket opens,
      // try again a second later.
      if (!noRetry) setTimeout(function() {
        send(t, d, o, true);
      }, 1000);
    }
  };
  lichess.pubsub.on('socket.send', send);

  var scheduleConnect = function(delay) {
    if (options.idle) delay = 10 * 1000 + Math.random() * 10 * 1000;
    // debug('schedule connect ' + delay);
    clearTimeout(pingSchedule);
    clearTimeout(connectSchedule);
    connectSchedule = setTimeout(function() {
      $('body').addClass('offline');
      tryOtherUrl = true;
      connect();
    }, delay);
  };

  var schedulePing = function(delay) {
    clearTimeout(pingSchedule);
    pingSchedule = setTimeout(pingNow, delay);
  };

  var pingNow = function() {
    clearTimeout(pingSchedule);
    clearTimeout(connectSchedule);
    try {
      ws.send(pingData());
      lastPingTime = now();
    } catch (e) {
      debug(e, true);
    }
    scheduleConnect(options.pingMaxLag);
  };

  var computePingDelay = function() {
    return options.pingDelay + (options.idle ? 1000 : 0);
  };

  var pong = function() {
    clearTimeout(connectSchedule);
    schedulePing(computePingDelay());
    var currentLag = Math.min(now() - lastPingTime, 10000);
    pongCount++;

    // Average first 4 pings, then switch to decaying average.
    var mix = pongCount > 4 ? 0.1 : 1 / pongCount;
    averageLag += mix * (currentLag - averageLag);

    lichess.pubsub.emit('socket.lag')(averageLag);
  };

  var pingData = function() {
    if (!versioned) return '{"t":"p"}';
    var data = {
      t: "p",
      v: version
    };
    if (pongCount % 8 === 2) data.l = Math.round(0.1 * averageLag);
    return JSON.stringify(data);
  };

  var handle = function(m) {
    if (m.v) {
      if (m.v <= version) {
        debug("already has event " + m.v);
        return;
      }
      if (m.v > version + 1) {
        debug("event gap detected from " + version + " to " + m.v);
        return;
      }
      version = m.v;
    }
    switch (m.t || false) {
      case false:
        break;
      case 'resync':
        lichess.reload();
        break;
      case 'ack':
        ackable.gotAck();
        break;
      default:
        lichess.pubsub.emit('socket.in.' + m.t)(m.d);
        var processed = settings.receive && settings.receive(m.t, m.d);
        if (!processed && settings.events[m.t]) settings.events[m.t](m.d || null, m);
    }
  };

  var debug = function(msg, always) {
    if (always || options.debug) {
      console.debug("[" + options.name + " " + settings.params.sri + "]", msg);
    }
  };

  var destroy = function() {
    clearTimeout(pingSchedule);
    clearTimeout(connectSchedule);
    disconnect();
    ws = null;
  };

  var disconnect = function(onNextConnect) {
    if (ws) {
      debug("Disconnect");
      autoReconnect = false;
      ws.onerror = ws.onclose = ws.onopen = ws.onmessage = $.noop;
      ws.close();
    }
    if (onNextConnect) options.onNextConnect = onNextConnect;
  };

  var onError = function(e) {
    options.debug = true;
    debug('error: ' + JSON.stringify(e));
    tryOtherUrl = true;
    setTimeout(function() {
      if (!$('#network_error').length) {
        $('#top').append('Network error');
      }
    }, 1000);
    clearTimeout(pingSchedule);
  };

  var onSuccess = function() {
    $('#network_error').remove();
    nbConnects++;
    if (nbConnects === 1) {
      options.onFirstConnect();
      var disconnectTimeout;
      lichess.idleTimer(10 * 60 * 1000, function() {
        options.idle = true;
        disconnectTimeout = setTimeout(lichess.socket.destroy, 2 * 60 * 60 * 1000);
      }, function() {
        options.idle = false;
        if (ws) clearTimeout(disconnectTimeout);
        else location.reload();
      });
    }
    if (options.onNextConnect) {
      options.onNextConnect();
      delete options.onNextConnect;
    }
  };

  var baseUrl = function() {
    var urls = options.baseUrls;
    var url = storage.get();
    if (!url) {
      url = urls[0];
      storage.set(url);
    } else if (tryOtherUrl) {
      tryOtherUrl = false;
      url = urls[(urls.indexOf(url) + 1) % urls.length];
      storage.set(url);
    }
    return url;
  };

  connect();
  window.addEventListener('unload', destroy);

  return {
    connect: connect,
    disconnect: disconnect,
    send: send,
    destroy: destroy,
    options: options,
    pingInterval: function() {
      return computePingDelay() + averageLag;
    },
    averageLag: function() {
      return averageLag;
    },
    getVersion: function() {
      return version;
    }
  };
};
lichess.StrongSocket.sri = Math.random().toString(36).slice(2, 12);
lichess.StrongSocket.available = window.WebSocket || window.MozWebSocket;
lichess.StrongSocket.defaults = {
  events: {
    fen: function(e) {
      $('.live_' + e.id).each(function() {
        lichess.parseFen($(this).data("fen", e.fen).data("lastmove", e.lm));
      });
    },
    challenges: function(d) {
      lichess.challengeApp.update(d);
    },
    notifications: function(d) {
      lichess.notifyApp.update(d, true);
    }
  },
  params: {
    sri: lichess.StrongSocket.sri
  },
  options: {
    name: "unnamed",
    idle: false,
    pingMaxLag: 8000, // time to wait for pong before reseting the connection
    pingDelay: 2000, // time between pong and ping
    autoReconnectDelay: 2000,
    protocol: location.protocol === 'https:' ? 'wss:' : 'ws:',
    baseUrls: (function(domain) {
      var main = 'socket.' + domain;
      var isProduction = /lichess\.org/.test(main);
      var extraPorts = [];
      if (isProduction) {
        if (location.protocol === 'https:') extraPorts = [9025, 9026, 9027, 9028, 9029];
        else extraPorts = [9021, 9022, 9023, 9024];
      }
      return [main].concat(extraPorts.map(function(port) {
        return main + ':' + port;
      }));
    })(document.domain),
    onFirstConnect: $.noop,
    baseUrlKey: 'surl5'
  }
};
<|endoftext|>


<commit_before>async function component( args ) {
/*
base_component_id("vb_editor_component")
load_once_from_file(true)
*/

    //alert(JSON.stringify(args,null,2))
    var uid2 = uuidv4()
    var mm = null
    var texti = null
    if (args) {
        texti = args.text
    }
    var designMode = true
    var runtimeMode = false
    Vue.component("vb_editor_component",
    {
    //*** COPY_START ***//
      template: `

VB app designer

                                <component v-bind:is='item.base_component_id'></component>

                                            ● {{field.text}}

                                    <textarea v-on:keyup='generateCodeFromModel(model  )' class='col-md-6' v-if='field.type=="text" && (current_edited_item == field.id)'
                                            v-bind:style='"border-radius: 25px; padding:20px; background: " + (current_edited_item == field.id?"whitesmoke":"") + ";" + getStyle(field.id)' v-model='field.text'>
                                            </textarea>

                                        <button v-bind:class='fieldSize(field.id)>5?"active":""'  type=button class='btn btn-sm btn-info'      v-on:click='$event.stopPropagation();updateFieldCssStyle(field.id, "size",fieldSize(field.id)-1) '  > - </button>
                                        <button v-bind:class='fieldSize(field.id)<50?"active":""'  type=button class='btn btn-sm btn-info'      v-on:click='$event.stopPropagation();updateFieldCssStyle(field.id, "size",fieldSize(field.id)+1)'  > + </button>
                                        <button v-bind:class='getFieldCssStyle(field.id,"bold")?"active":""'  type=button class='btn btn-sm btn-info'      v-on:click='$event.stopPropagation();updateFieldCssStyle(field.id, "bold",getFieldCssStyle(field.id,"bold")?false:true)'  > B </button>
                                        <button v-bind:class='getFieldCssStyle(field.id,"bold")?"active":""'  type=button class='btn btn-sm btn-info'      v-on:click='$event.stopPropagation();updateFieldCssStyle(field.id, "bullet",getFieldCssStyle(field.id,"bullet")?false:true)'  > ●  </button>
                                        <button class='xs-4'  type=button class='btn btn-sm btn-info'  v-bind:disabled='index==0'    v-on:click='$event.stopPropagation();moveUp(field.id)'  > ↑ </button>
                                        <button class='xs-4'  type=button class='btn btn-sm btn-info'  v-bind:disabled='index==(model.fields.length - 1)'    v-on:click='$event.stopPropagation();moveDown(field.id)'  > ↓ </button>
                                        <button class='xs-4'  type=button class='btn btn-sm btn-info'  v-on:click='$event.stopPropagation();deleteField(field.id)'  > Delete </button>

                        <button  v-if='design_mode' type=button class='btn btn-info'      v-on:click='addField()'  >Add field</button>

                     <slot v-if='text' :text2="text"></slot>

`
        ,

        mounted: function() {
            mm = this
            document.getElementById(uid2).style.width="100%"

            document.getElementById(uid2).style.height="45vh"

            if (texti) {
                var json2 = this.getJsonModelFromCode(  texti  )
                mm.model = json2
                mm.edited_app_component_id = saveHelper.getValueOfCodeString(texti, "base_component_id")

                this.generateCodeFromModel(  json2  )

                this.read_only = saveHelper.getValueOfCodeString(texti, "read_only")
             //alert(this.text)
           }

           //editor.getSession().on('change', function() {
           //mm.text = editor.getSession().getValue();
           //alert("changed text to : " + mm.text)
           //   });
     },

     methods: {
     allowDrop: function(ev) {
         ev.preventDefault();
     },

     drag: function(ev,name) {
         //alert(JSON.stringify(ev,null,2))
         ev.dataTransfer.setData("text", name);
     },

     drop: function (ev) {
     //alert(21)
         var data = ev.dataTransfer.getData("text");
         var newItem = new Object()

         var doc = document.documentElement;
         var left = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);
         var top = (window.pageYOffset || doc.scrollTop)  - (doc.clientTop || 0);
         var rrr = ev.target.getBoundingClientRect()
         //alert(JSON.stringify(rrr,null,2))

         newItem.leftX = event.clientX  - rrr.left ;
         newItem.topY = event.clientY  - rrr.top;
         newItem.base_component_id = data
         this.refresh++
         this.model.components.push(newItem)
         //+ ") =" + JSON.stringify(data,null,2));
         ev.preventDefault();
         this.generateCodeFromModel(  mm.model  )

     },

        addField: function() {
            mm.model.fields.push({   id: mm.model.next_id,   type: "text",   text: "Enter text here",
                                      style: {}})
            mm.model.next_id ++
            this.generateCodeFromModel(  mm.model  )
            //alert("Added: " + JSON.stringify(mm.model,null,2))
        },

        getFieldCssStyle: function(   fieldId   , styleName) {
            var mm = this
            var itemD = null
            for (var tt=0; tt < mm.model.fields.length ; tt++) {
                var ciurr = mm.model.fields[tt]
                if (ciurr.id == fieldId) {
                    if (!ciurr.style) {
                        ciurr.style = {}
                        return null
                    }
                    if (ciurr.style[styleName]) {
                        return ciurr.style[styleName]
                    }
                    return null
                }
            }
            return null
        },

        fieldSize: function(fieldId) {
            var mm = this
            if (mm.getFieldCssStyle(fieldId,"size") == null) {
                return 16
            }
            return this.getFieldCssStyle(fieldId,"size")
        },

        updateFieldCssStyle: function(   fieldId   , styleName, styleValue) {
            var itemD = null
            var mm = this
            for (var tt=0; tt < mm.model.fields.length ; tt++) {
                var ciurr = mm.model.fields[tt]
                if (ciurr.id == fieldId) {
                    if (!ciurr.style) {
                        ciurr.style = {}
                    }
                    ciurr.style[styleName] = styleValue
                }
            }
            this.generateCodeFromModel(  mm.model  )
        },

        getStyle: function(fieldId) {
            var mm = this
            var styleT = ""
            for (var tt = 0; tt < mm.model.fields.length ; tt++) {
                var ciurr = mm.model.fields[tt]
                if (ciurr ) {
                    if (ciurr.id == fieldId) {
                        if (!ciurr.style) {
                            return ""
                        }
                        var fg = ciurr.style
                        if (fg.bold){
                            styleT += "font-weight: bold;"
                        }
                        styleT += "font-size: " + mm.fieldSize(fieldId) + "px;"
                        return styleT
                    }
                }
            }
            return ""
        },

        moveUp: function(   fieldId   ) {
            var itemD = null
            for (var tt=0; tt < mm.model.fields.length ; tt++) {
                var ciurr = mm.model.fields[tt]
                if (ciurr.id == fieldId) {
                    itemD = ciurr
                }
            }
            if (itemD) {
                var index = mm.model.fields.indexOf(  itemD  );
                if (index > -1) {
                  mm.model.fields.splice(index, 1);
                  mm.model.fields.splice(index - 1, 0, itemD);
                }

            }

            this.generateCodeFromModel(  mm.model  )
        },

        moveDown: function(   fieldId   ) {
            var itemD = null
            for (var tt=0; tt < mm.model.fields.length ; tt++) {
                var ciurr = mm.model.fields[tt]
                if (ciurr.id == fieldId) {
                    itemD = ciurr
                }
            }
            if (itemD) {
                var index = mm.model.fields.indexOf(  itemD  );
                if (index > -1) {
                  mm.model.fields.splice(index, 1);
                  mm.model.fields.splice(index + 1, 0, itemD);
                }

            }

            this.generateCodeFromModel(  mm.model  )
        },

        deleteField: function(   fieldId   ) {
            var itemD = null
            for (var tt=0; tt < mm.model.fields.length ; tt++) {
                var ciurr = mm.model.fields[tt]
                if (ciurr.id == fieldId) {
                    itemD = ciurr
                }
            }
            if (itemD) {
                var index = mm.model.fields.indexOf(  itemD  );
                if (index > -1) {
                  mm.model.fields.splice(index, 1);
                }
            }

            this.generateCodeFromModel(  mm.model  )
            //alert("Added: " + JSON.stringify(mm.model,null,2))
        },
        getText: function() {
            return this.text
        },
        setText: function(textValue) {
            this.text =  textValue
            var json2 = this.getJsonModelFromCode(  textValue  )
            mm.model = json2
            this.generateCodeFromModel(  json2  )
        }
        ,
        getJsonModelFromCode: function(  codeV  ) {
            var json2 = saveHelper.getValueOfCodeString(codeV,"formEditor",")//formEditor")
            return json2
        }

        ,
        generateCodeFromModel: async function(  jsonModel  ) {
            var startIndex = this.text.indexOf("//** gen_" + "start **//")
            var endIndex = this.text.indexOf("//** gen_" + "end **//")

            //zzz
            var sql =    "select  cast(code as text)  as  code  from  system_code  where " +
                         "        base_component_id = 'vb_editor_component'   and   code_tag = 'LATEST' "

            var results = await callApp({ driver_name:    "systemFunctions2",method_name:    "sql"},
                {   sql: sql  })

            var editorCode = results[0].code
            var stt = "//*** COPY_" + "START ***//"
            var editorCodeToCopyStart = editorCode.indexOf(stt) + stt.length
            var editorCodeToCopyEnd = editorCode.indexOf("//*** COPY_" + "END ***//")
            var editorCodeToCopy = editorCode.substring(editorCodeToCopyStart, editorCodeToCopyEnd)
            console.log(editorCodeToCopy)
            //alert(JSON.stringify(mm.model,null,2))

            this.text = this.text.substring(0,startIndex) +

                `//** gen_start **//
                var uid2 = uuidv4()
                var mm = null
                var texti = null
                var designMode = false
                var runtimeMode = true
                Vue.component('${this.edited_app_component_id}', {`

                + editorCodeToCopy +

                `,
                data: function () {
                  return {
                      design_mode: designMode,
                      runtime_mode: runtimeMode,
                      current_edited_item: null,
                      text: texti,
                      uid2: uid2,
                      model: `
                      + JSON.stringify(mm.model,null,2) +

                  `}
                }
              })`

              +
              this.text.substring(endIndex)
              //console.log(this.text)

              this.text = saveHelper.deleteCodeString(  this.text, "formEditor", ")//form" + "Editor")

              this.text = saveHelper.insertCodeString(  this.text,
                                                        "formEditor",
                                                        mm.model,
                                                        ")//form" + "Editor")
        }

     }
     //*** COPY_END ***//
     ,
     data: function () {
       return {
           design_mode:                 designMode,
           runtime_mode:                runtimeMode,
           edited_app_component_id:     null,
           current_edited_item:         null,
           text:                        texti,
           uid2:                        uid2,
           refresh:                     0,
           read_only:                   false,

           model:                      {
                                            next_id: 1,

                                            fields: [

                                                    ],

                                            components: [

                                                        ],
                                        }
       }
     }

    }
    )

}
<commit_msg>Load all needed components when a VB style app is loaded<commit_after>async function component( args ) {
/*
base_component_id("vb_editor_component")
load_once_from_file(true)
*/

    //alert(JSON.stringify(args,null,2))
    var uid2 = uuidv4()
    var mm = null
    var texti = null
    if (args) {
        texti = args.text
    }
    var designMode = true
    var runtimeMode = false
    Vue.component("vb_editor_component",
    {
    //*** COPY_START ***//
      template: `

VB app designer

                                <component  v-bind:refresh='refresh' v-bind:is='item.base_component_id'></component>

                                            ● {{field.text}}

                                    <textarea v-on:keyup='generateCodeFromModel(model  )' class='col-md-6' v-if='field.type=="text" && (current_edited_item == field.id)'
                                            v-bind:style='"border-radius: 25px; padding:20px; background: " + (current_edited_item == field.id?"whitesmoke":"") + ";" + getStyle(field.id)' v-model='field.text'>
                                            </textarea>

                                        <button v-bind:class='fieldSize(field.id)>5?"active":""'  type=button class='btn btn-sm btn-info'      v-on:click='$event.stopPropagation();updateFieldCssStyle(field.id, "size",fieldSize(field.id)-1) '  > - </button>
                                        <button v-bind:class='fieldSize(field.id)<50?"active":""'  type=button class='btn btn-sm btn-info'      v-on:click='$event.stopPropagation();updateFieldCssStyle(field.id, "size",fieldSize(field.id)+1)'  > + </button>
                                        <button v-bind:class='getFieldCssStyle(field.id,"bold")?"active":""'  type=button class='btn btn-sm btn-info'      v-on:click='$event.stopPropagation();updateFieldCssStyle(field.id, "bold",getFieldCssStyle(field.id,"bold")?false:true)'  > B </button>
                                        <button v-bind:class='getFieldCssStyle(field.id,"bold")?"active":""'  type=button class='btn btn-sm btn-info'      v-on:click='$event.stopPropagation();updateFieldCssStyle(field.id, "bullet",getFieldCssStyle(field.id,"bullet")?false:true)'  > ●  </button>
                                        <button class='xs-4'  type=button class='btn btn-sm btn-info'  v-bind:disabled='index==0'    v-on:click='$event.stopPropagation();moveUp(field.id)'  > ↑ </button>
                                        <button class='xs-4'  type=button class='btn btn-sm btn-info'  v-bind:disabled='index==(model.fields.length - 1)'    v-on:click='$event.stopPropagation();moveDown(field.id)'  > ↓ </button>
                                        <button class='xs-4'  type=button class='btn btn-sm btn-info'  v-on:click='$event.stopPropagation();deleteField(field.id)'  > Delete </button>

                        <button  v-if='design_mode' type=button class='btn btn-info'      v-on:click='addField()'  >Add field</button>

                     <slot v-if='text' :text2="text"></slot>

`
        ,

        mounted: async function() {
            mm = this

            document.getElementById(uid2).style.width="100%"

            document.getElementById(uid2).style.height="45vh"

            if (texti) {
                var json2 = this.getJsonModelFromCode(  texti  )
                mm.model = json2
                mm.edited_app_component_id = saveHelper.getValueOfCodeString(texti, "base_component_id")

                //this.generateCodeFromModel(  json2  )

                this.read_only = saveHelper.getValueOfCodeString(texti, "read_only")
             //alert(this.text)
           }

           for (var rtw = 0; rtw < mm.model.components.length ; rtw++ )
           {
                var newItem = mm.model.components[rtw]
                //alert(newItem.base_component_id)
                await load(newItem.base_component_id)
           }

           mm.$forceUpdate();

           //editor.getSession().on('change', function() {
           //mm.text = editor.getSession().getValue();
           //alert("changed text to : " + mm.text)
           //   });
     },

     methods: {
     allowDrop: function(ev) {
         ev.preventDefault();
     },

     drag: function(ev,name) {
         //alert(JSON.stringify(ev,null,2))
         ev.dataTransfer.setData("text", name);
     },

     drop: async function (ev) {
     //alert(21)
         var data = ev.dataTransfer.getData("text");
         var newItem = new Object()

         var doc = document.documentElement;
         var left = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);
         var top = (window.pageYOffset || doc.scrollTop)  - (doc.clientTop || 0);
         var rrr = ev.target.getBoundingClientRect()
         //alert(JSON.stringify(rrr,null,2))

         newItem.leftX = event.clientX  - rrr.left ;
         newItem.topY = event.clientY  - rrr.top;
         newItem.base_component_id = data
         this.refresh++
         await load(newItem.base_component_id)
         this.model.components.push(newItem)
         //+ ") =" + JSON.stringify(data,null,2));
         ev.preventDefault();
         this.generateCodeFromModel(  mm.model  )

     },

        addField: function() {
            mm.model.fields.push({   id: mm.model.next_id,   type: "text",   text: "Enter text here",
                                      style: {}})
            mm.model.next_id ++
            this.generateCodeFromModel(  mm.model  )
            //alert("Added: " + JSON.stringify(mm.model,null,2))
        },

        getFieldCssStyle: function(   fieldId   , styleName) {
            var mm = this
            var itemD = null
            for (var tt=0; tt < mm.model.fields.length ; tt++) {
                var ciurr = mm.model.fields[tt]
                if (ciurr.id == fieldId) {
                    if (!ciurr.style) {
                        ciurr.style = {}
                        return null
                    }
                    if (ciurr.style[styleName]) {
                        return ciurr.style[styleName]
                    }
                    return null
                }
            }
            return null
        },

        fieldSize: function(fieldId) {
            var mm = this
            if (mm.getFieldCssStyle(fieldId,"size") == null) {
                return 16
            }
            return this.getFieldCssStyle(fieldId,"size")
        },

        updateFieldCssStyle: function(   fieldId   , styleName, styleValue) {
            var itemD = null
            var mm = this
            for (var tt=0; tt < mm.model.fields.length ; tt++) {
                var ciurr = mm.model.fields[tt]
                if (ciurr.id == fieldId) {
                    if (!ciurr.style) {
                        ciurr.style = {}
                    }
                    ciurr.style[styleName] = styleValue
                }
            }
            this.generateCodeFromModel(  mm.model  )
        },

        getStyle: function(fieldId) {
            var mm = this
            var styleT = ""
            for (var tt = 0; tt < mm.model.fields.length ; tt++) {
                var ciurr = mm.model.fields[tt]
                if (ciurr ) {
                    if (ciurr.id == fieldId) {
                        if (!ciurr.style) {
                            return ""
                        }
                        var fg = ciurr.style
                        if (fg.bold){
                            styleT += "font-weight: bold;"
                        }
                        styleT += "font-size: " + mm.fieldSize(fieldId) + "px;"
                        return styleT
                    }
                }
            }
            return ""
        },

        moveUp: function(   fieldId   ) {
            var itemD = null
            for (var tt=0; tt < mm.model.fields.length ; tt++) {
                var ciurr = mm.model.fields[tt]
                if (ciurr.id == fieldId) {
                    itemD = ciurr
                }
            }
            if (itemD) {
                var index = mm.model.fields.indexOf(  itemD  );
                if (index > -1) {
                  mm.model.fields.splice(index, 1);
                  mm.model.fields.splice(index - 1, 0, itemD);
                }

            }

            this.generateCodeFromModel(  mm.model  )
        },

        moveDown: function(   fieldId   ) {
            var itemD = null
            for (var tt=0; tt < mm.model.fields.length ; tt++) {
                var ciurr = mm.model.fields[tt]
                if (ciurr.id == fieldId) {
                    itemD = ciurr
                }
            }
            if (itemD) {
                var index = mm.model.fields.indexOf(  itemD  );
                if (index > -1) {
                  mm.model.fields.splice(index, 1);
                  mm.model.fields.splice(index + 1, 0, itemD);
                }

            }

            this.generateCodeFromModel(  mm.model  )
        },

        deleteField: function(   fieldId   ) {
            var itemD = null
            for (var tt=0; tt < mm.model.fields.length ; tt++) {
                var ciurr = mm.model.fields[tt]
                if (ciurr.id == fieldId) {
                    itemD = ciurr
                }
            }
            if (itemD) {
                var index = mm.model.fields.indexOf(  itemD  );
                if (index > -1) {
                  mm.model.fields.splice(index, 1);
                }
            }

            this.generateCodeFromModel(  mm.model  )
            //alert("Added: " + JSON.stringify(mm.model,null,2))
        },
        getText: function() {
            return this.text
        },
        setText: function(textValue) {
            this.text =  textValue
            var json2 = this.getJsonModelFromCode(  textValue  )
            mm.model = json2
            this.generateCodeFromModel(  json2  )
        }
        ,
        getJsonModelFromCode: function(  codeV  ) {
            var json2 = saveHelper.getValueOfCodeString(codeV,"formEditor",")//formEditor")
            return json2
        }

        ,
        generateCodeFromModel: async function(  jsonModel  ) {
            var startIndex = this.text.indexOf("//** gen_" + "start **//")
            var endIndex = this.text.indexOf("//** gen_" + "end **//")

            //zzz
            var sql =    "select  cast(code as text)  as  code  from  system_code  where " +
                         "        base_component_id = 'vb_editor_component'   and   code_tag = 'LATEST' "

            var results = await callApp({ driver_name:    "systemFunctions2",method_name:    "sql"},
                {   sql: sql  })

            var editorCode = results[0].code
            var stt = "//*** COPY_" + "START ***//"
            var editorCodeToCopyStart = editorCode.indexOf(stt) + stt.length
            var editorCodeToCopyEnd = editorCode.indexOf("//*** COPY_" + "END ***//")
            var editorCodeToCopy = editorCode.substring(editorCodeToCopyStart, editorCodeToCopyEnd)
            console.log(editorCodeToCopy)
            //alert(JSON.stringify(mm.model,null,2))

            this.text = this.text.substring(0,startIndex) +

                `//** gen_start **//
                var uid2 = uuidv4()
                var mm = null
                var texti = null
                var designMode = false
                var runtimeMode = true
                Vue.component('${this.edited_app_component_id}', {`

                + editorCodeToCopy +

                `,
                data: function () {
                  return {
                      design_mode: designMode,
                      runtime_mode: runtimeMode,
                      current_edited_item: null,
                      text: texti,
                      uid2: uid2,
                      model: `
                      + JSON.stringify(mm.model,null,2) +

                  `}
                }
              })`

              +
              this.text.substring(endIndex)
              //console.log(this.text)

              this.text = saveHelper.deleteCodeString(  this.text, "formEditor", ")//form" + "Editor")

              this.text = saveHelper.insertCodeString(  this.text,
                                                        "formEditor",
                                                        mm.model,
                                                        ")//form" + "Editor")
        }

     }
     //*** COPY_END ***//
     ,
     data: function () {
       return {
           design_mode:                 designMode,
           runtime_mode:                runtimeMode,
           edited_app_component_id:     null,
           current_edited_item:         null,
           text:                        texti,
           uid2:                        uid2,
           refresh:                     0,
           read_only:                   false,

           model:                      {
                                            next_id: 1,

                                            fields: [

                                                    ],

                                            components: [

                                                        ],
                                        }
       }
     }

    }
    )

}
<|endoftext|>