<commit_before>/* global django,ContentEditor */

/* Polyfilling a bit */
if (!Element.prototype.matches)
    Element.prototype.matches = Element.prototype.msMatchesSelector ||
                                Element.prototype.webkitMatchesSelector;

if (!Element.prototype.closest) {
    Element.prototype.closest = function(s) {
        var el = this;
        if (!document.documentElement.contains(el)) return null;
        do {
            if (el.matches(s)) return el;
            el = el.parentElement || el.parentNode;
        } while (el !== null && el.nodeType === 1);
        return null;
    };
}
/* Polyfill end */

django.jQuery(function($) {
  var context = document.getElementById("content-editor-context");
  if (!context) return;

  window.ContentEditor = {
    addContent: function addContent(label) {
      var plugin = ContentEditor.pluginsByKey[label];
      plugin && $("#" + plugin.prefix + "-group .add-row a").click();
    },
    addPluginButton: function addPluginButton(label, html) {
      var plugin = ContentEditor.pluginsByKey[label];
      if (!plugin) return;

      var unit = document.querySelector(".control-unit.plugin-buttons");
      if (!unit) {
        unit = document.createElement("div");
        unit.className = "control-unit plugin-buttons";
        var mc = document.querySelector(".machine-control");
        mc.insertBefore(unit, mc.firstChild);
      }

      var button = document.createElement("a");
      var $button = $(button);
      $button.data("plugin", plugin.key);
      button.className = "plugin-button";
      button.title = plugin.title;
      button.addEventListener("click", function() {
        ContentEditor.addContent(plugin.key);
      });
      button.innerHTML = html;

      unit.appendChild(button);

      hideNotAllowedPluginButtons($button);
    }
  };

  // .dataset.context instead of getAttribute would be nicer
  $.extend(
    window.ContentEditor,
    JSON.parse(context.getAttribute("data-context"))
  );

  ContentEditor.pluginsByKey = {};
  ContentEditor.prefixToKey = {};
  for (var i = 0; i < ContentEditor.plugins.length; ++i) {
    ContentEditor.pluginsByKey[ContentEditor.plugins[i].key] =
      ContentEditor.plugins[i];
    ContentEditor.prefixToKey[ContentEditor.plugins[i].prefix] =
      ContentEditor.plugins[i].key;
  }

  // Add basic structure. There is always at least one inline group if
  // we even have any plugins.
  $(".inline-group:first").before(
    '
' +
      '<button type="button" class="toggle">' +
      ContentEditor.messages.toggle +
      "</button>" +
      "
" +
      '
' +
      '

' +
      "
"
  );

  var orderMachine = $(".order-machine"),
    machineEmptyMessage = $('
')
      .text(ContentEditor.messages.empty)
      .appendTo(orderMachine);

  // Pre map plugin regions
  var pluginRegions = (function() {
    var result = {};
    var plugins = ContentEditor.plugins;
    for (var i = 0; i < plugins.length; i++) {
      result[plugins[i].key] = plugins[i].regions;
    }
    return result;
  })();

  function ensureDraggable(inline) {
    inline = inline[0];

    if (!inline.getAttribute("draggable")) {
      inline.setAttribute("draggable", true);
      inline.addEventListener("dragstart", function(e) {
        e.target.closest(".inline-related").classList.add("fs-dragging");
        e.dataTransfer.dropEffect = "move";
        e.dataTransfer.effectAllowed = "move";
        try {
          e.dataTransfer.setData("text/plain", "thing");
        } catch (e) {
          // IE11 needs this.
        }

        window.___dragging = this;
      });
      inline.addEventListener("dragend", function(e) {
        $(".fs-dragging").removeClass("fs-dragging");
        $(".fs-dragover").removeClass("fs-dragover");
      });
      inline.addEventListener(
        "dragover",
        function(e) {
          e.preventDefault();
          $(".fs-dragover").removeClass("fs-dragover");
          e.target.closest(".inline-related").classList.add("fs-dragover");
        },
        true
      );
      inline.addEventListener("drop", function(e) {
        e.preventDefault();
        insertBefore(window.___dragging, e.target.closest(".inline-related"));
      });
    } // !getAttribute("draggable")
  }

  function reorderInlines(context) {
    context = context || orderMachine;
    var inlines = context.find(".inline-related");
    inlines.not(".empty-form").each(function() {
      $(document).trigger("content-editor:deactivate", [$(this)]);

      ensureDraggable($(this));
    });

    inlines.detach();
    orderMachine.append(inlines);

    inlines.each(function() {
      var ordering = $(".field-ordering input", this).val() || 1e9;
      this.style.order = ordering;
      ensureDraggable($(this));
    });

    inlines.not(".empty-form").each(function() {
      $(document).trigger("content-editor:activate", [$(this)]);
    });
  }

  function buildDropdown(contents, title) {
    var select = document.createElement("select"),
      idx = 0;

    if (title) select.options[idx++] = new Option(title, "", true);

    for (var i = 0; i < contents.length; i++) {
      select.options[idx++] = new Option(contents[i].title, contents[i].key);
    }
    return select;
  }

  // Hides plugins that are not allowed in this region
  function hideNotAllowedDropdown() {
    $(ContentEditor.machineControlSelect)
      .find("option")
      .each(function() {
        var $option = $(this);
        var allowed = pluginRegions[$option.val()];
        if (!allowed || $.inArray(ContentEditor.currentRegion, allowed) >= 0) {
          $option.show();
        } else {
          $option.hide();
        }
      });
  }

  // Hide not allowed plugin buttons
  // If $buttons only checks this buttons, else checks all
  function hideNotAllowedPluginButtons($buttons) {
    $buttons = $buttons
      ? $buttons
      : $(".control-unit.plugin-buttons .plugin-button");

    var region = ContentEditor.currentRegion;

    $buttons.each(function() {
      var $button = $(this);
      var plugin = $button.data("plugin");
      var allowed = pluginRegions[plugin];

      if (!allowed || $.inArray(region, allowed) >= 0) {
        // Allowed
        $button.show();
      } else {
        // Not allowed
        $button.hide();
      }
    });
  }

  // Fetch the inline type from id
  function getInlineType(inline) {
    var match = /^([a-z0-9_]+)-\d+$/g.exec($(inline).attr("id"));
    if (match) {
      return ContentEditor.prefixToKey[match[1]];
    }
    return null;
  }

  function attachMoveToRegionDropdown(inline) {
    // Filter allowed regions
    var inlineType = getInlineType(inline);
    var regions = [];
    for (var i = 0; i < ContentEditor.regions.length; i++) {
      if (
        !inlineType ||
        !pluginRegions[inlineType] ||
        $.inArray(ContentEditor.regions[i].key, pluginRegions[inlineType]) >= 0
      ) {
        regions.push(ContentEditor.regions[i]);
      }
    }

    if (regions.length < 2) return;

    var controls = document.createElement("div"),
      select = buildDropdown(regions),
      regionInput = inline.find(".field-region input");

    select.value = regionInput.val();
    controls.className = "inline-controls";
    controls.appendChild(select);
    inline.append(controls);

    select.addEventListener("change", function() {
      inline.attr("data-region", select.value);
      regionInput.val(select.value);
      hideInlinesFromOtherRegions();
      setBiggestOrdering(inline);
      reorderInlines();
    });
  }

  // Assing data-region to all inlines.
  // We also want to the data attribute to be visible to selectors (that's why we're using $.attr)
  function assignRegionDataAttribute() {
    orderMachine.find(".inline-related:not(.empty-form)").each(function() {
      var $this = $(this),
        region = $this.find(".field-region input").val();

      $this.attr("data-region", region);
      attachMoveToRegionDropdown($this);
    });
  }

  function setBiggestOrdering(row) {
    var orderings = [];
    orderMachine.find(".field-ordering input").each(function() {
      if (!isNaN(+this.value)) orderings.push(+this.value);
    });
    var ordering = 10 + Math.max.apply(null, orderings);
    row.find(".field-ordering input").val(ordering);
    row.css("order", ordering);
  }

  function insertBefore(row, before) {
    var beforeOrdering = +before.querySelector(".field-ordering input").value,
      beforeRows = [],
      afterRows = [];
    orderMachine.find(".inline-related:not(.empty-form)").each(function() {
      var thisOrderingField = this.querySelector(".field-ordering input");
      if (this != row && !isNaN(+thisOrderingField.value)) {
        if (+thisOrderingField.value >= beforeOrdering) {
          afterRows.push([this, thisOrderingField]);
        } else {
          beforeRows.push([this, thisOrderingField]);
        }
      }
    });
    beforeRows.sort(function(a, b) {
      return a[1].value - b[1].value;
    });
    afterRows.sort(function(a, b) {
      return a[1].value - b[1].value;
    });
    var rows = [].concat(beforeRows);
    rows.push([row, row.querySelector(".field-ordering input")]);
    rows = rows.concat(afterRows);
    for (var i = 0; i < rows.length; ++i) {
      var row = rows[i];
      row[1].value = row[0].style.order = 10 * (1 + i);
    }
  }

  function hideInlinesFromOtherRegions() {
    var inlines = orderMachine.find(".inline-related:not(.empty-form)");
    inlines.addClass("content-editor-hidden");
    var shown = inlines.filter(
      '[data-region="' + ContentEditor.currentRegion + '"]'
    );
    machineEmptyMessage.addClass("hidden");
    if (shown.length) {
      shown.removeClass("content-editor-hidden");
    } else {
      machineEmptyMessage.removeClass("hidden");
    }
  }

  var pluginInlineGroups = (function selectPluginInlineGroups() {
    var selector = [];
    for (var i = 0; i < ContentEditor.plugins.length; i++) {
      selector.push("#" + ContentEditor.plugins[i].prefix + "-group");
    }
    return $(selector.join(", "));
  })();

  reorderInlines(pluginInlineGroups);
  pluginInlineGroups.hide();
  assignRegionDataAttribute();

  // Always move empty forms to the end, because new plugins are inserted
  // just before its empty form. Also, assign region data.
  $(document).on("formset:added", function newForm(event, row) {
    row.find(".field-region input").val(ContentEditor.currentRegion);
    row.attr("data-region", ContentEditor.currentRegion);

    setBiggestOrdering(row);
    attachMoveToRegionDropdown(row);
    ensureDraggable(row);

    machineEmptyMessage.addClass("hidden");

    $(document).trigger("content-editor:activate", [row]);
  });

  $(document).on("formset:removed", function resetInlines() {
    if (
      !orderMachine.find(
        '.inline-related[data-region="' + ContentEditor.currentRegion + '"]'
      ).length
    ) {
      machineEmptyMessage.removeClass("hidden");
    }
    orderMachine
      .find(".inline-related.last-related:not(.empty-form)")
      .each(function() {
        $(document).trigger("content-editor:deactivate", [$(this)]);
      });

    // As soon as possible, but not sooner (let the inline.js code run to the end first)
    setTimeout(function() {
      orderMachine
        .find(".inline-related.last-related:not(.empty-form)")
        .each(function() {
          $(document).trigger("content-editor:activate", [$(this)]);
        });
    }, 0);
  });

  // Initialize tabs and currentRegion.
  (function() {
    var tabContainer = $(".tabs.regions");
    for (var i = 0; i < ContentEditor.regions.length; i++) {
      var t = document.createElement("h2");
      t.textContent = ContentEditor.regions[i].title;
      t.setAttribute("data-region", ContentEditor.regions[i].key);
      tabContainer.append(t);
    }

    var tabs = tabContainer.find("h2"),
      tab;
    tabs.on("click", function() {
      ContentEditor.currentRegion = $(this).data("region");
      tabs
        .removeClass("active")
        .filter('[data-region="' + ContentEditor.currentRegion + '"]')
        .addClass("active");
      hideInlinesFromOtherRegions();
      window.location.hash = "tab_" + ContentEditor.currentRegion;

      // Make sure only allowed plugins are in select
      hideNotAllowedDropdown();
      hideNotAllowedPluginButtons();
    });

    // Restore tab if location hash matches.
    if (
      window.location.hash &&
      (tab = tabs.filter(
        '[data-region="' + window.location.hash.substr(5) + '"]'
      )) &&
      tab.length
    ) {
      tab.click();
    } else {
      tabs.eq(0).click();
    }

    tabContainer.find(".toggle").on("click", function() {
      $(".order-machine .inline-related").toggleClass("collapsed");
    });
  })();

  $(document)
    .on("content-editor:deactivate", function(event, row) {
      row.find("fieldset").addClass("content-editor-hidden");
    })
    .on("content-editor:activate", function(event, row) {
      row.find("fieldset").removeClass("content-editor-hidden");
    });

  // Hide fieldsets of to-be-deleted inlines.
  orderMachine.on(
    "click",
    ".delete>input[type=checkbox]",
    function toggleForDeletionClass() {
      $(this)
        .closest(".inline-related")
        [this.checked ? "addClass" : "removeClass"]("for-deletion");
    }
  );

  // Try to keep the current region tab (location hash).
  $("form").submit(function() {
    var form = $(this);
    form.attr("action", form.attr("action") + window.location.hash);
    return true;
  });

  // Cmd-S and Escape behavior.
  $(document).keydown(function handleKeys(event) {
    if (event.which == 83 && event.metaKey) {
      $(
        "form input[name=" + (event.shiftKey ? "_continue" : "_save") + "]"
      ).click();
      return false;
    } else if (event.which == 27) {
      // TODO cancel an ongoing drag.
      // orderMachine.sortable("cancel");
    }
  });
  (function buildPluginDropdown() {
    var select = buildDropdown(
      ContentEditor.plugins,
      ContentEditor.messages.createNew
    );
    select.addEventListener("change", function() {
      ContentEditor.addContent(select.value);
      select.value = "";
    });
    ContentEditor.machineControlSelect = select;
    hideNotAllowedDropdown();
    $(select)
      .appendTo(".machine-control")
      .wrap('

');
  })();

  $(document).trigger("content-editor:ready");
});
<commit_msg>Fix a bug with dragging new content blocks<commit_after>/* global django,ContentEditor */

/* Polyfilling a bit */
if (!Element.prototype.matches)
    Element.prototype.matches = Element.prototype.msMatchesSelector ||
                                Element.prototype.webkitMatchesSelector;

if (!Element.prototype.closest) {
    Element.prototype.closest = function(s) {
        var el = this;
        if (!document.documentElement.contains(el)) return null;
        do {
            if (el.matches(s)) return el;
            el = el.parentElement || el.parentNode;
        } while (el !== null && el.nodeType === 1);
        return null;
    };
}
/* Polyfill end */

django.jQuery(function($) {
  var context = document.getElementById("content-editor-context");
  if (!context) return;

  window.ContentEditor = {
    addContent: function addContent(label) {
      var plugin = ContentEditor.pluginsByKey[label];
      plugin && $("#" + plugin.prefix + "-group .add-row a").click();
    },
    addPluginButton: function addPluginButton(label, html) {
      var plugin = ContentEditor.pluginsByKey[label];
      if (!plugin) return;

      var unit = document.querySelector(".control-unit.plugin-buttons");
      if (!unit) {
        unit = document.createElement("div");
        unit.className = "control-unit plugin-buttons";
        var mc = document.querySelector(".machine-control");
        mc.insertBefore(unit, mc.firstChild);
      }

      var button = document.createElement("a");
      var $button = $(button);
      $button.data("plugin", plugin.key);
      button.className = "plugin-button";
      button.title = plugin.title;
      button.addEventListener("click", function() {
        ContentEditor.addContent(plugin.key);
      });
      button.innerHTML = html;

      unit.appendChild(button);

      hideNotAllowedPluginButtons($button);
    }
  };

  // .dataset.context instead of getAttribute would be nicer
  $.extend(
    window.ContentEditor,
    JSON.parse(context.getAttribute("data-context"))
  );

  ContentEditor.pluginsByKey = {};
  ContentEditor.prefixToKey = {};
  for (var i = 0; i < ContentEditor.plugins.length; ++i) {
    ContentEditor.pluginsByKey[ContentEditor.plugins[i].key] =
      ContentEditor.plugins[i];
    ContentEditor.prefixToKey[ContentEditor.plugins[i].prefix] =
      ContentEditor.plugins[i].key;
  }

  // Add basic structure. There is always at least one inline group if
  // we even have any plugins.
  $(".inline-group:first").before(
    '
' +
      '<button type="button" class="toggle">' +
      ContentEditor.messages.toggle +
      "</button>" +
      "
" +
      '
' +
      '

' +
      "
"
  );

  var orderMachine = $(".order-machine"),
    machineEmptyMessage = $('
')
      .text(ContentEditor.messages.empty)
      .appendTo(orderMachine);

  // Pre map plugin regions
  var pluginRegions = (function() {
    var result = {};
    var plugins = ContentEditor.plugins;
    for (var i = 0; i < plugins.length; i++) {
      result[plugins[i].key] = plugins[i].regions;
    }
    return result;
  })();

  function ensureDraggable(arg) {
    if (arg.hasClass("empty-form") || arg.hasClass("fs-draggable")) return;

    var inline = arg[0];

    inline.addEventListener("dragstart", function(e) {
      // window.__fs_dragging = e.target.closest(".inline-related");
      window.__fs_dragging = inline;
      window.__fs_dragging.classList.add("fs-dragging");

      e.dataTransfer.dropEffect = "move";
      e.dataTransfer.effectAllowed = "move";
      try {
        e.dataTransfer.setData("text/plain", "thing");
      } catch (e) {
        // IE11 needs this.
      }
    });
    inline.addEventListener("dragend", function(e) {
      $(".fs-dragging").removeClass("fs-dragging");
      $(".fs-dragover").removeClass("fs-dragover");
    });
    inline.addEventListener(
      "dragover",
      function(e) {
        e.preventDefault();
        $(".fs-dragover").removeClass("fs-dragover");
        e.target.closest(".inline-related").classList.add("fs-dragover");
      },
      true
    );
    inline.addEventListener("drop", function(e) {
      e.preventDefault();
      console.log(window.__fs_dragging, e.target, e.target.closest(".inline-related"));
      insertBefore(window.__fs_dragging, e.target.closest(".inline-related"));
    });

    inline.setAttribute("draggable", true);
    arg.addClass("fs-draggable");
  }

  function reorderInlines(context) {
    context = context || orderMachine;
    var inlines = context.find(".inline-related");
    inlines.not(".empty-form").each(function() {
      $(document).trigger("content-editor:deactivate", [$(this)]);

      ensureDraggable($(this));
    });

    inlines.detach();
    orderMachine.append(inlines);

    inlines.each(function() {
      var ordering = $(".field-ordering input", this).val() || 1e9;
      this.style.order = ordering;
      ensureDraggable($(this));
    });

    inlines.not(".empty-form").each(function() {
      $(document).trigger("content-editor:activate", [$(this)]);
    });
  }

  function buildDropdown(contents, title) {
    var select = document.createElement("select"),
      idx = 0;

    if (title) select.options[idx++] = new Option(title, "", true);

    for (var i = 0; i < contents.length; i++) {
      select.options[idx++] = new Option(contents[i].title, contents[i].key);
    }
    return select;
  }

  // Hides plugins that are not allowed in this region
  function hideNotAllowedDropdown() {
    $(ContentEditor.machineControlSelect)
      .find("option")
      .each(function() {
        var $option = $(this);
        var allowed = pluginRegions[$option.val()];
        if (!allowed || $.inArray(ContentEditor.currentRegion, allowed) >= 0) {
          $option.show();
        } else {
          $option.hide();
        }
      });
  }

  // Hide not allowed plugin buttons
  // If $buttons only checks this buttons, else checks all
  function hideNotAllowedPluginButtons($buttons) {
    $buttons = $buttons
      ? $buttons
      : $(".control-unit.plugin-buttons .plugin-button");

    var region = ContentEditor.currentRegion;

    $buttons.each(function() {
      var $button = $(this);
      var plugin = $button.data("plugin");
      var allowed = pluginRegions[plugin];

      if (!allowed || $.inArray(region, allowed) >= 0) {
        // Allowed
        $button.show();
      } else {
        // Not allowed
        $button.hide();
      }
    });
  }

  // Fetch the inline type from id
  function getInlineType(inline) {
    var match = /^([a-z0-9_]+)-\d+$/g.exec($(inline).attr("id"));
    if (match) {
      return ContentEditor.prefixToKey[match[1]];
    }
    return null;
  }

  function attachMoveToRegionDropdown(inline) {
    // Filter allowed regions
    var inlineType = getInlineType(inline);
    var regions = [];
    for (var i = 0; i < ContentEditor.regions.length; i++) {
      if (
        !inlineType ||
        !pluginRegions[inlineType] ||
        $.inArray(ContentEditor.regions[i].key, pluginRegions[inlineType]) >= 0
      ) {
        regions.push(ContentEditor.regions[i]);
      }
    }

    if (regions.length < 2) return;

    var controls = document.createElement("div"),
      select = buildDropdown(regions),
      regionInput = inline.find(".field-region input");

    select.value = regionInput.val();
    controls.className = "inline-controls";
    controls.appendChild(select);
    inline.append(controls);

    select.addEventListener("change", function() {
      inline.attr("data-region", select.value);
      regionInput.val(select.value);
      hideInlinesFromOtherRegions();
      setBiggestOrdering(inline);
      reorderInlines();
    });
  }

  // Assing data-region to all inlines.
  // We also want to the data attribute to be visible to selectors (that's why we're using $.attr)
  function assignRegionDataAttribute() {
    orderMachine.find(".inline-related:not(.empty-form)").each(function() {
      var $this = $(this),
        region = $this.find(".field-region input").val();

      $this.attr("data-region", region);
      attachMoveToRegionDropdown($this);
    });
  }

  function setBiggestOrdering(row) {
    var orderings = [];
    orderMachine.find(".field-ordering input").each(function() {
      if (!isNaN(+this.value)) orderings.push(+this.value);
    });
    var ordering = 10 + Math.max.apply(null, orderings);
    row.find(".field-ordering input").val(ordering);
    row.css("order", ordering);
  }

  function insertBefore(row, before) {
    var beforeOrdering = +before.querySelector(".field-ordering input").value,
      beforeRows = [],
      afterRows = [];
    orderMachine.find(".inline-related:not(.empty-form)").each(function() {
      var thisOrderingField = this.querySelector(".field-ordering input");
      if (this != row && !isNaN(+thisOrderingField.value)) {
        if (+thisOrderingField.value >= beforeOrdering) {
          afterRows.push([this, thisOrderingField]);
        } else {
          beforeRows.push([this, thisOrderingField]);
        }
      }
    });
    beforeRows.sort(function(a, b) {
      return a[1].value - b[1].value;
    });
    afterRows.sort(function(a, b) {
      return a[1].value - b[1].value;
    });
    var rows = [].concat(beforeRows);
    rows.push([row, row.querySelector(".field-ordering input")]);
    rows = rows.concat(afterRows);
    for (var i = 0; i < rows.length; ++i) {
      var row = rows[i];
      row[1].value = row[0].style.order = 10 * (1 + i);
    }
  }

  function hideInlinesFromOtherRegions() {
    var inlines = orderMachine.find(".inline-related:not(.empty-form)");
    inlines.addClass("content-editor-hidden");
    var shown = inlines.filter(
      '[data-region="' + ContentEditor.currentRegion + '"]'
    );
    machineEmptyMessage.addClass("hidden");
    if (shown.length) {
      shown.removeClass("content-editor-hidden");
    } else {
      machineEmptyMessage.removeClass("hidden");
    }
  }

  var pluginInlineGroups = (function selectPluginInlineGroups() {
    var selector = [];
    for (var i = 0; i < ContentEditor.plugins.length; i++) {
      selector.push("#" + ContentEditor.plugins[i].prefix + "-group");
    }
    return $(selector.join(", "));
  })();

  reorderInlines(pluginInlineGroups);
  pluginInlineGroups.hide();
  assignRegionDataAttribute();

  // Always move empty forms to the end, because new plugins are inserted
  // just before its empty form. Also, assign region data.
  $(document).on("formset:added", function newForm(event, row) {
    row.find(".field-region input").val(ContentEditor.currentRegion);
    row.attr("data-region", ContentEditor.currentRegion);

    setBiggestOrdering(row);
    attachMoveToRegionDropdown(row);
    ensureDraggable(row);

    machineEmptyMessage.addClass("hidden");

    $(document).trigger("content-editor:activate", [row]);
  });

  $(document).on("formset:removed", function resetInlines() {
    if (
      !orderMachine.find(
        '.inline-related[data-region="' + ContentEditor.currentRegion + '"]'
      ).length
    ) {
      machineEmptyMessage.removeClass("hidden");
    }
    orderMachine
      .find(".inline-related.last-related:not(.empty-form)")
      .each(function() {
        $(document).trigger("content-editor:deactivate", [$(this)]);
      });

    // As soon as possible, but not sooner (let the inline.js code run to the end first)
    setTimeout(function() {
      orderMachine
        .find(".inline-related.last-related:not(.empty-form)")
        .each(function() {
          $(document).trigger("content-editor:activate", [$(this)]);
        });
    }, 0);
  });

  // Initialize tabs and currentRegion.
  (function() {
    var tabContainer = $(".tabs.regions");
    for (var i = 0; i < ContentEditor.regions.length; i++) {
      var t = document.createElement("h2");
      t.textContent = ContentEditor.regions[i].title;
      t.setAttribute("data-region", ContentEditor.regions[i].key);
      tabContainer.append(t);
    }

    var tabs = tabContainer.find("h2"),
      tab;
    tabs.on("click", function() {
      ContentEditor.currentRegion = $(this).data("region");
      tabs
        .removeClass("active")
        .filter('[data-region="' + ContentEditor.currentRegion + '"]')
        .addClass("active");
      hideInlinesFromOtherRegions();
      window.location.hash = "tab_" + ContentEditor.currentRegion;

      // Make sure only allowed plugins are in select
      hideNotAllowedDropdown();
      hideNotAllowedPluginButtons();
    });

    // Restore tab if location hash matches.
    if (
      window.location.hash &&
      (tab = tabs.filter(
        '[data-region="' + window.location.hash.substr(5) + '"]'
      )) &&
      tab.length
    ) {
      tab.click();
    } else {
      tabs.eq(0).click();
    }

    tabContainer.find(".toggle").on("click", function() {
      $(".order-machine .inline-related").toggleClass("collapsed");
    });
  })();

  $(document)
    .on("content-editor:deactivate", function(event, row) {
      row.find("fieldset").addClass("content-editor-hidden");
    })
    .on("content-editor:activate", function(event, row) {
      row.find("fieldset").removeClass("content-editor-hidden");
    });

  // Hide fieldsets of to-be-deleted inlines.
  orderMachine.on(
    "click",
    ".delete>input[type=checkbox]",
    function toggleForDeletionClass() {
      $(this)
        .closest(".inline-related")
        [this.checked ? "addClass" : "removeClass"]("for-deletion");
    }
  );

  // Try to keep the current region tab (location hash).
  $("form").submit(function() {
    var form = $(this);
    form.attr("action", form.attr("action") + window.location.hash);
    return true;
  });

  // Cmd-S and Escape behavior.
  $(document).keydown(function handleKeys(event) {
    if (event.which == 83 && event.metaKey) {
      $(
        "form input[name=" + (event.shiftKey ? "_continue" : "_save") + "]"
      ).click();
      return false;
    } else if (event.which == 27) {
      // TODO cancel an ongoing drag.
      // orderMachine.sortable("cancel");
    }
  });
  (function buildPluginDropdown() {
    var select = buildDropdown(
      ContentEditor.plugins,
      ContentEditor.messages.createNew
    );
    select.addEventListener("change", function() {
      ContentEditor.addContent(select.value);
      select.value = "";
    });
    ContentEditor.machineControlSelect = select;
    hideNotAllowedDropdown();
    $(select)
      .appendTo(".machine-control")
      .wrap('

');
  })();

  $(document).trigger("content-editor:ready");
});
<|endoftext|>


<commit_before>var LightsOut = (function(lightsOut){
  lightsOut.States = lightsOut.States || {};

  lightsOut.States.Menu = {
    preload: function () {

    },

    create: function () {
      game.stage.backgroundColor = '#20343B';

      this.title = game.add.text(0, 0,
        'WASD to move, and E to interact', { font: '20px monospace', fill: "white" });
      this.title.anchor.set(0.5, 0.5);
      this.title.fixedToCamera = true;
      this.title.cameraOffset.setTo(this.game.camera.view.width / 2,
        this.game.camera.view.height / 2);

      this.subtitle = game.add.text(0, 0,
        'Press any key for New Game', { font: '18px monospace', fill: "white" });
      this.subtitle.anchor.set(0.5, 0.5);
      this.subtitle.fixedToCamera = true;
      this.subtitle.cameraOffset.setTo(this.game.camera.view.width / 2,
        this.game.camera.view.height / 2 + 80);

      game.input.keyboard.onDownCallback = function () {
        game.state.start('main', true, true, { mapName: "00.json" });
        game.input.keyboard.onDownCallback = null;
      };
    },

    update: function () { }
  };

  return lightsOut;
}(LightsOut || {}));<commit_msg>Change background color.<commit_after>var LightsOut = (function(lightsOut){
  lightsOut.States = lightsOut.States || {};

  lightsOut.States.Menu = {
    preload: function () {

    },

    create: function () {
      game.stage.backgroundColor = '#000000';

      this.title = game.add.text(0, 0,
        'WASD to move, and E to interact', { font: '20px monospace', fill: "white" });
      this.title.anchor.set(0.5, 0.5);
      this.title.fixedToCamera = true;
      this.title.cameraOffset.setTo(this.game.camera.view.width / 2,
        this.game.camera.view.height / 2);

      this.subtitle = game.add.text(0, 0,
        'Press any key for New Game', { font: '18px monospace', fill: "white" });
      this.subtitle.anchor.set(0.5, 0.5);
      this.subtitle.fixedToCamera = true;
      this.subtitle.cameraOffset.setTo(this.game.camera.view.width / 2,
        this.game.camera.view.height / 2 + 80);

      game.input.keyboard.onDownCallback = function () {
        game.state.start('main', true, true, { mapName: "00.json" });
        game.input.keyboard.onDownCallback = null;
      };
    },

    update: function () { }
  };

  return lightsOut;
}(LightsOut || {}));<|endoftext|>


<commit_before>const { Project } = require('imdone-api/lib/project')
const PluginManager = require('./plugin-manager')
const Repository = require('./repository')
const _cloneDeep = require('lodash.clonedeep')
const newCard = require('./card')
const sanitize = require("sanitize-filename")
const { JOURNAL_TYPE } = require('./constants')
const _path = require('path')
const moment = require('moment')
const { removeMD } = require('./adapters/markdown')
const fileGateway = require('./adapters/file-gateway')
const matter = require('gray-matter')
const _isObject = require('lodash.isobject')
const _isString = require('lodash.isstring')
const exec = require('child_process').exec

function calculateTotals (lists) {
  const totals = {}
  lists.forEach(list => {
    try {
      totals[list.name] = list.tasks.length
    } catch (e) {
      console.error('Error calculating list totals:', e)
    }
  })
  return totals
}

function onChange(project, event, data) {
  project.emit(event, data)
}

const EVENTS = [
  'file.processed',
  'files.found',
  'file.update',
  'file.saved',
  'list.modified',
  'config.update'
]

module.exports = class WorkerProject extends Project {
  constructor(repo) {
    super()
    this.repo = repo
    this.innerFilter = ''
    this.pluginManager = new PluginManager(this.repo)
    EVENTS.forEach(event => {
      this.repo.on(event, (data) => onChange(this, event, data))
    })
  }

  toJSON () {
    console.log('defaultFilter:', this.defaultViewFilter)
    console.time('totals time')
    let allLists = this.allLists
    const totals = calculateTotals(allLists)
    allLists = allLists.map(list => {
      list.tasks = list.tasks.map(card => {
        card.interpretedContent = ''
        card.totals = totals
        return card
      })
      return list
    })

    this.pluginManager.updateLists(allLists)
    const cards = allLists.map(list => list.tasks).flat().map(card => card.init(totals))
    const lists = this.getLists({
      tasks: this.getCards(this.filter, cards),
      populateFiltered: true
    })
    console.timeEnd('totals time')
    return {
      path: this.path,
      config: this.config,
      lists: lists,
      files: this.files,
      totals,
      filter: this.filter,
      actions: this.boardActions
    }
  }

  init (cb = () => {}) {
    this.repo.project = this
    this.repo.init((err, files) => {
      if (err) return cb(err)
      cb(null, files)
    })
  }

  emit () {}

  destroy () {
    this.repo.destroy()
    if (this.pluginManager) this.pluginManager.destroyPlugins()
  }

  get lists() {
    const cards = this.getCards()
    return this.getLists({
      tasks: this.getCards(this.filter, cards)
    })
  }

  get allLists() {
    return this.getLists()
  }

  getLists(opts) {
    const tasks = (opts && opts.tasks) || this.getCards()
    const populateFiltered = opts && opts.populateFiltered
    return Repository.getTasksByList(this.repo, tasks, false, populateFiltered)
  }

  getCards (filter = this.defaultViewFilter, tasks = this.repo.getTasks()) {
    return Repository.query(tasks, filter)
  }

  get files () {
    return this.repo.getFilePaths()
  }

  get config () {
    return this.repo.config
  }

  get defaultViewFilter () {
    return this.config.defaultFilter
  }

  get totals () {
    return calculateTotals(this.lists)
  }

  get path() {
    return this.repo.path;
  }

  get doneList() {
    return this.config.getDoneList()
  }

  get filter () {
    return this.innerFilter
  }

  get boardActions () {
    return [
      ...this.config.actions.map((item, index) => {
        return {...item, index}
      }),
      ...this.pluginManager.getBoardActions()
    ]
  }

  set filter (filter) {
    this.innerFilter = filter
  }

  addMetadata(task, key, value) {
    if (!task.allMeta[key]) task.allMeta[key] = []
    task.allMeta[key].push(value)
    if (!/^['"]/.test(value) && /\s/.test(value)) value = `"${value}"`
    const metaData = `${key}${this.repo.config.getMetaSep()}${value}`
    const content = task.addToLastCommentInContent(task.content, metaData, this.repo.config.isMetaNewLine())
    this.updateCardContent(task, content)
  }

  addTag(task, tag) {
    task.allTags.push(tag)
    const tagContent = `${this.repo.config.getTagPrefix()}${tag}`
    const content = task.addToLastCommentInContent(task.content, tagContent, this.repo.config.isMetaNewLine())
    this.updateCardContent(task, content)
  }

  updateCardContent(task, content) {
    this.repo.modifyTaskFromContent(task, content, err => {
      if (err) return console.error(err)
      this.emit('file.update')
    })
  }

  snackBar({ message, type, duration }) {
    this.emit('project.snackBar', { message, type, duration })
  }

  toast({ message, type, duration }) {
    this.emit('project.toast', { message, type, duration })
  }

  filterLists (filter, lists = this.lists) {
    return lists.map(list => {
      let newList = {...list, tasks:[]}
      newList = _cloneDeep(newList)
      newList.tasks = Repository.query(list.tasks, filter)
      return newList
    })
  }

  copyToClipboard(text, message) {
    this.emit('project.copyToClipboard', {text, message})
  }

  openUrl(url) {
    this.emit('project.openUrl', url)
  }

  openPath(path) {
    this.emit('project.openPath', path)
  }

  newCard(list, path) {
    if (path) {
      path = this.getFullPath(path)
      if (!fileGateway.existsSync(path)) {
        fileGateway.writeFileSync(path, '')
      }
    }
		const template = this.getNewCardTemplate(path);
		this.emit('project.newCard', { list, path, template });
	}

  getNewCardTemplate(file) {
		const frontMatter = this.getNewCardFileFrontMatter(file);
    const card = newCard(
      {frontMatter, repoId: this.path, text: ''},
      this,
      true
    )
    card.init(this.totals)
    return card.formatContent(frontMatter.template).content
	}

  getNewCardFileFrontMatter(file) {
		let fileContent;
		if (file) {
			fileContent = fileGateway.readFileSync(file, "utf8");
		} else if (JOURNAL_TYPE.NEW_FILE === this.config.journlType) {
			fileContent = this.config.journalTemplate
		} else {
			const newCardsFile = this.getNewCardsFile();
			fileContent = fileGateway.readFileSync(newCardsFile, "utf8");
		}
		let { props, computed, template } = this.config.settings.cards
		const frontMatter = matter(fileContent).data || {};
		if (!_isObject(props)) props = {};
		if (!_isObject(computed)) computed = {};
		if (!_isString(template)) template = "";
		if (!_isObject(frontMatter.props)) frontMatter.props = {};
		if (!_isObject(frontMatter.computed)) frontMatter.computed = {};
		if (!_isString(frontMatter.template)) frontMatter.template = template;
		props = {
			...props,
			...frontMatter.props,
			now: new Date().toDateString(),
			totals: this.totals,
		};
		computed = { ...computed, ...frontMatter.computed };
		return {
			...frontMatter,
			props,
			computed,
			template: frontMatter.template,
		};
	}

  getNewCardsFile(opts = {relPath: false}) {
    const { relPath, title } = opts
		if (!this.config) return;
		const filePath = this.appendNewCardsTo(title);
		if (fileGateway.existsSync(filePath))
			return relPath ? filePath.replace(this.path, "") : filePath;
		if (this.config.journalType !== JOURNAL_TYPE.NEW_FILE)
			this.snackBar({ message: `File not found: ${filePath}` });
	}

  appendNewCardsTo(title) {
		const journalType = this.config.journalType;
		if (journalType === JOURNAL_TYPE.SINGLE_FILE)
			return this.getFullPath(this.settings.appendNewCardsTo);
		if (journalType === JOURNAL_TYPE.FOLDER)
			return this.getJournalFile().fullFilePath;
		if (journalType === JOURNAL_TYPE.NEW_FILE) {
			let fileName = `${sanitize(removeMD(title))}.md`;
			if (this.config.replaceSpacesWith)
				fileName = fileName.replace(/ /g, this.config.replaceSpacesWith);
			const fileFolder = this.getFullPath(this.config.journalPath);
			const filePath = title
				? _path.join(fileFolder, fileName)
				: _path.join(fileFolder, "journal.md");

			if (!fileGateway.existsSync(fileFolder)) fileGateway.mkdirpSync(fileFolder);
			if (!fileGateway.existsSync(filePath)) fileGateway.writeFileSync(filePath, this.config.journalTemplate);

			return filePath;
		}
	}

  getJournalFile() {
		const month = moment().format("YYYY-MM")
		const today = moment().format("YYYY-MM-DD")
		const journalPath = this.config.journalPath
		const folderPath = _path.join(journalPath, month)
		const journalFilePrefix = this.config.journalFilePrefix
		const journalFileSuffix = this.config.journalFileSuffix
		const filePath = _path.join(
			folderPath,
			`${journalFilePrefix}${today}${journalFileSuffix}.md`
		);

		const fullFolderPath = this.getFullPath(folderPath);
		const fullFilePath = this.getFullPath(filePath);
		if (!fileGateway.existsSync(fullFolderPath)) fileGateway.mkdirpSync(fullFolderPath);
		if (!fileGateway.existsSync(fullFilePath)) {
			fileGateway.writeFileSync(fullFilePath, this.config.journalTemplate);
		}
		return { filePath, fullFilePath };
	}

  getFullPath(...path) {
    if (_path.join(...path).startsWith(this.path)) {
      return _path.join(...path)
    }
    return _path.join.apply({}, [this.path, ...path])
  }

  performCardAction(action, task) {
    task = this.repo.getTask(task.id)
    try {
      action = JSON.parse(action)
    } catch (e) {
      //
    }
    if (action.plugin) return this.pluginManager.performCardAction(action, task)

    const actionFunction = task.getCardLinks()[action.index].action
    const actions = {
			filter: (filter) => {
        this.emit('project.filter', { filter })
			},
			newCard: (list, path) => {
				if (task.source.lang !== "text") return this.alert("Unable to append cards in code files.");
				path = this.getFullPath(path);
				this.newCard(list, path);
			},
			alert: (msg) => {
				this.snackBar({message: msg})
			},
			openUrl: (url) => {
				this.openUrl(url)
			},
			execCommand: (cmd) => {
				return this.exec(cmd)
			},
		};
		const actionThis = {
			...task.data,
			...task.desc,
			actions,
		};
		try {
			const func = new Function(`return ${actionFunction}`)();
			func.apply(actionThis);
		} catch (e) {
			console.error(e);
			console.log("action:", actionFunction);
			console.log("this:", actionThis);
		}
	}

  exec(cmd) {
		return new Promise((resolve, reject) => {
			exec(cmd, (error, stdout, stderr) => {
        if (stderr) console.warn('cmd stderr:', stderr)
				if (error) return reject(error)
				resolve(stdout)
			})
		})
	}
}<commit_msg>Perform board actions<commit_after>const { Project } = require('imdone-api/lib/project')
const PluginManager = require('./plugin-manager')
const Repository = require('./repository')
const _cloneDeep = require('lodash.clonedeep')
const newCard = require('./card')
const sanitize = require("sanitize-filename")
const { JOURNAL_TYPE } = require('./constants')
const _path = require('path')
const moment = require('moment')
const { removeMD } = require('./adapters/markdown')
const fileGateway = require('./adapters/file-gateway')
const matter = require('gray-matter')
const _isObject = require('lodash.isobject')
const _isString = require('lodash.isstring')
const exec = require('child_process').exec

function calculateTotals (lists) {
  const totals = {}
  lists.forEach(list => {
    try {
      totals[list.name] = list.tasks.length
    } catch (e) {
      console.error('Error calculating list totals:', e)
    }
  })
  return totals
}

function onChange(project, event, data) {
  project.emit(event, data)
}

const EVENTS = [
  'file.processed',
  'files.found',
  'file.update',
  'file.saved',
  'list.modified',
  'config.update'
]

module.exports = class WorkerProject extends Project {
  constructor(repo) {
    super()
    this.repo = repo
    this.innerFilter = ''
    this.pluginManager = new PluginManager(this.repo)
    EVENTS.forEach(event => {
      this.repo.on(event, (data) => onChange(this, event, data))
    })
  }

  toJSON () {
    console.log('defaultFilter:', this.defaultViewFilter)
    console.time('totals time')
    let allLists = this.allLists
    const totals = calculateTotals(allLists)
    allLists = allLists.map(list => {
      list.tasks = list.tasks.map(card => {
        card.interpretedContent = ''
        card.totals = totals
        return card
      })
      return list
    })

    this.pluginManager.updateLists(allLists)
    const cards = allLists.map(list => list.tasks).flat().map(card => card.init(totals))
    const lists = this.getLists({
      tasks: this.getCards(this.filter, cards),
      populateFiltered: true
    })
    console.timeEnd('totals time')
    return {
      path: this.path,
      config: this.config,
      lists: lists,
      files: this.files,
      totals,
      filter: this.filter,
      actions: this.boardActions
    }
  }

  init (cb = () => {}) {
    this.repo.project = this
    this.repo.init((err, files) => {
      if (err) return cb(err)
      cb(null, files)
    })
  }

  emit () {}

  destroy () {
    this.repo.destroy()
    if (this.pluginManager) this.pluginManager.destroyPlugins()
  }

  get lists() {
    const cards = this.getCards()
    return this.getLists({
      tasks: this.getCards(this.filter, cards)
    })
  }

  get allLists() {
    return this.getLists()
  }

  getLists(opts) {
    const tasks = (opts && opts.tasks) || this.getCards()
    const populateFiltered = opts && opts.populateFiltered
    return Repository.getTasksByList(this.repo, tasks, false, populateFiltered)
  }

  getCards (filter = this.defaultViewFilter, tasks = this.repo.getTasks()) {
    return Repository.query(tasks, filter)
  }

  get files () {
    return this.repo.getFilePaths()
  }

  get config () {
    return this.repo.config
  }

  get defaultViewFilter () {
    return this.config.defaultFilter
  }

  get totals () {
    return calculateTotals(this.lists)
  }

  get path() {
    return this.repo.path;
  }

  get doneList() {
    return this.config.getDoneList()
  }

  get filter () {
    return this.innerFilter
  }

  get boardActions () {
    return [
      ...this.config.actions.map((item, index) => {
        return {...item, index}
      }),
      ...this.pluginManager.getBoardActions()
    ]
  }

  set filter (filter) {
    this.innerFilter = filter
  }

  addMetadata(task, key, value) {
    if (!task.allMeta[key]) task.allMeta[key] = []
    task.allMeta[key].push(value)
    if (!/^['"]/.test(value) && /\s/.test(value)) value = `"${value}"`
    const metaData = `${key}${this.repo.config.getMetaSep()}${value}`
    const content = task.addToLastCommentInContent(task.content, metaData, this.repo.config.isMetaNewLine())
    this.updateCardContent(task, content)
  }

  addTag(task, tag) {
    task.allTags.push(tag)
    const tagContent = `${this.repo.config.getTagPrefix()}${tag}`
    const content = task.addToLastCommentInContent(task.content, tagContent, this.repo.config.isMetaNewLine())
    this.updateCardContent(task, content)
  }

  updateCardContent(task, content) {
    this.repo.modifyTaskFromContent(task, content, err => {
      if (err) return console.error(err)
      this.emit('file.update')
    })
  }

  snackBar({ message, type, duration }) {
    this.emit('project.snackBar', { message, type, duration })
  }

  toast({ message, type, duration }) {
    this.emit('project.toast', { message, type, duration })
  }

  filterLists (filter, lists = this.lists) {
    return lists.map(list => {
      let newList = {...list, tasks:[]}
      newList = _cloneDeep(newList)
      newList.tasks = Repository.query(list.tasks, filter)
      return newList
    })
  }

  copyToClipboard(text, message) {
    this.emit('project.copyToClipboard', {text, message})
  }

  openUrl(url) {
    this.emit('project.openUrl', url)
  }

  openPath(path) {
    this.emit('project.openPath', path)
  }

  saveFile(content, file) {
    const filePath = this.getFullPath(file);
    this.emit('project.saveFile', {file: filePath, content})
  }

  newCard(list, path) {
    if (path) {
      path = this.getFullPath(path)
      if (!fileGateway.existsSync(path)) {
        fileGateway.writeFileSync(path, '')
      }
    }
		const template = this.getNewCardTemplate(path);
		this.emit('project.newCard', { list, path, template });
	}

  getNewCardTemplate(file) {
		const frontMatter = this.getNewCardFileFrontMatter(file);
    const card = newCard(
      {frontMatter, repoId: this.path, text: ''},
      this,
      true
    )
    card.init(this.totals)
    return card.formatContent(frontMatter.template).content
	}

  getNewCardFileFrontMatter(file) {
		let fileContent;
		if (file) {
			fileContent = fileGateway.readFileSync(file, "utf8");
		} else if (JOURNAL_TYPE.NEW_FILE === this.config.journlType) {
			fileContent = this.config.journalTemplate
		} else {
			const newCardsFile = this.getNewCardsFile();
			fileContent = fileGateway.readFileSync(newCardsFile, "utf8");
		}
		let { props, computed, template } = this.config.settings.cards
		const frontMatter = matter(fileContent).data || {};
		if (!_isObject(props)) props = {};
		if (!_isObject(computed)) computed = {};
		if (!_isString(template)) template = "";
		if (!_isObject(frontMatter.props)) frontMatter.props = {};
		if (!_isObject(frontMatter.computed)) frontMatter.computed = {};
		if (!_isString(frontMatter.template)) frontMatter.template = template;
		props = {
			...props,
			...frontMatter.props,
			now: new Date().toDateString(),
			totals: this.totals,
		};
		computed = { ...computed, ...frontMatter.computed };
		return {
			...frontMatter,
			props,
			computed,
			template: frontMatter.template,
		};
	}

  getNewCardsFile(opts = {relPath: false}) {
    const { relPath, title } = opts
		if (!this.config) return;
		const filePath = this.appendNewCardsTo(title);
		if (fileGateway.existsSync(filePath))
			return relPath ? filePath.replace(this.path, "") : filePath;
		if (this.config.journalType !== JOURNAL_TYPE.NEW_FILE)
			this.snackBar({ message: `File not found: ${filePath}` });
	}

  appendNewCardsTo(title) {
		const journalType = this.config.journalType;
		if (journalType === JOURNAL_TYPE.SINGLE_FILE)
			return this.getFullPath(this.settings.appendNewCardsTo);
		if (journalType === JOURNAL_TYPE.FOLDER)
			return this.getJournalFile().fullFilePath;
		if (journalType === JOURNAL_TYPE.NEW_FILE) {
			let fileName = `${sanitize(removeMD(title))}.md`;
			if (this.config.replaceSpacesWith)
				fileName = fileName.replace(/ /g, this.config.replaceSpacesWith);
			const fileFolder = this.getFullPath(this.config.journalPath);
			const filePath = title
				? _path.join(fileFolder, fileName)
				: _path.join(fileFolder, "journal.md");

			if (!fileGateway.existsSync(fileFolder)) fileGateway.mkdirpSync(fileFolder);
			if (!fileGateway.existsSync(filePath)) fileGateway.writeFileSync(filePath, this.config.journalTemplate);

			return filePath;
		}
	}

  getJournalFile() {
		const month = moment().format("YYYY-MM")
		const today = moment().format("YYYY-MM-DD")
		const journalPath = this.config.journalPath
		const folderPath = _path.join(journalPath, month)
		const journalFilePrefix = this.config.journalFilePrefix
		const journalFileSuffix = this.config.journalFileSuffix
		const filePath = _path.join(
			folderPath,
			`${journalFilePrefix}${today}${journalFileSuffix}.md`
		);

		const fullFolderPath = this.getFullPath(folderPath);
		const fullFilePath = this.getFullPath(filePath);
		if (!fileGateway.existsSync(fullFolderPath)) fileGateway.mkdirpSync(fullFolderPath);
		if (!fileGateway.existsSync(fullFilePath)) {
			fileGateway.writeFileSync(fullFilePath, this.config.journalTemplate);
		}
		return { filePath, fullFilePath };
	}

  getFullPath(...path) {
    if (_path.join(...path).startsWith(this.path)) {
      return _path.join(...path)
    }
    return _path.join.apply({}, [this.path, ...path])
  }

  performCardAction(action, task) {
    task = this.repo.getTask(task.id)
    try {
      action = JSON.parse(action)
    } catch (e) {
      //
    }
    if (action.plugin) return this.pluginManager.performCardAction(action, task)

    const actionFunction = task.getCardLinks()[action.index].action
    const actions = {
			filter: (filter) => {
        this.emit('project.filter', { filter })
			},
			newCard: (list, path) => {
				if (task.source.lang !== "text") return this.alert("Unable to append cards in code files.");
				path = this.getFullPath(path);
				this.newCard(list, path);
			},
			alert: (msg) => {
				this.toast({message: msg})
			},
			openUrl: (url) => {
				this.openUrl(url)
			},
			execCommand: (cmd) => {
				return this.exec(cmd)
			},
		};
		const actionThis = {
			...task.data,
			...task.desc,
			actions,
		};
		try {
			const func = new Function(`return ${actionFunction}`)();
			func.apply(actionThis);
		} catch (e) {
			console.error(e);
			console.log("action:", actionFunction);
			console.log("this:", actionThis);
		}
	}
  performBoardAction(action) {
    if (action && action.plugin) return this.pluginManager.performBoardAction(action)
    const actions = {
			alert: (msg) => {
				this.toast({message: msg})
			},
			saveFile: ({ file, content }) => {
        this.saveFile(content, file)
      },
			mailto: ({ subject, body, to, cc, bcc }) => {
				const params = [];
				if (subject) params.push(`subject=${encodeURIComponent(subject)}`);
				if (body) params.push(`body=${encodeURIComponent(body)}`);
				if (cc) params.push(`cc=${encodeURIComponent(cc)}`);
				if (bcc) params.push(`bcc=${encodeURIComponent(bcc)}`);
				const url = `mailto:${to}?${params.join("&")}`;
				console.log("opening email with:", url);
				this.openUrl(url)
			},
			copy: (content, message) => {
        this.copyToClipboard(
					content,
					message || "Your content has been copied"
				)
			},
			updateCard: (task, content) => {
				this.updateCardContent(task, content);
			},
		};
    const actionFunction = this.boardActions[action.index].function
    const actionThis = { cards: this.lists, ...actions }
    try {
      actionFunction.apply(actionThis);
    } catch (err) {
      console.error(err);
      console.log("action:", actionFunction);
      console.log("this:", actionThis);
    }
	}

  exec(cmd) {
		return new Promise((resolve, reject) => {
			exec(cmd, (error, stdout, stderr) => {
        if (stderr) console.warn('cmd stderr:', stderr)
				if (error) return reject(error)
				resolve(stdout)
			})
		})
	}
}<|endoftext|>


<commit_before>/**
 * Library methods for generating stubs
 *
 * @author jaker
 */

'use strict';

var expect = require('gulp-expect-file');
var extend = require('gulp-extend');
var fs = require('fs');
var http = require('http');
var merge = require('merge-stream');
var path = require('path');
var rename = require('gulp-rename');
var spawnSeries = require('spawn-series');
var template = require('gulp-template');
var inquirer = require('inquirer');
var through = require('through2').obj;
var prettyTime = require('pretty-hrtime');
var is = require('is_js');
var gulpif = require('gulp-if');

module.exports = function (gulp, gutil) {

  function logFile() {
    function decorate(color, text) {
      return text ? '[' + gutil.colors[color](text) + ']' : '';
    }

    var stream = through(function (file, enc, callback) {
      var items = [];

      items.push(gutil.colors.cyan(file.path));

      if (file.isDirectory()) {
        items.push(decorate('magenta', 'DIR'));
      } else if (file.isNull()) {
        items.push(decorate('magenta', 'EMPTY'));
      }

      gutil.log(items.join(' '));

      this.push(file);
      return callback();
    });
    stream.resume();
    return stream;
  }

  function validate(value, regex, allowEmpty) {
    if (value === '') {
      if (allowEmpty) {
        return true;
      } else {
        return 'Please provide a valid value';
      }
    }
    var cases = value.match(regex);

    if (cases === null) {
      return 'Invalid value, please try again.';
    } else {
      return true;
    }
  }

  function npmLinkAndInstall(cwd, callback) {
    /**
      * Attempt to npm link global packages.
      * This may fail because of permissions, if it fails packages
      * will be installed locally using npm install
      */
    spawnSeries(
      [
        {
          command: 'npm',
          args: ['link', require('../package.json').name],
          options: {
            cwd: cwd,
            ignoreError: true,
            stdio: 'inherit'
          }
        },
        {
          command: 'npm',
          args: ['link', 'gulp'],
          options: {
            cwd: cwd,
            ignoreError: true,
            stdio: 'inherit'
          }
        },
        {
          command: 'npm',
          args: ['install'],
          options: {
            cwd: cwd,
            stdio: 'inherit'
          }
        }
      ],
      callback,
      function (child, i, cmdObj) {
        gutil.log(gutil.colors.blue('Starting: ' + cmdObj.command + ' ' + cmdObj.args.join(' ')));
        child.on('close', function (code) {
          if (code === 0) {
            gutil.log(gutil.colors.blue('Finished: ' + cmdObj.command + ' ' + cmdObj.args.join(' ')));
          } else if (cmdObj.options.ignoreError) {
            gutil.log(gutil.colors.red('Failed: ' + cmdObj.command + ' ' + cmdObj.args.join(' ')));
            gutil.log(gutil.colors.blue('Ignoring error and continuing (Package will be installed in npm install) ..'));
          }
        });
      }
    );
  }

  function parseVersionPage(page) {
    var start = page.lastIndexOf('');
    var end = page.lastIndexOf('');
    var body = page.substring(start + 6, end);
    var versionArr = body.split(' ');
    var revision;

    if (versionArr.length > 2) {
      revision = versionArr[2].replace(')', '');
    }

    return {
      version: versionArr[0],
      branch: versionArr[1].replace('(', ''),
      revision: revision
    };
  }

  function bowerInstall(cwd, callback) {
    spawnSeries(
        [
          {
            command: 'bower',
            args: ['install'],
            options: {
              cwd: cwd,
              stdio: 'inherit'
            }
          }
        ],
        callback,
        function (child, i, cmdObj) {
          gutil.log(gutil.colors.blue('Starting: ' + cmdObj.command + ' ' + cmdObj.args.join(' ')));
          child.on('close', function () {
            gutil.log(gutil.colors.blue('Finished: ' + cmdObj.command + ' ' + cmdObj.args.join(' ')));
          });
        }
    );
  }

  return {

    scaffold: function () {
      var ops = {
        serverConf: {}
      };

      inquirer.prompt([
        {
          type: 'input',
          name: 'name',
          message: 'What is your repository name?\nOnly lower case characters, numbers or hyphens (-) are allowed.',
          validate: function (val) {
            return validate(val, /^[a-z0-9\-]+$/);
          }
        },
        {
          type: 'confirm',
          name: 'continue',
          message: 'Do you want to continue?',
          when: function (repo) {
            if (fs.existsSync('./' + repo.name)) {
              console.log(gutil.colors.red(gutil.colors.bold(repo.name) + ' already exists in ' + process.cwd() + '.' +
                '\nTo update the project please "li update" from root directory of the project.' +
                '\nTo recreate the project please delete the existing project and then re-run "li create"'));
              process.exit(1);
              return true;
            } else {
              return false;
            }
          },
          default: true
        },
        {
          type: 'input',
          name: 'serverUrl',
          message: function () {
            return 'What is the URL (include port if exists) to the dev/stage site that you will connect to ' +
              '(beginning with "http")?';
          },
          validate: function (val) {
            return validate(val, /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_\+.~#?&\/\/=]*)/);
          }
        },
        {
          type: 'input',
          name: 'pluginToken',
          message: function () {
            return 'Do you have a plugin token (if not press enter)?';
          },
          validate: function (val) {
            return validate(val, /^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$/, true);
          },
          default: function () {
            return '';
          }
        }
      ], function (answers) {

        // set up vars for server.conf.json
        ops.serverConf.serverUrl = answers.serverUrl;

        if (answers.pluginToken) {
          ops.serverConf.pluginToken = answers.pluginToken;
        }

        //default plugin upload protocol
        ops.serverConf.pluginUploadProtocol = 'https';

        //default studio overrides allowed
        ops.serverConf.allowStudioOverrides = true;

        //default strict mode false
        ops.serverConf.strictMode = false;

        //default dry run mode false
        ops.serverConf.dryRun = false;

        //default verbose false
        ops.serverConf.verbose = false;

        //default force false
        ops.serverConf.force = false;

        //default pluginPoints
        ops.serverConf.pluginPoints = [];

        ops.name = answers.name;
        ops.description = 'lithium-sdk project: ' + answers.name + '\nGenerated by ' + process.env.USER;

        var templateConstraint = function(file) {
          return path.basename(file.path) !== 'README.md';
        };

        // copy and process templates
        gulp.src(path.join(__dirname, '../templates/project/**/*'))
          .pipe(gulpif(templateConstraint, template(ops)))
          .pipe(rename(function (path) {
            path.basename = path.basename.replace('_', '');
          }))
          .pipe(gulp.dest('./' + answers.name))
          .pipe(logFile())
          .on('end', function () {
            // once templates are finished, create server.conf.json
            fs.writeFile('./' + answers.name + '/server.conf.json', JSON.stringify(ops.serverConf, null, 2),
              function (error) {
                if (error) {
                  throw error;
                } else {
                  gutil.log(gutil.colors.cyan(path.resolve('./' + answers.name + '/server.conf.json')));

                  // npm link and install
                  npmLinkAndInstall('./' + answers.name, function (code) {
                    if (code === 0) {
                      gutil.log(gutil.colors.green('Created ' + answers.name));
                    } else {
                      gutil.log(gutil.colors.red('Error while creating ' + answers.name));
                    }
                  });
                }
              });
          });
      });
    },

    update: function (cb) {
      var ops = {
        serverConf: {}
      };

      try {
        ops.serverConf = require(process.cwd() + '/server.conf.json');
      } catch (err) {
        console.log('Your project  may be corrupt.\nPlease delete try recreating the project using "li create"');
        process.exit(1);
      }

      inquirer.prompt([
        {
          type: 'input',
          name: 'serverUrl',
          message: function () {
            return 'Enter the new URL (include port if exists) to the dev/stage site that you will connect to (beginning with "http") or press enter to use existing';
          },
          validate: function (val) {
            return validate(val, /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_\+.~#?&\/\/=]*)/);
          },
          default: function () {
            return ops.serverConf.serverUrl;
          }
        },
        {
          type: 'input',
          name: 'pluginToken',
          message: function () {
            if (ops.serverConf.pluginToken) {
              return 'Enter plugin token to change it or press enter to continue';
            } else {
              return 'Do you have a plugin token (if not press enter)?';
            }
          },
          validate: function (val) {
            return validate(val, /^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$/, true);
          },
          default: function () {
            return ops.serverConf.pluginToken || '';
          }
        }
      ], function (answers) {

        // set up vars for server.conf.json
        ops.serverConf.serverUrl = answers.serverUrl;

        if (answers.pluginToken) {
          ops.serverConf.pluginToken = answers.pluginToken;
        }

        answers.name = process.cwd().split('/').pop();
        ops.name = answers.name;
        ops.description = 'lithium-sdk project: ' + answers.name + '\nGenerated by ' + process.env.USER;

        console.log("are we here??");

        // copy and process templates
        gulp.src([path.join(__dirname, '../templates/project/**/*'),
            '!' + path.join(__dirname, '../templates/project/sdk.conf.json')])
          .pipe(template(ops))
          .pipe(rename(function (path) {
            path.basename = path.basename.replace('_', '');
          }))
          .pipe(gulp.dest('.'))
          .pipe(logFile())
          .on('end', function () {
            // once templates are finished, create server.conf.json
            fs.writeFile('./server.conf.json', JSON.stringify(ops.serverConf, null, 2),
              function (error) {
                if (error) {
                  cb(error);
                } else {
                  gutil.log(gutil.colors.cyan(path.resolve('./server.conf.json')));

                  // npm link and install
                  npmLinkAndInstall('./', function (code) {
                    if (code === 0) {
                      gutil.log(gutil.colors.green('Project updated!'));
                      cb();
                    } else {
                      gutil.log(gutil.colors.red('Error while updating project.'));
                      cb(error);
                    }
                  });
                }
            });
          });

        // copy README files
      });
    },

    // before we enable angular, we need to make sure they have a few things
    verify: function () {
      var files = ['sdk.conf.json', 'server.conf.json'];
      return gulp.src(files)
        .pipe(expect({ checkRealFile: true,  errorOnFailure: true }, files))
        .on('end', function () {
          var sdkConf = require(path.join(process.cwd(), 'sdk.conf.json'));
          if (!is.undefined(sdkConf.ng)) {
            gutil.log(gutil.colors.red('Angularjs support is already enabled on this project.'));
            process.exit(1);
          }
        })
        .on('error', function () {
          gutil.log(gutil.colors.red('File check failed.'));
          process.exit(1);
        });
    },

    enableAngular: function (cb) {
      // get version
      var serverUrl = require(path.join(process.cwd(), 'server.conf.json')).serverUrl;
      var statusPath = '/t5/status/versionpage';

      var request = http.get(serverUrl + statusPath);
      var reqCallStarted = setInterval(function () {
        gutil.log(gutil.colors.cyan('Waiting for response from ' + serverUrl + ' ...'));
      }, 3000);

      var reqCallStartTime = process.hrtime();
      gutil.log(gutil.colors.cyan('Starting call to ' + serverUrl + ' ...'));

      request.on('response', function (response) {
        clearInterval(reqCallStarted);

        if (response.statusCode === 200) {
          var serverCallTime = prettyTime(process.hrtime(reqCallStartTime));
          gutil.log(gutil.colors.cyan('Finished call to ' + serverUrl + ' in ' + gutil.colors.magenta(serverCallTime)));

          response.setEncoding('utf8');
          response.on('data', function (chunk) {

            var liaBranch = parseVersionPage(chunk);
            var custName = require(path.join(process.cwd(), 'package.json')).name;

            gutil.log(gutil.colors.cyan('Lia version ' + gutil.colors.magenta(liaBranch.branch)));

            if (liaBranch.branch.indexOf('release') > -1) {
              liaBranch = 'release/' + liaBranch.version;
            } else { // active branch
              liaBranch = 'develop';
            }

            var ops = {
              name: custName,
              branch: liaBranch
            };

            // now that we have template variables, run gulp.src
            merge(
              // merge sdk.conf.json
              gulp.src(['./sdk.conf.json',
                    path.join(__dirname, '../templates/project-ng/sdk.conf.json')])
                .pipe(extend('sdk.conf.json'))
                .pipe(gulp.dest('./'))
                .pipe(logFile()),
              // copy over everything else
              gulp.src([path.join(__dirname, '../templates/project-ng/**/*'),
                        '!' + path.join(__dirname, '../templates/project-ng/sdk.conf.json')])
                .pipe(template(ops))
                .pipe(rename(function (path) {
                  path.basename = path.basename.replace('_', '');
                }))
                .pipe(gulp.dest('./'))
                .pipe(logFile())
            ).on('end', function () {
              bowerInstall('./', function (code) {
                if (code === 0) {
                  gutil.log(gutil.colors.green('Finished! You may now use angular in your sdk environment.'));
                  cb();
                } else {
                  gutil.log(gutil.colors.red('Bower install failed'));
                  cb(new Error('Bower install failed'));
                }
              });
            });
          });
        } else {
          gutil.log(gutil.colors.red('Call to ' + serverUrl + ' failed. Response status code: ' + response.statusCode));
          cb(new Error('Call to lia failed.'));
        }
      });
    }
  };
};
<commit_msg>LIA-43389 don't pass README.md files in templates/project directories through gulp-template (apply same fix as before to li update-project script)<commit_after>/**
 * Library methods for generating stubs
 *
 * @author jaker
 */

'use strict';

var expect = require('gulp-expect-file');
var extend = require('gulp-extend');
var fs = require('fs');
var http = require('http');
var merge = require('merge-stream');
var path = require('path');
var rename = require('gulp-rename');
var spawnSeries = require('spawn-series');
var template = require('gulp-template');
var inquirer = require('inquirer');
var through = require('through2').obj;
var prettyTime = require('pretty-hrtime');
var is = require('is_js');
var gulpif = require('gulp-if');

module.exports = function (gulp, gutil) {

  function logFile() {
    function decorate(color, text) {
      return text ? '[' + gutil.colors[color](text) + ']' : '';
    }

    var stream = through(function (file, enc, callback) {
      var items = [];

      items.push(gutil.colors.cyan(file.path));

      if (file.isDirectory()) {
        items.push(decorate('magenta', 'DIR'));
      } else if (file.isNull()) {
        items.push(decorate('magenta', 'EMPTY'));
      }

      gutil.log(items.join(' '));

      this.push(file);
      return callback();
    });
    stream.resume();
    return stream;
  }

  function validate(value, regex, allowEmpty) {
    if (value === '') {
      if (allowEmpty) {
        return true;
      } else {
        return 'Please provide a valid value';
      }
    }
    var cases = value.match(regex);

    if (cases === null) {
      return 'Invalid value, please try again.';
    } else {
      return true;
    }
  }

  function npmLinkAndInstall(cwd, callback) {
    /**
      * Attempt to npm link global packages.
      * This may fail because of permissions, if it fails packages
      * will be installed locally using npm install
      */
    spawnSeries(
      [
        {
          command: 'npm',
          args: ['link', require('../package.json').name],
          options: {
            cwd: cwd,
            ignoreError: true,
            stdio: 'inherit'
          }
        },
        {
          command: 'npm',
          args: ['link', 'gulp'],
          options: {
            cwd: cwd,
            ignoreError: true,
            stdio: 'inherit'
          }
        },
        {
          command: 'npm',
          args: ['install'],
          options: {
            cwd: cwd,
            stdio: 'inherit'
          }
        }
      ],
      callback,
      function (child, i, cmdObj) {
        gutil.log(gutil.colors.blue('Starting: ' + cmdObj.command + ' ' + cmdObj.args.join(' ')));
        child.on('close', function (code) {
          if (code === 0) {
            gutil.log(gutil.colors.blue('Finished: ' + cmdObj.command + ' ' + cmdObj.args.join(' ')));
          } else if (cmdObj.options.ignoreError) {
            gutil.log(gutil.colors.red('Failed: ' + cmdObj.command + ' ' + cmdObj.args.join(' ')));
            gutil.log(gutil.colors.blue('Ignoring error and continuing (Package will be installed in npm install) ..'));
          }
        });
      }
    );
  }

  function parseVersionPage(page) {
    var start = page.lastIndexOf('');
    var end = page.lastIndexOf('');
    var body = page.substring(start + 6, end);
    var versionArr = body.split(' ');
    var revision;

    if (versionArr.length > 2) {
      revision = versionArr[2].replace(')', '');
    }

    return {
      version: versionArr[0],
      branch: versionArr[1].replace('(', ''),
      revision: revision
    };
  }

  function bowerInstall(cwd, callback) {
    spawnSeries(
        [
          {
            command: 'bower',
            args: ['install'],
            options: {
              cwd: cwd,
              stdio: 'inherit'
            }
          }
        ],
        callback,
        function (child, i, cmdObj) {
          gutil.log(gutil.colors.blue('Starting: ' + cmdObj.command + ' ' + cmdObj.args.join(' ')));
          child.on('close', function () {
            gutil.log(gutil.colors.blue('Finished: ' + cmdObj.command + ' ' + cmdObj.args.join(' ')));
          });
        }
    );
  }

    var templateConstraint = function(file) {
        return path.basename(file.path) !== 'README.md';
    };

  return {

    scaffold: function () {
      var ops = {
        serverConf: {}
      };

      inquirer.prompt([
        {
          type: 'input',
          name: 'name',
          message: 'What is your repository name?\nOnly lower case characters, numbers or hyphens (-) are allowed.',
          validate: function (val) {
            return validate(val, /^[a-z0-9\-]+$/);
          }
        },
        {
          type: 'confirm',
          name: 'continue',
          message: 'Do you want to continue?',
          when: function (repo) {
            if (fs.existsSync('./' + repo.name)) {
              console.log(gutil.colors.red(gutil.colors.bold(repo.name) + ' already exists in ' + process.cwd() + '.' +
                '\nTo update the project please "li update" from root directory of the project.' +
                '\nTo recreate the project please delete the existing project and then re-run "li create"'));
              process.exit(1);
              return true;
            } else {
              return false;
            }
          },
          default: true
        },
        {
          type: 'input',
          name: 'serverUrl',
          message: function () {
            return 'What is the URL (include port if exists) to the dev/stage site that you will connect to ' +
              '(beginning with "http")?';
          },
          validate: function (val) {
            return validate(val, /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_\+.~#?&\/\/=]*)/);
          }
        },
        {
          type: 'input',
          name: 'pluginToken',
          message: function () {
            return 'Do you have a plugin token (if not press enter)?';
          },
          validate: function (val) {
            return validate(val, /^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$/, true);
          },
          default: function () {
            return '';
          }
        }
      ], function (answers) {

        // set up vars for server.conf.json
        ops.serverConf.serverUrl = answers.serverUrl;

        if (answers.pluginToken) {
          ops.serverConf.pluginToken = answers.pluginToken;
        }

        //default plugin upload protocol
        ops.serverConf.pluginUploadProtocol = 'https';

        //default studio overrides allowed
        ops.serverConf.allowStudioOverrides = true;

        //default strict mode false
        ops.serverConf.strictMode = false;

        //default dry run mode false
        ops.serverConf.dryRun = false;

        //default verbose false
        ops.serverConf.verbose = false;

        //default force false
        ops.serverConf.force = false;

        //default pluginPoints
        ops.serverConf.pluginPoints = [];

        ops.name = answers.name;
        ops.description = 'lithium-sdk project: ' + answers.name + '\nGenerated by ' + process.env.USER;

        // copy and process templates
        gulp.src(path.join(__dirname, '../templates/project/**/*'))
          .pipe(gulpif(templateConstraint, template(ops)))
          .pipe(rename(function (path) {
            path.basename = path.basename.replace('_', '');
          }))
          .pipe(gulp.dest('./' + answers.name))
          .pipe(logFile())
          .on('end', function () {
            // once templates are finished, create server.conf.json
            fs.writeFile('./' + answers.name + '/server.conf.json', JSON.stringify(ops.serverConf, null, 2),
              function (error) {
                if (error) {
                  throw error;
                } else {
                  gutil.log(gutil.colors.cyan(path.resolve('./' + answers.name + '/server.conf.json')));

                  // npm link and install
                  npmLinkAndInstall('./' + answers.name, function (code) {
                    if (code === 0) {
                      gutil.log(gutil.colors.green('Created ' + answers.name));
                    } else {
                      gutil.log(gutil.colors.red('Error while creating ' + answers.name));
                    }
                  });
                }
              });
          });
      });
    },

    update: function (cb) {
      var ops = {
        serverConf: {}
      };

      try {
        ops.serverConf = require(process.cwd() + '/server.conf.json');
      } catch (err) {
        console.log('Your project  may be corrupt.\nPlease delete try recreating the project using "li create"');
        process.exit(1);
      }

      inquirer.prompt([
        {
          type: 'input',
          name: 'serverUrl',
          message: function () {
            return 'Enter the new URL (include port if exists) to the dev/stage site that you will connect to (beginning with "http") or press enter to use existing';
          },
          validate: function (val) {
            return validate(val, /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_\+.~#?&\/\/=]*)/);
          },
          default: function () {
            return ops.serverConf.serverUrl;
          }
        },
        {
          type: 'input',
          name: 'pluginToken',
          message: function () {
            if (ops.serverConf.pluginToken) {
              return 'Enter plugin token to change it or press enter to continue';
            } else {
              return 'Do you have a plugin token (if not press enter)?';
            }
          },
          validate: function (val) {
            return validate(val, /^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$/, true);
          },
          default: function () {
            return ops.serverConf.pluginToken || '';
          }
        }
      ], function (answers) {

        // set up vars for server.conf.json
        ops.serverConf.serverUrl = answers.serverUrl;

        if (answers.pluginToken) {
          ops.serverConf.pluginToken = answers.pluginToken;
        }

        answers.name = process.cwd().split('/').pop();
        ops.name = answers.name;
        ops.description = 'lithium-sdk project: ' + answers.name + '\nGenerated by ' + process.env.USER;

        console.log("are we here??");

        // copy and process templates
        gulp.src([path.join(__dirname, '../templates/project/**/*'),
            '!' + path.join(__dirname, '../templates/project/sdk.conf.json')])
          .pipe(gulpif(templateConstraint, template(ops)))
          .pipe(rename(function (path) {
            path.basename = path.basename.replace('_', '');
          }))
          .pipe(gulp.dest('.'))
          .pipe(logFile())
          .on('end', function () {
            // once templates are finished, create server.conf.json
            fs.writeFile('./server.conf.json', JSON.stringify(ops.serverConf, null, 2),
              function (error) {
                if (error) {
                  cb(error);
                } else {
                  gutil.log(gutil.colors.cyan(path.resolve('./server.conf.json')));

                  // npm link and install
                  npmLinkAndInstall('./', function (code) {
                    if (code === 0) {
                      gutil.log(gutil.colors.green('Project updated!'));
                      cb();
                    } else {
                      gutil.log(gutil.colors.red('Error while updating project.'));
                      cb(error);
                    }
                  });
                }
            });
          });

        // copy README files
      });
    },

    // before we enable angular, we need to make sure they have a few things
    verify: function () {
      var files = ['sdk.conf.json', 'server.conf.json'];
      return gulp.src(files)
        .pipe(expect({ checkRealFile: true,  errorOnFailure: true }, files))
        .on('end', function () {
          var sdkConf = require(path.join(process.cwd(), 'sdk.conf.json'));
          if (!is.undefined(sdkConf.ng)) {
            gutil.log(gutil.colors.red('Angularjs support is already enabled on this project.'));
            process.exit(1);
          }
        })
        .on('error', function () {
          gutil.log(gutil.colors.red('File check failed.'));
          process.exit(1);
        });
    },

    enableAngular: function (cb) {
      // get version
      var serverUrl = require(path.join(process.cwd(), 'server.conf.json')).serverUrl;
      var statusPath = '/t5/status/versionpage';

      var request = http.get(serverUrl + statusPath);
      var reqCallStarted = setInterval(function () {
        gutil.log(gutil.colors.cyan('Waiting for response from ' + serverUrl + ' ...'));
      }, 3000);

      var reqCallStartTime = process.hrtime();
      gutil.log(gutil.colors.cyan('Starting call to ' + serverUrl + ' ...'));

      request.on('response', function (response) {
        clearInterval(reqCallStarted);

        if (response.statusCode === 200) {
          var serverCallTime = prettyTime(process.hrtime(reqCallStartTime));
          gutil.log(gutil.colors.cyan('Finished call to ' + serverUrl + ' in ' + gutil.colors.magenta(serverCallTime)));

          response.setEncoding('utf8');
          response.on('data', function (chunk) {

            var liaBranch = parseVersionPage(chunk);
            var custName = require(path.join(process.cwd(), 'package.json')).name;

            gutil.log(gutil.colors.cyan('Lia version ' + gutil.colors.magenta(liaBranch.branch)));

            if (liaBranch.branch.indexOf('release') > -1) {
              liaBranch = 'release/' + liaBranch.version;
            } else { // active branch
              liaBranch = 'develop';
            }

            var ops = {
              name: custName,
              branch: liaBranch
            };

            // now that we have template variables, run gulp.src
            merge(
              // merge sdk.conf.json
              gulp.src(['./sdk.conf.json',
                    path.join(__dirname, '../templates/project-ng/sdk.conf.json')])
                .pipe(extend('sdk.conf.json'))
                .pipe(gulp.dest('./'))
                .pipe(logFile()),
              // copy over everything else
              gulp.src([path.join(__dirname, '../templates/project-ng/**/*'),
                        '!' + path.join(__dirname, '../templates/project-ng/sdk.conf.json')])
                .pipe(template(ops))
                .pipe(rename(function (path) {
                  path.basename = path.basename.replace('_', '');
                }))
                .pipe(gulp.dest('./'))
                .pipe(logFile())
            ).on('end', function () {
              bowerInstall('./', function (code) {
                if (code === 0) {
                  gutil.log(gutil.colors.green('Finished! You may now use angular in your sdk environment.'));
                  cb();
                } else {
                  gutil.log(gutil.colors.red('Bower install failed'));
                  cb(new Error('Bower install failed'));
                }
              });
            });
          });
        } else {
          gutil.log(gutil.colors.red('Call to ' + serverUrl + ' failed. Response status code: ' + response.statusCode));
          cb(new Error('Call to lia failed.'));
        }
      });
    }
  };
};
<|endoftext|>