<commit_before>/**
 * Taking care of events
 *  - Simulating 'change' event on contentEditable element
 *  - Handling drag & drop logic
 *  - Catch paste events
 *  - Dispatch proprietary newword:composer event
 *  - Keyboard shortcuts
 */
(function(wysihtml5) {
  var dom       = wysihtml5.dom,
      browser   = wysihtml5.browser,
      /**
       * Map keyCodes to query commands
       */
      shortcuts = {
        "66": "bold",     // B
        "73": "italic",   // I
        "85": "underline" // U
      };

  // Adds multiple eventlisteners to target, bound to one callback
  // TODO: If needed elsewhere make it part of wysihtml5.dom or sth
  var addListeners = function (target, events, callback) {
    for(var i = 0, max = events.length; i < max; i++) {
      target.addEventListener(events[i], callback, false);
    }
  };

  // Removes multiple eventlisteners from target, bound to one callback
  // TODO: If needed elsewhere make it part of wysihtml5.dom or sth
  var removeListeners = function (target, events, callback) {
    for(var i = 0, max = events.length; i < max; i++) {
      target.removeEventListener(events[i], callback, false);
    }
  };

  // Override for giving user ability to delete last line break in table cell
  var fixLastBrDeletionInTable = function(composer, force) {
    if (composer.selection.caretIsLastInSelection()) {
      var sel = composer.selection.getSelection(),
          aNode = sel.anchorNode;
      if (aNode && aNode.nodeType === 1 && (wysihtml5.dom.getParentElement(aNode, {query: 'td, th'}, false, composer.element) || force)) {
        var nextNode = aNode.childNodes[sel.anchorOffset];
        if (nextNode && nextNode.nodeType === 1 & nextNode.nodeName === "BR") {
          nextNode.parentNode.removeChild(nextNode);
          return true;
        }
      }
    }
    return false;
  };

  // If found an uneditable before caret then notify it before deletion
  var handleUneditableDeletion = function(composer) {
    var before = composer.selection.getBeforeSelection(true);
    if (before && (before.type === "element" || before.type === "leafnode") && before.node.nodeType === 1 && before.node.classList.contains(composer.config.classNames.uneditableContainer)) {
      if (fixLastBrDeletionInTable(composer, true)) {
        return true;
      }
      try {
        var ev = new CustomEvent("wysihtml5:uneditable:delete");
        before.node.dispatchEvent(ev);
      } catch (err) {}
      before.node.parentNode.removeChild(before.node);
      return true;
    }
    return false;
  };

  // Deletion with caret in the beginning of headings needs special attention
  // Heading does not concate text to previous block node correctly (browsers do unexpected miracles here especially webkit)
  var fixDeleteInTheBeginnigOfHeading = function(composer) {
    var selection = composer.selection,
        prevNode = selection.getPreviousNode();

    if (selection.caretIsFirstInSelection() &&
        prevNode &&
        prevNode.nodeType === 1 &&
        (/block/).test(composer.win.getComputedStyle(prevNode).display)
    ) {
      if ((/^\s*$/).test(prevNode.textContent || prevNode.innerText)) {
        // If heading is empty remove the heading node
        prevNode.parentNode.removeChild(prevNode);
        return true;
      } else {
        if (prevNode.lastChild) {
          var selNode = prevNode.lastChild,
              selectedNode = selection.getSelectedNode(),
              commonAncestorNode = wysihtml5.dom.domNode(prevNode).commonAncestor(selectedNode, composer.element);
              curNode = commonAncestorNode ? wysihtml5.dom.getParentElement(selectedNode, {
                query: "h1, h2, h3, h4, h5, h6, p, pre, div, blockquote"
              }, false, commonAncestorNode) : null;

            if (curNode) {
              while (curNode.firstChild) {
                prevNode.appendChild(curNode.firstChild);
              }
              selection.setAfter(selNode);
              return true;
            } else if (selectedNode.nodeType === 3) {
              prevNode.appendChild(selectedNode);
              selection.setAfter(selNode);
              return true;
            }
        }
      }
    }
    return false;
  };

  var handleDeleteKeyPress = function(event, composer) {
    var selection = composer.selection,
        element = composer.element;

    if (selection.isCollapsed()) {
      if (fixDeleteInTheBeginnigOfHeading(composer)) {
        event.preventDefault();
        return;
      }
      if (fixLastBrDeletionInTable(composer)) {
        event.preventDefault();
        return;
      }
      if (handleUneditableDeletion(composer)) {
        event.preventDefault();
        return;
      }
    } else {
      if (selection.containsUneditable()) {
        event.preventDefault();
        selection.deleteContents();
      }
    }
  };

  var handleTabKeyDown = function(composer, element, shiftKey) {
    if (!composer.selection.isCollapsed()) {
      composer.selection.deleteContents();
    } else if (composer.selection.caretIsInTheBeginnig('li')) {
      if (shiftKey) {
        if (composer.commands.exec('outdentList')) return;
      } else {
        if (composer.commands.exec('indentList')) return;
      }
    }

    // Is   close enough to tab. Could not find enough counter arguments for now.
    composer.commands.exec("insertHTML", " ");
  };

  var handleDomNodeRemoved = function(event) {
      if (this.domNodeRemovedInterval) {
        clearInterval(domNodeRemovedInterval);
      }
      this.parent.fire("destroy:composer");
  };

  // Listens to "drop", "paste", "mouseup", "focus", "keyup" events and fires
  var handleUserInteraction = function (event) {
    this.parent.fire("beforeinteraction", event).fire("beforeinteraction:composer", event);
    setTimeout((function() {
      this.parent.fire("interaction", event).fire("interaction:composer", event);
    }).bind(this), 0);
  };

  var handleFocus = function(event) {
    this.parent.fire("focus", event).fire("focus:composer", event);

    // Delay storing of state until all focus handler are fired
    // especially the one which resets the placeholder
    setTimeout((function() {
      this.focusState = this.getValue(false, false);
    }).bind(this), 0);
  };

  var handleBlur = function(event) {
    if (this.focusState !== this.getValue(false, false)) {
      //create change event if supported (all except IE8)
      var changeevent = event;
      if(typeof Object.create == 'function') {
        changeevent = Object.create(event, { type: { value: 'change' } });
      }
      this.parent.fire("change", changeevent).fire("change:composer", changeevent);
    }
    this.parent.fire("blur", event).fire("blur:composer", event);
  };

  var handlePaste = function(event) {
    this.parent.fire(event.type, event).fire(event.type + ":composer", event);
    if (event.type === "paste") {
      setTimeout((function() {
        this.parent.fire("newword:composer");
      }).bind(this), 0);
    }
  };

  var handleCopy = function(event) {
    if (this.config.copyedFromMarking) {
      // If supported the copied source can be based directly on selection
      // Very useful for webkit based browsers where copy will otherwise contain a lot of code and styles based on whatever and not actually in selection.
      if (event.clipboardData) {
        event.clipboardData.setData("text/html", this.config.copyedFromMarking + this.selection.getHtml());
        event.clipboardData.setData("text/plain", this.selection.getPlainText());
        event.preventDefault();
      }
      this.parent.fire(event.type, event).fire(event.type + ":composer", event);
    }
  };

  var handleKeyUp = function(event) {
    var keyCode = event.keyCode;
    if (keyCode === wysihtml5.SPACE_KEY || keyCode === wysihtml5.ENTER_KEY) {
      this.parent.fire("newword:composer");
    }
  };

  var handleMouseDown = function(event) {
    if (!browser.canSelectImagesInContentEditable()) {
      // Make sure that images are selected when clicking on them
      var target = event.target,
          allImages = this.element.querySelectorAll('img'),
          notMyImages = this.element.querySelectorAll('.' + this.config.classNames.uneditableContainer + ' img'),
          myImages = wysihtml5.lang.array(allImages).without(notMyImages);

      if (target.nodeName === "IMG" && wysihtml5.lang.array(myImages).contains(target)) {
        this.selection.selectNode(target);
      }
    }
  };

  // TODO: mouseover is not actually a foolproof and obvious place for this, must be changed as it modifies dom on random basis
  // Shows url in tooltip when hovering links or images
  var handleMouseOver = function(event) {
    var titlePrefixes = {
          IMG: "Image: ",
          A:   "Link: "
        },
        target   = event.target,
        nodeName = target.nodeName,
        title;

    if (nodeName !== "A" && nodeName !== "IMG") {
      return;
    }
    if(!target.hasAttribute("title")){
      title = titlePrefixes[nodeName] + (target.getAttribute("href") || target.getAttribute("src"));
      target.setAttribute("title", title);
    }
  };

  var handleClick = function(event) {
    if (this.config.classNames.uneditableContainer) {
      // If uneditables is configured, makes clicking on uneditable move caret after clicked element (so it can be deleted like text)
      // If uneditable needs text selection itself event.stopPropagation can be used to prevent this behaviour
      var uneditable = wysihtml5.dom.getParentElement(event.target, { query: "." + this.config.classNames.uneditableContainer }, false, this.element);
      if (uneditable) {
        this.selection.setAfter(uneditable);
      }
    }
  };

  var handleDrop = function(event) {
    if (!browser.canSelectImagesInContentEditable()) {
      // TODO: if I knew how to get dropped elements list from event I could limit it to only IMG element case
      setTimeout((function() {
        this.selection.getSelection().removeAllRanges();
      }).bind(this), 0);
    }
  };

  var handleKeyDown = function(event) {
    var keyCode = event.keyCode,
        command = shortcuts[keyCode],
        target, parent;

    // Select all (meta/ctrl + a)
    if ((event.ctrlKey || event.metaKey) && !event.altKey && keyCode === 65) {
      this.selection.selectAll();
      event.preventDefault();
      return;
    }

    // Shortcut logic
    if ((event.ctrlKey || event.metaKey) && !event.altKey && command) {
      this.commands.exec(command);
      event.preventDefault();
    }

    if (keyCode === wysihtml5.BACKSPACE_KEY) {
      // Delete key override for special cases
      handleDeleteKeyPress(event, this);
    }

    // Make sure that when pressing backspace/delete on selected images deletes the image and it's anchor
    if (keyCode === wysihtml5.BACKSPACE_KEY || keyCode === wysihtml5.DELETE_KEY) {
      target = this.selection.getSelectedNode(true);
      if (target && target.nodeName === "IMG") {
        event.preventDefault();
        parent = target.parentNode;
        parent.removeChild(target);// delete the <img>
        // And it's parent <a> too if it hasn't got any other child nodes
        if (parent.nodeName === "A" && !parent.firstChild) {
          parent.parentNode.removeChild(parent);
        }
        setTimeout((function() {
          wysihtml5.quirks.redraw(this.element);
        }).bind(this), 0);
      }
    }

    if (this.config.handleTabKey && keyCode === wysihtml5.TAB_KEY) {
      // TAB key handling
      event.preventDefault();
      handleTabKeyDown(this, this.element, event.shiftKey);
    }

  };

  var handleIframeFocus = function(event) {
    setTimeout((function() {
      if (this.doc.querySelector(":focus") !== this.element) {
        this.focus();
      }
    }).bind(this), 0);
  };

  var handleIframeBlur = function(event) {
    setTimeout((function() {
      this.selection.getSelection().removeAllRanges();
    }).bind(this), 0);
  };

  // Table management
  // If present enableObjectResizing and enableInlineTableEditing command should be called with false to prevent native table handlers
  var initTableHandling = function () {
    var hideHandlers = function () {
          this.doc.execCommand("enableObjectResizing", false, "false");
          this.doc.execCommand("enableInlineTableEditing", false, "false");
        },
        iframeInitiator = (function() {
          hideHandlers.call(this);
          removeListeners(this.sandbox.getIframe(), ["focus", "mouseup", "mouseover"], iframeInitiator);
        }).bind(this);

    if( this.doc.execCommand &&
        wysihtml5.browser.supportsCommand(this.doc, "enableObjectResizing") &&
        wysihtml5.browser.supportsCommand(this.doc, "enableInlineTableEditing"))
    {
      if (this.sandbox.getIframe) {
        addListeners(this.sandbox.getIframe(), ["focus", "mouseup", "mouseover"], iframeInitiator);
      } else {
        setTimeout((function() {
          hideHandlers.call(this);
        }).bind(this), 0);
      }
    }
    this.tableSelection = wysihtml5.quirks.tableCellsSelection(this.element, this.parent);
  };

  wysihtml5.views.Composer.prototype.observe = function() {
    var that                = this,
        container           = (this.sandbox.getIframe) ? this.sandbox.getIframe() : this.sandbox.getContentEditable(),
        element             = this.element,
        focusBlurElement    = (browser.supportsEventsInIframeCorrectly() || this.sandbox.getContentEditable) ? this.element : this.sandbox.getWindow();

    this.focusState = this.getValue(false, false);

    // --------- destroy:composer event ---------
    container.addEventListener(["DOMNodeRemoved"], handleDomNodeRemoved.bind(this), false);

    // DOMNodeRemoved event is not supported in IE 8
    // TODO: try to figure out a polyfill style fix, so it could be transferred to polyfills and removed if ie8 is not needed
    if (!browser.supportsMutationEvents()) {
      this.domNodeRemovedInterval = setInterval(function() {
        if (!dom.contains(document.documentElement, container)) {
          handleDomNodeRemoved.call(this);
        }
      }, 250);
    }

    // --------- User interactions --
    if (this.config.handleTables) {
      // If handleTables option is true, table handling functions are bound
      initTableHandling.call(this);
    }

    addListeners(focusBlurElement, ["drop", "paste", "mouseup", "focus", "keyup"], handleUserInteraction.bind(this));
    focusBlurElement.addEventListener("focus", handleFocus.bind(this), false);
    focusBlurElement.addEventListener("blur",  handleBlur.bind(this), false);

    addListeners(this.element, ["drop", "paste", "beforepaste"], handlePaste.bind(this), false);
    this.element.addEventListener("copy",       handleCopy.bind(this), false);
    this.element.addEventListener("mousedown",  handleMouseDown.bind(this), false);
    this.element.addEventListener("mouseover",  handleMouseOver.bind(this), false);
    this.element.addEventListener("click",      handleClick.bind(this), false);
    this.element.addEventListener("drop",       handleDrop.bind(this), false);
    this.element.addEventListener("keyup",      handleKeyUp.bind(this), false);
    this.element.addEventListener("keydown",    handleKeyDown.bind(this), false);

    this.element.addEventListener("dragenter", (function() {
      this.parent.fire("unset_placeholder");
    }).bind(this), false);

  };
})(wysihtml5);
<commit_msg>Fix FF initiation error<commit_after>/**
 * Taking care of events
 *  - Simulating 'change' event on contentEditable element
 *  - Handling drag & drop logic
 *  - Catch paste events
 *  - Dispatch proprietary newword:composer event
 *  - Keyboard shortcuts
 */
(function(wysihtml5) {
  var dom       = wysihtml5.dom,
      browser   = wysihtml5.browser,
      /**
       * Map keyCodes to query commands
       */
      shortcuts = {
        "66": "bold",     // B
        "73": "italic",   // I
        "85": "underline" // U
      };

  // Adds multiple eventlisteners to target, bound to one callback
  // TODO: If needed elsewhere make it part of wysihtml5.dom or sth
  var addListeners = function (target, events, callback) {
    for(var i = 0, max = events.length; i < max; i++) {
      target.addEventListener(events[i], callback, false);
    }
  };

  // Removes multiple eventlisteners from target, bound to one callback
  // TODO: If needed elsewhere make it part of wysihtml5.dom or sth
  var removeListeners = function (target, events, callback) {
    for(var i = 0, max = events.length; i < max; i++) {
      target.removeEventListener(events[i], callback, false);
    }
  };

  // Override for giving user ability to delete last line break in table cell
  var fixLastBrDeletionInTable = function(composer, force) {
    if (composer.selection.caretIsLastInSelection()) {
      var sel = composer.selection.getSelection(),
          aNode = sel.anchorNode;
      if (aNode && aNode.nodeType === 1 && (wysihtml5.dom.getParentElement(aNode, {query: 'td, th'}, false, composer.element) || force)) {
        var nextNode = aNode.childNodes[sel.anchorOffset];
        if (nextNode && nextNode.nodeType === 1 & nextNode.nodeName === "BR") {
          nextNode.parentNode.removeChild(nextNode);
          return true;
        }
      }
    }
    return false;
  };

  // If found an uneditable before caret then notify it before deletion
  var handleUneditableDeletion = function(composer) {
    var before = composer.selection.getBeforeSelection(true);
    if (before && (before.type === "element" || before.type === "leafnode") && before.node.nodeType === 1 && before.node.classList.contains(composer.config.classNames.uneditableContainer)) {
      if (fixLastBrDeletionInTable(composer, true)) {
        return true;
      }
      try {
        var ev = new CustomEvent("wysihtml5:uneditable:delete");
        before.node.dispatchEvent(ev);
      } catch (err) {}
      before.node.parentNode.removeChild(before.node);
      return true;
    }
    return false;
  };

  // Deletion with caret in the beginning of headings needs special attention
  // Heading does not concate text to previous block node correctly (browsers do unexpected miracles here especially webkit)
  var fixDeleteInTheBeginnigOfHeading = function(composer) {
    var selection = composer.selection,
        prevNode = selection.getPreviousNode();

    if (selection.caretIsFirstInSelection() &&
        prevNode &&
        prevNode.nodeType === 1 &&
        (/block/).test(composer.win.getComputedStyle(prevNode).display)
    ) {
      if ((/^\s*$/).test(prevNode.textContent || prevNode.innerText)) {
        // If heading is empty remove the heading node
        prevNode.parentNode.removeChild(prevNode);
        return true;
      } else {
        if (prevNode.lastChild) {
          var selNode = prevNode.lastChild,
              selectedNode = selection.getSelectedNode(),
              commonAncestorNode = wysihtml5.dom.domNode(prevNode).commonAncestor(selectedNode, composer.element);
              curNode = commonAncestorNode ? wysihtml5.dom.getParentElement(selectedNode, {
                query: "h1, h2, h3, h4, h5, h6, p, pre, div, blockquote"
              }, false, commonAncestorNode) : null;

            if (curNode) {
              while (curNode.firstChild) {
                prevNode.appendChild(curNode.firstChild);
              }
              selection.setAfter(selNode);
              return true;
            } else if (selectedNode.nodeType === 3) {
              prevNode.appendChild(selectedNode);
              selection.setAfter(selNode);
              return true;
            }
        }
      }
    }
    return false;
  };

  var handleDeleteKeyPress = function(event, composer) {
    var selection = composer.selection,
        element = composer.element;

    if (selection.isCollapsed()) {
      if (fixDeleteInTheBeginnigOfHeading(composer)) {
        event.preventDefault();
        return;
      }
      if (fixLastBrDeletionInTable(composer)) {
        event.preventDefault();
        return;
      }
      if (handleUneditableDeletion(composer)) {
        event.preventDefault();
        return;
      }
    } else {
      if (selection.containsUneditable()) {
        event.preventDefault();
        selection.deleteContents();
      }
    }
  };

  var handleTabKeyDown = function(composer, element, shiftKey) {
    if (!composer.selection.isCollapsed()) {
      composer.selection.deleteContents();
    } else if (composer.selection.caretIsInTheBeginnig('li')) {
      if (shiftKey) {
        if (composer.commands.exec('outdentList')) return;
      } else {
        if (composer.commands.exec('indentList')) return;
      }
    }

    // Is   close enough to tab. Could not find enough counter arguments for now.
    composer.commands.exec("insertHTML", " ");
  };

  var handleDomNodeRemoved = function(event) {
      if (this.domNodeRemovedInterval) {
        clearInterval(domNodeRemovedInterval);
      }
      this.parent.fire("destroy:composer");
  };

  // Listens to "drop", "paste", "mouseup", "focus", "keyup" events and fires
  var handleUserInteraction = function (event) {
    this.parent.fire("beforeinteraction", event).fire("beforeinteraction:composer", event);
    setTimeout((function() {
      this.parent.fire("interaction", event).fire("interaction:composer", event);
    }).bind(this), 0);
  };

  var handleFocus = function(event) {
    this.parent.fire("focus", event).fire("focus:composer", event);

    // Delay storing of state until all focus handler are fired
    // especially the one which resets the placeholder
    setTimeout((function() {
      this.focusState = this.getValue(false, false);
    }).bind(this), 0);
  };

  var handleBlur = function(event) {
    if (this.focusState !== this.getValue(false, false)) {
      //create change event if supported (all except IE8)
      var changeevent = event;
      if(typeof Object.create == 'function') {
        changeevent = Object.create(event, { type: { value: 'change' } });
      }
      this.parent.fire("change", changeevent).fire("change:composer", changeevent);
    }
    this.parent.fire("blur", event).fire("blur:composer", event);
  };

  var handlePaste = function(event) {
    this.parent.fire(event.type, event).fire(event.type + ":composer", event);
    if (event.type === "paste") {
      setTimeout((function() {
        this.parent.fire("newword:composer");
      }).bind(this), 0);
    }
  };

  var handleCopy = function(event) {
    if (this.config.copyedFromMarking) {
      // If supported the copied source can be based directly on selection
      // Very useful for webkit based browsers where copy will otherwise contain a lot of code and styles based on whatever and not actually in selection.
      if (event.clipboardData) {
        event.clipboardData.setData("text/html", this.config.copyedFromMarking + this.selection.getHtml());
        event.clipboardData.setData("text/plain", this.selection.getPlainText());
        event.preventDefault();
      }
      this.parent.fire(event.type, event).fire(event.type + ":composer", event);
    }
  };

  var handleKeyUp = function(event) {
    var keyCode = event.keyCode;
    if (keyCode === wysihtml5.SPACE_KEY || keyCode === wysihtml5.ENTER_KEY) {
      this.parent.fire("newword:composer");
    }
  };

  var handleMouseDown = function(event) {
    if (!browser.canSelectImagesInContentEditable()) {
      // Make sure that images are selected when clicking on them
      var target = event.target,
          allImages = this.element.querySelectorAll('img'),
          notMyImages = this.element.querySelectorAll('.' + this.config.classNames.uneditableContainer + ' img'),
          myImages = wysihtml5.lang.array(allImages).without(notMyImages);

      if (target.nodeName === "IMG" && wysihtml5.lang.array(myImages).contains(target)) {
        this.selection.selectNode(target);
      }
    }
  };

  // TODO: mouseover is not actually a foolproof and obvious place for this, must be changed as it modifies dom on random basis
  // Shows url in tooltip when hovering links or images
  var handleMouseOver = function(event) {
    var titlePrefixes = {
          IMG: "Image: ",
          A:   "Link: "
        },
        target   = event.target,
        nodeName = target.nodeName,
        title;

    if (nodeName !== "A" && nodeName !== "IMG") {
      return;
    }
    if(!target.hasAttribute("title")){
      title = titlePrefixes[nodeName] + (target.getAttribute("href") || target.getAttribute("src"));
      target.setAttribute("title", title);
    }
  };

  var handleClick = function(event) {
    if (this.config.classNames.uneditableContainer) {
      // If uneditables is configured, makes clicking on uneditable move caret after clicked element (so it can be deleted like text)
      // If uneditable needs text selection itself event.stopPropagation can be used to prevent this behaviour
      var uneditable = wysihtml5.dom.getParentElement(event.target, { query: "." + this.config.classNames.uneditableContainer }, false, this.element);
      if (uneditable) {
        this.selection.setAfter(uneditable);
      }
    }
  };

  var handleDrop = function(event) {
    if (!browser.canSelectImagesInContentEditable()) {
      // TODO: if I knew how to get dropped elements list from event I could limit it to only IMG element case
      setTimeout((function() {
        this.selection.getSelection().removeAllRanges();
      }).bind(this), 0);
    }
  };

  var handleKeyDown = function(event) {
    var keyCode = event.keyCode,
        command = shortcuts[keyCode],
        target, parent;

    // Select all (meta/ctrl + a)
    if ((event.ctrlKey || event.metaKey) && !event.altKey && keyCode === 65) {
      this.selection.selectAll();
      event.preventDefault();
      return;
    }

    // Shortcut logic
    if ((event.ctrlKey || event.metaKey) && !event.altKey && command) {
      this.commands.exec(command);
      event.preventDefault();
    }

    if (keyCode === wysihtml5.BACKSPACE_KEY) {
      // Delete key override for special cases
      handleDeleteKeyPress(event, this);
    }

    // Make sure that when pressing backspace/delete on selected images deletes the image and it's anchor
    if (keyCode === wysihtml5.BACKSPACE_KEY || keyCode === wysihtml5.DELETE_KEY) {
      target = this.selection.getSelectedNode(true);
      if (target && target.nodeName === "IMG") {
        event.preventDefault();
        parent = target.parentNode;
        parent.removeChild(target);// delete the <img>
        // And it's parent <a> too if it hasn't got any other child nodes
        if (parent.nodeName === "A" && !parent.firstChild) {
          parent.parentNode.removeChild(parent);
        }
        setTimeout((function() {
          wysihtml5.quirks.redraw(this.element);
        }).bind(this), 0);
      }
    }

    if (this.config.handleTabKey && keyCode === wysihtml5.TAB_KEY) {
      // TAB key handling
      event.preventDefault();
      handleTabKeyDown(this, this.element, event.shiftKey);
    }

  };

  var handleIframeFocus = function(event) {
    setTimeout((function() {
      if (this.doc.querySelector(":focus") !== this.element) {
        this.focus();
      }
    }).bind(this), 0);
  };

  var handleIframeBlur = function(event) {
    setTimeout((function() {
      this.selection.getSelection().removeAllRanges();
    }).bind(this), 0);
  };

  // Table management
  // If present enableObjectResizing and enableInlineTableEditing command should be called with false to prevent native table handlers
  var initTableHandling = function () {
    var hideHandlers = function () {
          document.removeEventListener('load', hideHandlers);
          this.doc.execCommand("enableObjectResizing", false, "false");
          this.doc.execCommand("enableInlineTableEditing", false, "false");

        }.bind(this),
        iframeInitiator = (function() {
          hideHandlers.call(this);
          removeListeners(this.sandbox.getIframe(), ["focus", "mouseup", "mouseover"], iframeInitiator);
        }).bind(this);

    if( this.doc.execCommand &&
        wysihtml5.browser.supportsCommand(this.doc, "enableObjectResizing") &&
        wysihtml5.browser.supportsCommand(this.doc, "enableInlineTableEditing"))
    {
      if (this.sandbox.getIframe) {
        addListeners(this.sandbox.getIframe(), ["focus", "mouseup", "mouseover"], iframeInitiator);
      } else {
        document.addEventListener('load', hideHandlers);
      }
    }
    this.tableSelection = wysihtml5.quirks.tableCellsSelection(this.element, this.parent);
  };

  wysihtml5.views.Composer.prototype.observe = function() {
    var that                = this,
        container           = (this.sandbox.getIframe) ? this.sandbox.getIframe() : this.sandbox.getContentEditable(),
        element             = this.element,
        focusBlurElement    = (browser.supportsEventsInIframeCorrectly() || this.sandbox.getContentEditable) ? this.element : this.sandbox.getWindow();

    this.focusState = this.getValue(false, false);

    // --------- destroy:composer event ---------
    container.addEventListener(["DOMNodeRemoved"], handleDomNodeRemoved.bind(this), false);

    // DOMNodeRemoved event is not supported in IE 8
    // TODO: try to figure out a polyfill style fix, so it could be transferred to polyfills and removed if ie8 is not needed
    if (!browser.supportsMutationEvents()) {
      this.domNodeRemovedInterval = setInterval(function() {
        if (!dom.contains(document.documentElement, container)) {
          handleDomNodeRemoved.call(this);
        }
      }, 250);
    }

    // --------- User interactions --
    if (this.config.handleTables) {
      // If handleTables option is true, table handling functions are bound
      initTableHandling.call(this);
    }

    addListeners(focusBlurElement, ["drop", "paste", "mouseup", "focus", "keyup"], handleUserInteraction.bind(this));
    focusBlurElement.addEventListener("focus", handleFocus.bind(this), false);
    focusBlurElement.addEventListener("blur",  handleBlur.bind(this), false);

    addListeners(this.element, ["drop", "paste", "beforepaste"], handlePaste.bind(this), false);
    this.element.addEventListener("copy",       handleCopy.bind(this), false);
    this.element.addEventListener("mousedown",  handleMouseDown.bind(this), false);
    this.element.addEventListener("mouseover",  handleMouseOver.bind(this), false);
    this.element.addEventListener("click",      handleClick.bind(this), false);
    this.element.addEventListener("drop",       handleDrop.bind(this), false);
    this.element.addEventListener("keyup",      handleKeyUp.bind(this), false);
    this.element.addEventListener("keydown",    handleKeyDown.bind(this), false);

    this.element.addEventListener("dragenter", (function() {
      this.parent.fire("unset_placeholder");
    }).bind(this), false);

  };
})(wysihtml5);
<|endoftext|>


<commit_before>import {logError} from './utils.js';
import * as events from './events.js';
import CONSTANTS from './constants.json';

const {AD_RENDER_FAILED, AD_RENDER_SUCCEEDED} = CONSTANTS.EVENTS;

/**
 * Emit the AD_RENDER_FAILED event.
 *
 * @param reason one of the values in CONSTANTS.AD_RENDER_FAILED_REASON
 * @param message failure description
 * @param bid? bid response object that failed to render
 * @param id? adId that failed to render
 */
export function emitAdRenderFail({ reason, message, bid, id }) {
  const data = { reason, message };
  if (bid) data.bid = bid;
  if (id) data.adId = id;

  logError(message);
  events.emit(AD_RENDER_FAILED, data);
}

/**
 * Emit the AD_RENDER_SUCCEEDED event.
 *
 * @param doc document object that was used to `.write` the ad. Should be `null` if unavailable (e.g. for documents in
 * a cross-origin frame).
 * @param bid bid response object for the ad that was rendered
 * @param id adId that was rendered.
 */
export function emitAdRenderSucceeded({ doc, bid, id }) {
  const data = { doc };
  if (bid) data.bid = bid;
  if (id) data.adId = id;

  events.emit(AD_RENDER_SUCCEEDED, data);
}
<commit_msg>updated docs for adRenderSucceeded event (#9195)<commit_after>import {logError} from './utils.js';
import * as events from './events.js';
import CONSTANTS from './constants.json';

const {AD_RENDER_FAILED, AD_RENDER_SUCCEEDED} = CONSTANTS.EVENTS;

/**
 * Emit the AD_RENDER_FAILED event.
 *
 * @param reason one of the values in CONSTANTS.AD_RENDER_FAILED_REASON
 * @param message failure description
 * @param bid? bid response object that failed to render
 * @param id? adId that failed to render
 */
export function emitAdRenderFail({ reason, message, bid, id }) {
  const data = { reason, message };
  if (bid) data.bid = bid;
  if (id) data.adId = id;

  logError(message);
  events.emit(AD_RENDER_FAILED, data);
}

/**
 * Emit the AD_RENDER_SUCCEEDED event.
 * (Note: Invocation of this function indicates that the render function did not generate an error, it does not guarantee that tracking for this event has occurred yet.)
 * @param doc document object that was used to `.write` the ad. Should be `null` if unavailable (e.g. for documents in
 * a cross-origin frame).
 * @param bid bid response object for the ad that was rendered
 * @param id adId that was rendered.
 */
export function emitAdRenderSucceeded({ doc, bid, id }) {
  const data = { doc };
  if (bid) data.bid = bid;
  if (id) data.adId = id;

  events.emit(AD_RENDER_SUCCEEDED, data);
}
<|endoftext|>


<commit_before>'use strict';

angular.module('mgcrea.ngStrap.affix', ['mgcrea.ngStrap.helpers.dimensions', 'mgcrea.ngStrap.helpers.debounce'])

  .provider('$affix', function() {

    var defaults = this.defaults = {
      offsetTop: 'auto'
    };

    this.$get = function($window, debounce, dimensions) {

      var bodyEl = angular.element($window.document.body);
      var windowEl = angular.element($window);

      function AffixFactory(element, config) {

        var $affix = {};

        // Common vars
        var options = angular.extend({}, defaults, config);
        var targetEl = options.target;

        // Initial private vars
        var reset = 'affix affix-top affix-bottom',
            initialAffixTop = 0,
            initialOffsetTop = 0,
            offsetTop = 0,
            offsetBottom = 0,
            affixed = null,
            unpin = null;

        var parent = element.parent();
        // Options: custom parent
        if (options.offsetParent) {
          if (options.offsetParent.match(/^\d+$/)) {
            for (var i = 0; i < (options.offsetParent * 1) - 1; i++) {
              parent = parent.parent();
            }
          }
          else {
            parent = angular.element(options.offsetParent);
          }
        }

        $affix.init = function() {

          $affix.$parseOffsets();
          initialOffsetTop = dimensions.offset(element[0]).top + initialAffixTop;

          // Bind events
          targetEl.on('scroll', $affix.checkPosition);
          targetEl.on('click', $affix.checkPositionWithEventLoop);
          windowEl.on('resize', $affix.$debouncedOnResize);

          // Both of these checkPosition() calls are necessary for the case where
          // the user hits refresh after scrolling to the bottom of the page.
          $affix.checkPosition();
          $affix.checkPositionWithEventLoop();

        };

        $affix.destroy = function() {

          // Unbind events
          targetEl.off('scroll', $affix.checkPosition);
          targetEl.off('click', $affix.checkPositionWithEventLoop);
          windowEl.off('resize', $affix.$debouncedOnResize);

        };

        $affix.checkPositionWithEventLoop = function() {

          setTimeout($affix.checkPosition, 1);

        };

        $affix.checkPosition = function() {
          // if (!this.$element.is(':visible')) return

          var scrollTop = getScrollTop();
          var position = dimensions.offset(element[0]);
          var elementHeight = dimensions.height(element[0]);

          // Get required affix class according to position
          var affix = getRequiredAffixClass(unpin, position, elementHeight);

          // Did affix status changed this last check?
          if(affixed === affix) return;
          affixed = affix;

          // Add proper affix class
          element.removeClass(reset).addClass('affix' + ((affix !== 'middle') ? '-' + affix : ''));

          if(affix === 'top') {
            unpin = null;
            element.css('position', (options.offsetParent) ? '' : 'relative');
            element.css('top', '');
          } else if(affix === 'bottom') {
            if (options.offsetUnpin) {
              unpin = -(options.offsetUnpin * 1);
            }
            else {
              // Calculate unpin threshold when affixed to bottom.
              // Hopefully the browser scrolls pixel by pixel.
              unpin = position.top - scrollTop;
            }
            element.css('position', (options.offsetParent) ? '' : 'relative');
            element.css('top', (options.offsetParent) ? '' : ((bodyEl[0].offsetHeight - offsetBottom - elementHeight - initialOffsetTop) + 'px'));
          } else { // affix === 'middle'
            unpin = null;
            element.css('position', 'fixed');
            element.css('top', initialAffixTop + 'px');
          }

        };

        $affix.$onResize = function() {
          $affix.$parseOffsets();
          $affix.checkPosition();
        };
        $affix.$debouncedOnResize = debounce($affix.$onResize, 50);

        $affix.$parseOffsets = function() {

          // Reset position to calculate correct offsetTop
          element.css('position', (options.offsetParent) ? '' : 'relative');

          if(options.offsetTop) {
            if(options.offsetTop === 'auto') {
              options.offsetTop = '+0';
            }
            if(options.offsetTop.match(/^[-+]\d+$/)) {
              initialAffixTop = - options.offsetTop * 1;
              if(options.offsetParent) {
                offsetTop = dimensions.offset(parent[0]).top + (options.offsetTop * 1);
              }
              else {
                offsetTop = dimensions.offset(element[0]).top - dimensions.css(element[0], 'marginTop', true) + (options.offsetTop * 1);
              }
            }
            else {
              offsetTop = options.offsetTop * 1;
            }
          }

          if(options.offsetBottom) {
            if(options.offsetParent && options.offsetBottom.match(/^[-+]\d+$/)) {
              // add 1 pixel due to rounding problems...
              offsetBottom = getScrollHeight() - (dimensions.offset(parent[0]).top + dimensions.height(parent[0])) + (options.offsetBottom * 1) + 1;
            }
            else {
              offsetBottom = options.offsetBottom * 1;
            }
          }

        };

        // Private methods

        function getRequiredAffixClass(unpin, position, elementHeight) {

          var scrollTop = getScrollTop();
          var scrollHeight = getScrollHeight();

          if(scrollTop <= offsetTop) {
            return 'top';
          } else if(unpin !== null && (scrollTop + unpin <= position.top)) {
            return 'middle';
          } else if(offsetBottom !== null && (position.top + elementHeight + initialAffixTop >= scrollHeight - offsetBottom)) {
            return 'bottom';
          } else {
            return 'middle';
          }

        }

        function getScrollTop() {
          return targetEl[0] === $window ? $window.pageYOffset : targetEl[0] === $window;
        }

        function getScrollHeight() {
          return targetEl[0] === $window ? $window.document.body.scrollHeight : targetEl[0].scrollHeight;
        }

        $affix.init();
        return $affix;

      }

      return AffixFactory;

    };

  })

  .directive('bsAffix', function($affix, $window) {

    return {
      restrict: 'EAC',
      require: '^?bsAffixTarget',
      link: function postLink(scope, element, attr, affixTarget) {

        var options = {scope: scope, offsetTop: 'auto', target: affixTarget ? affixTarget.$element : angular.element($window)};
        angular.forEach(['offsetTop', 'offsetBottom', 'offsetParent', 'offsetUnpin'], function(key) {
          if(angular.isDefined(attr[key])) options[key] = attr[key];
        });

        var affix = $affix(element, options);
        scope.$on('$destroy', function() {
          affix.destroy()
          options = null;
          affix = null;
        });

      }
    };

  })

  .directive('bsAffixTarget', function() {
    return {
      controller: function($element) {
        this.$element = $element;
      }
    };
  });
<commit_msg>chore(affix): make jshint happy<commit_after>'use strict';

angular.module('mgcrea.ngStrap.affix', ['mgcrea.ngStrap.helpers.dimensions', 'mgcrea.ngStrap.helpers.debounce'])

  .provider('$affix', function() {

    var defaults = this.defaults = {
      offsetTop: 'auto'
    };

    this.$get = function($window, debounce, dimensions) {

      var bodyEl = angular.element($window.document.body);
      var windowEl = angular.element($window);

      function AffixFactory(element, config) {

        var $affix = {};

        // Common vars
        var options = angular.extend({}, defaults, config);
        var targetEl = options.target;

        // Initial private vars
        var reset = 'affix affix-top affix-bottom',
            initialAffixTop = 0,
            initialOffsetTop = 0,
            offsetTop = 0,
            offsetBottom = 0,
            affixed = null,
            unpin = null;

        var parent = element.parent();
        // Options: custom parent
        if (options.offsetParent) {
          if (options.offsetParent.match(/^\d+$/)) {
            for (var i = 0; i < (options.offsetParent * 1) - 1; i++) {
              parent = parent.parent();
            }
          }
          else {
            parent = angular.element(options.offsetParent);
          }
        }

        $affix.init = function() {

          $affix.$parseOffsets();
          initialOffsetTop = dimensions.offset(element[0]).top + initialAffixTop;

          // Bind events
          targetEl.on('scroll', $affix.checkPosition);
          targetEl.on('click', $affix.checkPositionWithEventLoop);
          windowEl.on('resize', $affix.$debouncedOnResize);

          // Both of these checkPosition() calls are necessary for the case where
          // the user hits refresh after scrolling to the bottom of the page.
          $affix.checkPosition();
          $affix.checkPositionWithEventLoop();

        };

        $affix.destroy = function() {

          // Unbind events
          targetEl.off('scroll', $affix.checkPosition);
          targetEl.off('click', $affix.checkPositionWithEventLoop);
          windowEl.off('resize', $affix.$debouncedOnResize);

        };

        $affix.checkPositionWithEventLoop = function() {

          setTimeout($affix.checkPosition, 1);

        };

        $affix.checkPosition = function() {
          // if (!this.$element.is(':visible')) return

          var scrollTop = getScrollTop();
          var position = dimensions.offset(element[0]);
          var elementHeight = dimensions.height(element[0]);

          // Get required affix class according to position
          var affix = getRequiredAffixClass(unpin, position, elementHeight);

          // Did affix status changed this last check?
          if(affixed === affix) return;
          affixed = affix;

          // Add proper affix class
          element.removeClass(reset).addClass('affix' + ((affix !== 'middle') ? '-' + affix : ''));

          if(affix === 'top') {
            unpin = null;
            element.css('position', (options.offsetParent) ? '' : 'relative');
            element.css('top', '');
          } else if(affix === 'bottom') {
            if (options.offsetUnpin) {
              unpin = -(options.offsetUnpin * 1);
            }
            else {
              // Calculate unpin threshold when affixed to bottom.
              // Hopefully the browser scrolls pixel by pixel.
              unpin = position.top - scrollTop;
            }
            element.css('position', (options.offsetParent) ? '' : 'relative');
            element.css('top', (options.offsetParent) ? '' : ((bodyEl[0].offsetHeight - offsetBottom - elementHeight - initialOffsetTop) + 'px'));
          } else { // affix === 'middle'
            unpin = null;
            element.css('position', 'fixed');
            element.css('top', initialAffixTop + 'px');
          }

        };

        $affix.$onResize = function() {
          $affix.$parseOffsets();
          $affix.checkPosition();
        };
        $affix.$debouncedOnResize = debounce($affix.$onResize, 50);

        $affix.$parseOffsets = function() {

          // Reset position to calculate correct offsetTop
          element.css('position', (options.offsetParent) ? '' : 'relative');

          if(options.offsetTop) {
            if(options.offsetTop === 'auto') {
              options.offsetTop = '+0';
            }
            if(options.offsetTop.match(/^[-+]\d+$/)) {
              initialAffixTop = - options.offsetTop * 1;
              if(options.offsetParent) {
                offsetTop = dimensions.offset(parent[0]).top + (options.offsetTop * 1);
              }
              else {
                offsetTop = dimensions.offset(element[0]).top - dimensions.css(element[0], 'marginTop', true) + (options.offsetTop * 1);
              }
            }
            else {
              offsetTop = options.offsetTop * 1;
            }
          }

          if(options.offsetBottom) {
            if(options.offsetParent && options.offsetBottom.match(/^[-+]\d+$/)) {
              // add 1 pixel due to rounding problems...
              offsetBottom = getScrollHeight() - (dimensions.offset(parent[0]).top + dimensions.height(parent[0])) + (options.offsetBottom * 1) + 1;
            }
            else {
              offsetBottom = options.offsetBottom * 1;
            }
          }

        };

        // Private methods

        function getRequiredAffixClass(unpin, position, elementHeight) {

          var scrollTop = getScrollTop();
          var scrollHeight = getScrollHeight();

          if(scrollTop <= offsetTop) {
            return 'top';
          } else if(unpin !== null && (scrollTop + unpin <= position.top)) {
            return 'middle';
          } else if(offsetBottom !== null && (position.top + elementHeight + initialAffixTop >= scrollHeight - offsetBottom)) {
            return 'bottom';
          } else {
            return 'middle';
          }

        }

        function getScrollTop() {
          return targetEl[0] === $window ? $window.pageYOffset : targetEl[0] === $window;
        }

        function getScrollHeight() {
          return targetEl[0] === $window ? $window.document.body.scrollHeight : targetEl[0].scrollHeight;
        }

        $affix.init();
        return $affix;

      }

      return AffixFactory;

    };

  })

  .directive('bsAffix', function($affix, $window) {

    return {
      restrict: 'EAC',
      require: '^?bsAffixTarget',
      link: function postLink(scope, element, attr, affixTarget) {

        var options = {scope: scope, offsetTop: 'auto', target: affixTarget ? affixTarget.$element : angular.element($window)};
        angular.forEach(['offsetTop', 'offsetBottom', 'offsetParent', 'offsetUnpin'], function(key) {
          if(angular.isDefined(attr[key])) options[key] = attr[key];
        });

        var affix = $affix(element, options);
        scope.$on('$destroy', function() {
          affix && affix.destroy();
          options = null;
          affix = null;
        });

      }
    };

  })

  .directive('bsAffixTarget', function() {
    return {
      controller: function($element) {
        this.$element = $element;
      }
    };
  });
<|endoftext|>


<commit_before>export default function getBaseUrl() {
  const inDevelopment = window.location.hostname === 'localhost';
  return inDevelopment ? 'http://localhost:3001/' : 'yourBaseUrlInProduction';
}
<commit_msg>Fix baseurl<commit_after>export default function getBaseUrl() {
  const inDevelopment = window.location.hostname === 'localhost';
  return inDevelopment ? 'http://localhost:3001/' : '/';
}
<|endoftext|>


<commit_before>module.exports = function initNetwork() {
  this.prototype.ajax = function(params) {
    // process jquery data
    if(!params.method && params.type && typeof params.type === 'string') {
      params.method = params.type.toLowerCase();
    }

    api.ajax(params, function(ret, err) {
      if (ret) {
        if(params.success) {
          params.success(ret);
        }
      } else {
        if(params.error) {
          params.error(err);
        }
        console.log("[network error]:"+JSON.stringify(err));
      }

      if(params.complete) {
        params.complete(ret, err);
      }
    })
  }

  this.prototype.get = function(url, data, success, error) {
    var _queryStr = '';
    if(typeof data === 'object') {
      var _dl = [];
      for (var key in data) {
        if (data.hasOwnProperty(key)) {
          _dl.push(key+"="+JSON.stringify(data[key]));
        }
      }
      _queryStr = _dl.join('&');
    }

    if(_queryStr) {
      if(url.indexOf('?') === -1) {
        url += '?' + _queryStr;
      }else {
        url += '&' + _queryStr;
      }
    }

    if(typeof data === 'function') {
      error = success;
      success = data;
    }

    api.ajax({
      url,
      method: 'get',
    }, function(ret, err) {
      if (ret) {
        if(success) {
          success(ret);
        }
      } else {
        if(error) {
          error(err);
        }
        console.log("[network error]:"+JSON.stringify(err));
      }
    })
  }

  this.prototype.post = function(url, data, success, error) {
    if(typeof data === 'function') {
      error = success;
      success = data;
    }

    api.ajax({
      url,
      method: 'post',
      data: {
        values: data
      },
    }, function(ret, err) {
      if (ret) {
        if(success) {
          success(ret);
        }
      } else {
        if(error) {
          error(err);
        }
        console.error(JSON.stringify(err));
      }
    })
  }

  this.prototype.cancelAjax = function(tag) {
    api.cancelAjax({tag});
  }

  this.prototype.download = function(url, params, cb) {
    api.download(Object.assign({url}, params), cb);
  }

  this.prototype.download = function(url) {
    api.download({url});
  }

  this.prototype.imageCache = function(url, params, cb) {
    api.imageCache(Object.assign({url}, params), cb);
  }
}
<commit_msg>修复一个get的bug<commit_after>module.exports = function initNetwork() {
  this.prototype.ajax = function(params) {
    // process jquery data
    if(!params.method && params.type && typeof params.type === 'string') {
      params.method = params.type.toLowerCase();
    }

    api.ajax(params, function(ret, err) {
      if (ret) {
        if(params.success) {
          params.success(ret);
        }
      } else {
        if(params.error) {
          params.error(err);
        }
        console.log("[network error]:"+JSON.stringify(err));
      }

      if(params.complete) {
        params.complete(ret, err);
      }
    })
  }

  this.prototype.get = function(url, data, success, error) {
    var _queryStr = '';
    if(typeof data === 'object') {
      var _dl = [];
      for (var key in data) {
        if (data.hasOwnProperty(key)) {
          if(typeof data[key] === 'object') {
            _dl.push(key+"="+JSON.stringify(data[key]));
          }else {
            _dl.push(key+"="+data[key]);
          }
        }
      }
      _queryStr = _dl.join('&');
    }

    if(_queryStr) {
      if(url.indexOf('?') === -1) {
        url += '?' + _queryStr;
      }else {
        url += '&' + _queryStr;
      }
    }

    if(typeof data === 'function') {
      error = success;
      success = data;
    }

    api.ajax({
      url,
      method: 'get',
    }, function(ret, err) {
      if (ret) {
        if(success) {
          success(ret);
        }
      } else {
        if(error) {
          error(err);
        }
        console.log("[network error]:"+JSON.stringify(err));
      }
    })
  }

  this.prototype.post = function(url, data, success, error) {
    if(typeof data === 'function') {
      error = success;
      success = data;
    }

    api.ajax({
      url,
      method: 'post',
      data: {
        values: data
      },
    }, function(ret, err) {
      if (ret) {
        if(success) {
          success(ret);
        }
      } else {
        if(error) {
          error(err);
        }
        console.error(JSON.stringify(err));
      }
    })
  }

  this.prototype.cancelAjax = function(tag) {
    api.cancelAjax({tag});
  }

  this.prototype.download = function(url, params, cb) {
    api.download(Object.assign({url}, params), cb);
  }

  this.prototype.download = function(url) {
    api.download({url});
  }

  this.prototype.imageCache = function(url, params, cb) {
    api.imageCache(Object.assign({url}, params), cb);
  }
}
<|endoftext|>


<commit_before>import 'colors';
import debug from 'debug';
import fs from 'fs-extra';
import glob from 'glob';
import path from 'path';
import pify from 'pify';
import packager from 'electron-packager';
import { hostArch } from 'electron-packager/targets';

import getForgeConfig from '../util/forge-config';
import runHook from '../util/hook';
import { warn } from '../util/messages';
import realOra, { fakeOra } from '../util/ora';
import packagerCompileHook from '../util/compile-hook';
import readPackageJSON from '../util/read-package-json';
import rebuildHook from '../util/rebuild';
import requireSearch from '../util/require-search';
import resolveDir from '../util/resolve-dir';

const d = debug('electron-forge:packager');

/**
 * @typedef {Object} PackageOptions
 * @property {string} [dir=process.cwd()] The path to the app to package
 * @property {boolean} [interactive=false] Whether to use sensible defaults or prompt the user visually
 * @property {string} [arch=process.arch] The target arch
 * @property {string} [platform=process.platform] The target platform.
 * @property {string} [outDir=`${dir}/out`] The path to the output directory for packaged apps
 */

/**
 * Resolves hooks if they are a path to a file (instead of a `Function`).
 */
function resolveHooks(hooks, dir) {
  if (hooks) {
    return hooks.map(hook => (typeof hook === 'string' ? requireSearch(dir, [hook]) : hook));
  }

  return [];
}

/**
 * Package an Electron application into an platform dependent format.
 *
 * @param {PackageOptions} providedOptions - Options for the Package method
 * @return {Promise} Will resolve when the package process is complete
 */
export default async (providedOptions = {}) => {
  // eslint-disable-next-line prefer-const, no-unused-vars
  let { dir, interactive, arch, platform } = Object.assign({
    dir: process.cwd(),
    interactive: false,
    arch: hostArch(),
    platform: process.platform,
  }, providedOptions);

  const ora = interactive ? realOra : fakeOra;

  const outDir = providedOptions.outDir || path.resolve(dir, 'out');
  let prepareSpinner = ora(`Preparing to Package Application for arch: ${(arch === 'all' ? 'ia32' : arch).cyan}`).start();
  let prepareCounter = 0;

  dir = await resolveDir(dir);
  if (!dir) {
    throw 'Failed to locate compilable Electron application';
  }

  const packageJSON = await readPackageJSON(dir);

  if (path.dirname(require.resolve(path.resolve(dir, packageJSON.main))) === dir) {
    console.error(`Entry point: ${packageJSON.main}`.red);
    throw 'The entry point to your application ("packageJSON.main") must be in a subfolder not in the top level directory';
  }

  const forgeConfig = await getForgeConfig(dir);
  let packagerSpinner;

  const pruneEnabled = !('prune' in forgeConfig.electronPackagerConfig) || forgeConfig.electronPackagerConfig.prune;

  const rebuildHookFn = async (buildPath, electronVersion, pPlatform, pArch, done) => {
    await rebuildHook(buildPath, electronVersion, pPlatform, pArch);
    packagerSpinner = ora('Packaging Application').start();
    done();
  };

  const afterCopyHooks = [
    async (buildPath, electronVersion, pPlatform, pArch, done) => {
      if (packagerSpinner) {
        packagerSpinner.succeed();
        prepareCounter += 1;
        prepareSpinner = ora(`Preparing to Package Application for arch: ${(prepareCounter === 2 ? 'armv7l' : 'x64').cyan}`).start();
      }
      await fs.remove(path.resolve(buildPath, 'node_modules/electron-compile/test'));
      const bins = await pify(glob)(path.join(buildPath, '**/.bin/**/*'));
      for (const bin of bins) {
        await fs.remove(bin);
      }
      done();
    }, async (...args) => {
      prepareSpinner.succeed();
      await packagerCompileHook(dir, ...args);
    },
  ];

  if (!pruneEnabled) {
    afterCopyHooks.push(rebuildHookFn);
  }

  afterCopyHooks.push(async (buildPath, electronVersion, pPlatform, pArch, done) => {
    const copiedPackageJSON = await readPackageJSON(buildPath);
    if (copiedPackageJSON.config && copiedPackageJSON.config.forge) {
      delete copiedPackageJSON.config.forge;
    }
    await fs.writeFile(path.resolve(buildPath, 'package.json'), JSON.stringify(copiedPackageJSON, null, 2));
    done();
  });

  afterCopyHooks.concat(resolveHooks(forgeConfig.electronPackagerConfig.afterCopy, dir));

  const afterPruneHooks = [];

  if (pruneEnabled) {
    afterPruneHooks.push(rebuildHookFn);
    afterPruneHooks.concat(resolveHooks(forgeConfig.electronPackagerConfig.afterPrune, dir));
  }

  const packageOpts = Object.assign({
    asar: false,
    overwrite: true,
  }, forgeConfig.electronPackagerConfig, {
    afterCopy: afterCopyHooks,
    afterExtract: resolveHooks(forgeConfig.electronPackagerConfig.afterExtract, dir),
    afterPrune: afterPruneHooks,
    dir,
    arch,
    platform,
    out: outDir,
    electronVersion: packageJSON.devDependencies['electron-prebuilt-compile'],
  });
  packageOpts.quiet = true;
  if (typeof packageOpts.asar === 'object' && packageOpts.asar.unpack) {
    packagerSpinner.fail();
    throw new Error('electron-compile does not support asar.unpack yet.  Please use asar.unpackDir');
  }

  if (!packageJSON.version && !packageOpts.appVersion) {
    // eslint-disable-next-line max-len
    warn(interactive, "Please set 'version' or 'config.forge.electronPackagerConfig.appVersion' in your application's package.json so auto-updates work properly".yellow);
  }

  await runHook(forgeConfig, 'generateAssets');
  await runHook(forgeConfig, 'prePackage');

  d('packaging with options', packageOpts);

  await packager(packageOpts);

  await runHook(forgeConfig, 'postPackage');

  packagerSpinner.succeed();
};
<commit_msg>fix(packager): fix custom afterCopy, afterPrune not being included<commit_after>import 'colors';
import debug from 'debug';
import fs from 'fs-extra';
import glob from 'glob';
import path from 'path';
import pify from 'pify';
import packager from 'electron-packager';
import { hostArch } from 'electron-packager/targets';

import getForgeConfig from '../util/forge-config';
import runHook from '../util/hook';
import { warn } from '../util/messages';
import realOra, { fakeOra } from '../util/ora';
import packagerCompileHook from '../util/compile-hook';
import readPackageJSON from '../util/read-package-json';
import rebuildHook from '../util/rebuild';
import requireSearch from '../util/require-search';
import resolveDir from '../util/resolve-dir';

const d = debug('electron-forge:packager');

/**
 * @typedef {Object} PackageOptions
 * @property {string} [dir=process.cwd()] The path to the app to package
 * @property {boolean} [interactive=false] Whether to use sensible defaults or prompt the user visually
 * @property {string} [arch=process.arch] The target arch
 * @property {string} [platform=process.platform] The target platform.
 * @property {string} [outDir=`${dir}/out`] The path to the output directory for packaged apps
 */

/**
 * Resolves hooks if they are a path to a file (instead of a `Function`).
 */
function resolveHooks(hooks, dir) {
  if (hooks) {
    return hooks.map(hook => (typeof hook === 'string' ? requireSearch(dir, [hook]) : hook));
  }

  return [];
}

/**
 * Package an Electron application into an platform dependent format.
 *
 * @param {PackageOptions} providedOptions - Options for the Package method
 * @return {Promise} Will resolve when the package process is complete
 */
export default async (providedOptions = {}) => {
  // eslint-disable-next-line prefer-const, no-unused-vars
  let { dir, interactive, arch, platform } = Object.assign({
    dir: process.cwd(),
    interactive: false,
    arch: hostArch(),
    platform: process.platform,
  }, providedOptions);

  const ora = interactive ? realOra : fakeOra;

  const outDir = providedOptions.outDir || path.resolve(dir, 'out');
  let prepareSpinner = ora(`Preparing to Package Application for arch: ${(arch === 'all' ? 'ia32' : arch).cyan}`).start();
  let prepareCounter = 0;

  dir = await resolveDir(dir);
  if (!dir) {
    throw 'Failed to locate compilable Electron application';
  }

  const packageJSON = await readPackageJSON(dir);

  if (path.dirname(require.resolve(path.resolve(dir, packageJSON.main))) === dir) {
    console.error(`Entry point: ${packageJSON.main}`.red);
    throw 'The entry point to your application ("packageJSON.main") must be in a subfolder not in the top level directory';
  }

  const forgeConfig = await getForgeConfig(dir);
  let packagerSpinner;

  const pruneEnabled = !('prune' in forgeConfig.electronPackagerConfig) || forgeConfig.electronPackagerConfig.prune;

  const rebuildHookFn = async (buildPath, electronVersion, pPlatform, pArch, done) => {
    await rebuildHook(buildPath, electronVersion, pPlatform, pArch);
    packagerSpinner = ora('Packaging Application').start();
    done();
  };

  const afterCopyHooks = [
    async (buildPath, electronVersion, pPlatform, pArch, done) => {
      if (packagerSpinner) {
        packagerSpinner.succeed();
        prepareCounter += 1;
        prepareSpinner = ora(`Preparing to Package Application for arch: ${(prepareCounter === 2 ? 'armv7l' : 'x64').cyan}`).start();
      }
      await fs.remove(path.resolve(buildPath, 'node_modules/electron-compile/test'));
      const bins = await pify(glob)(path.join(buildPath, '**/.bin/**/*'));
      for (const bin of bins) {
        await fs.remove(bin);
      }
      done();
    }, async (...args) => {
      prepareSpinner.succeed();
      await packagerCompileHook(dir, ...args);
    },
  ];

  if (!pruneEnabled) {
    afterCopyHooks.push(rebuildHookFn);
  }

  afterCopyHooks.push(async (buildPath, electronVersion, pPlatform, pArch, done) => {
    const copiedPackageJSON = await readPackageJSON(buildPath);
    if (copiedPackageJSON.config && copiedPackageJSON.config.forge) {
      delete copiedPackageJSON.config.forge;
    }
    await fs.writeFile(path.resolve(buildPath, 'package.json'), JSON.stringify(copiedPackageJSON, null, 2));
    done();
  });

  afterCopyHooks.push(...resolveHooks(forgeConfig.electronPackagerConfig.afterCopy, dir));

  const afterPruneHooks = [];

  if (pruneEnabled) {
    afterPruneHooks.push(rebuildHookFn);
    afterPruneHooks.push(...resolveHooks(forgeConfig.electronPackagerConfig.afterPrune, dir));
  }

  const packageOpts = Object.assign({
    asar: false,
    overwrite: true,
  }, forgeConfig.electronPackagerConfig, {
    afterCopy: afterCopyHooks,
    afterExtract: resolveHooks(forgeConfig.electronPackagerConfig.afterExtract, dir),
    afterPrune: afterPruneHooks,
    dir,
    arch,
    platform,
    out: outDir,
    electronVersion: packageJSON.devDependencies['electron-prebuilt-compile'],
  });
  packageOpts.quiet = true;
  if (typeof packageOpts.asar === 'object' && packageOpts.asar.unpack) {
    packagerSpinner.fail();
    throw new Error('electron-compile does not support asar.unpack yet.  Please use asar.unpackDir');
  }

  if (!packageJSON.version && !packageOpts.appVersion) {
    // eslint-disable-next-line max-len
    warn(interactive, "Please set 'version' or 'config.forge.electronPackagerConfig.appVersion' in your application's package.json so auto-updates work properly".yellow);
  }

  await runHook(forgeConfig, 'generateAssets');
  await runHook(forgeConfig, 'prePackage');

  d('packaging with options', packageOpts);

  await packager(packageOpts);

  await runHook(forgeConfig, 'postPackage');

  packagerSpinner.succeed();
};
<|endoftext|>


<commit_before>
function CatalogController(catalogService, $routeParams) {
    var self = this;
    self.selectedCategoryId = $routeParams.categoryId || 0;

    var selectCategory = function (categoryId) {
        self.selectedCategoryId = categoryId;
        if (!self.allCatalogItems) {
            return;
        }
        if (!categoryId) {
            self.catalog = self.allCatalogItems;
            return;
        }
        self.catalog = self.allCatalogItems.filter(function (x) {
            return x.categoryId == categoryId;
        });
    }

    catalogService.catalog().then(function (catalog) {
        self.allCatalogItems = catalog;
        selectCategory(self.selectedCategoryId);
    });
}

function CatalogItemController(catalogService, $routeParams, cartService) {
    var self = this;
    self.item = null;

    self.quantityList = [];
    self.quantity = 1;

    for (var i = 1; i <= 10; i++) {
        self.quantityList.push(i);
    }

    self.addToCart = function () {
        cartService.addItem(self.item.id, self.item.title, self.quantity, self.item.salePrice, self.item.productArtUrl);
    };

    self.back = function () {
        history.back();
    };

    catalogService.item($routeParams.id)
        .then(function (item) {
            self.item = item;
        })
}

function CatalogService($q, $http) {
    var self = this;

    var items = [{
        id: '1',
        categoryId: 1,
        title: 'Lazer Blaster',
        description: 'Don\'t get stuck on mars without it.',
        productArtUrl: 'https://flak.blob.core.windows.net/catalog/lazer.jpg',
        salePrice: 10
    },
        {
            id: '2',
            categoryId: 2,
            title: 'Pet Space Rock',
            description: 'Just don\'t feed it after midnight.',
            productArtUrl: 'https://flak.blob.core.windows.net/catalog/pet-bio-rock.jpg',
            salePrice: 20
        },
        {
            id: '3',
            categoryId: 3,
            title: 'New World Flag Pole',
            description: 'Claim that new world you found.',
            productArtUrl: 'https://flak.blob.core.windows.net/catalog/indestructable-high-suction-flag-pole.jpg',
            salePrice: 30
        }];

    this.item = function (id) {
        var deferred = $q.defer();

        deferred.resolve(items.find(function (x) { return x.id == id }));

        return deferred.promise;
    };

    this.categories = function () {
        var deferred = $q.defer();

        $http({
            method: 'GET',
            url: '/api/categories'
        }).then(function successCallback(response) {
            deferred.resolve(response);
        }, function errorCallback(response) {

            //If we can't connect put the sample data in there for now
            deferred.resolve(
            [{
                id: 1,
                name: 'Lazers'
            },
            {
                id: 2,
                name: 'Rocks'
            },
            {
                id: 3,
                name: 'Flags'
            }]);
        });

        return deferred.promise;
    };

    this.catalog = function () {
        var deferred = $q.defer();

        deferred.resolve(items);

        return deferred.promise;

        $http({
            method: 'GET',
            url: '/api/catalog/products'
        }).then(function successCallback(response) {
            // this callback will be called asynchronously
            // when the response is available
        }, function errorCallback(response) {
            // called asynchronously if an error occurs
            // or server returns response with an error status.
        });
    }
}

angular.module('flakio')
    .controller('Catalog', CatalogController)
    .controller('CatalogItem', CatalogItemController)
    .service('catalogService', CatalogService);<commit_msg>fix catalog path<commit_after>
function CatalogController(catalogService, $routeParams) {
    var self = this;
    self.selectedCategoryId = $routeParams.categoryId || 0;

    var selectCategory = function (categoryId) {
        self.selectedCategoryId = categoryId;
        if (!self.allCatalogItems) {
            return;
        }
        if (!categoryId) {
            self.catalog = self.allCatalogItems;
            return;
        }
        self.catalog = self.allCatalogItems.filter(function (x) {
            return x.categoryId == categoryId;
        });
    }

    catalogService.catalog().then(function (catalog) {
        self.allCatalogItems = catalog;
        selectCategory(self.selectedCategoryId);
    });
}

function CatalogItemController(catalogService, $routeParams, cartService) {
    var self = this;
    self.item = null;

    self.quantityList = [];
    self.quantity = 1;

    for (var i = 1; i <= 10; i++) {
        self.quantityList.push(i);
    }

    self.addToCart = function () {
        cartService.addItem(self.item.id, self.item.title, self.quantity, self.item.salePrice, self.item.productArtUrl);
    };

    self.back = function () {
        history.back();
    };

    catalogService.item($routeParams.id)
        .then(function (item) {
            self.item = item;
        })
}

function CatalogService($q, $http) {
    var self = this;

    var items = [{
        id: '1',
        categoryId: 1,
        title: 'Lazer Blaster',
        description: 'Don\'t get stuck on mars without it.',
        productArtUrl: 'https://flak.blob.core.windows.net/catalog/lazer.jpg',
        salePrice: 10
    },
        {
            id: '2',
            categoryId: 2,
            title: 'Pet Space Rock',
            description: 'Just don\'t feed it after midnight.',
            productArtUrl: 'https://flak.blob.core.windows.net/catalog/pet-bio-rock.jpg',
            salePrice: 20
        },
        {
            id: '3',
            categoryId: 3,
            title: 'New World Flag Pole',
            description: 'Claim that new world you found.',
            productArtUrl: 'https://flak.blob.core.windows.net/catalog/indestructable-high-suction-flag-pole.jpg',
            salePrice: 30
        }];

    this.item = function (id) {
        var deferred = $q.defer();

        deferred.resolve(items.find(function (x) { return x.id == id }));

        return deferred.promise;
    };

    this.categories = function () {
        var deferred = $q.defer();

        $http({
            method: 'GET',
            url: '/api/catalog/categories'
        }).then(function successCallback(response) {
            deferred.resolve(response);
        }, function errorCallback(response) {

            //If we can't connect put the sample data in there for now
            deferred.resolve(
            [{
                id: 1,
                name: 'Lazers'
            },
            {
                id: 2,
                name: 'Rocks'
            },
            {
                id: 3,
                name: 'Flags'
            }]);
        });

        return deferred.promise;
    };

    this.catalog = function () {
        var deferred = $q.defer();

        deferred.resolve(items);

        return deferred.promise;

        $http({
            method: 'GET',
            url: '/api/catalog/products'
        }).then(function successCallback(response) {
            // this callback will be called asynchronously
            // when the response is available
        }, function errorCallback(response) {
            // called asynchronously if an error occurs
            // or server returns response with an error status.
        });
    }
}

angular.module('flakio')
    .controller('Catalog', CatalogController)
    .controller('CatalogItem', CatalogItemController)
    .service('catalogService', CatalogService);<|endoftext|>


<commit_before>var fs = require('./utils/fs.js');
var path = require('path');
var server = require('server-root');
var _ = require('underscore');

_.str = require('underscore.string');

module.exports = {

    bower: {
        install: function (name, force) {
            if (!fs.existsSync(server.getPath('bower/' + name)))
                throw new Error('bower module not found: ' + name);

            if (!fs.lstatSync(server.getPath('bower/' + name)).isDirectory())
                throw new Error('bower module not a directory: ' + name);

            if (fs.existsSync(server.getPath('bower_components/' + name)))
                if (force === true)
                    fs.delete(server.getPath('bower_components/' + name));

            if (!fs.existsSync(server.getPath('bower_components/' + name)))
                fs.copy(server.getPath('bower/' + name), server.getPath('bower_components/' + name));
        }
    },

    npm: {

        getInstalled: function () {
            var installed = [];
            var found = fs.readdirSync(server.getPath('node_modules'));
            for (var i in found) {
                if (_.str.startsWith(found[i], 'jsnbt')) {
                    installed.push(found[i]);
                }
            }
            return installed;
        },

        install: function (name, force) {
            if (!fs.existsSync(server.getPath('npm/' + name)))
                throw new Error('npm module not found: ' + name);

            if (!fs.lstatSync(server.getPath('npm/' + name)).isDirectory())
                throw new Error('npm module not a directory: ' + name);

            if (fs.existsSync(server.getPath('node_modules/' + name)))
                if (force === true)
                    fs.delete(server.getPath('node_modules/' + name));

            if (!fs.existsSync(server.getPath('node_modules/' + name)))
                fs.copy(server.getPath('npm/' + name), server.getPath('node_modules/' + name));

            this.pack(name, force);
        },

        pack: function (name, force) {
            if (fs.existsSync(server.getPath('node_modules/' + name))) {
                var sourcePath = server.getPath('node_modules/' + name + '/src');
                var targetPath = server.getPath('src/pck/' + name);

                if (fs.existsSync(sourcePath)) {
                    if (fs.existsSync(targetPath)) {
                        if (force) {
                            fs.delete(targetPath, true);
                            fs.copy(sourcePath, targetPath);
                        }
                    }
                    else {
                        fs.copy(sourcePath, targetPath);
                    }
                }
            }
        },

        unpack: function (name) {
            var targetPath = server.getPath('src/pck/' + name);
            if (fs.existsSync(targetPath)) {
                fs.delete(targetPath, true);
            }
        },

        deploy: function (name, folder) {
            if (fs.existsSync(server.getPath('src/pck/' + name))) {

                if (fs.existsSync(server.getPath('src/pck/' + name + '/web/public'))) {
                    deployFiles(server.getPath('src/pck/' + name + '/web/public'), server.getPath(folder + '/public'));
                }

                if (fs.existsSync(server.getPath('src/pck/' + name + '/web/admin'))) {
                    deployFiles(server.getPath('src/pck/' + name + '/web/admin'), server.getPath(folder + '/public/admin'));
                }

                var resourcesFolder = server.getPath('src/pck/' + name + '/dpd/resources');
                if (fs.existsSync(resourcesFolder)) {

                    if (!fs.existsSync(server.getPath(folder + '/resources'))) {
                        fs.create(server.getPath(folder + '/resources'));
                    }

                    var resources = fs.readdirSync(resourcesFolder);
                    for (var ii in resources) {
                        var resourceName = resources[ii];
                        var configFile = resourcesFolder + '/' + resourceName + '/config.json';
                        var config = JSON.parse(fs.readFileSync(configFile));

                        var targetFolder = server.getPath(folder + '/resources/' + resourceName);
                        var targetFile = server.getPath(folder + '/resources/' + resourceName + '/config.json');
                        if (!fs.existsSync(targetFolder)) {
                            fs.mkdirSync(targetFolder);
                            fs.writeFileSync(targetFile, JSON.stringify(config, null, '\t'), 'utf-8');
                        }
                        else {
                            var targetConfig = JSON.parse(fs.readFileSync(targetFile));
                            var targetProperties = targetConfig.properties || {};

                            if (config.properties) {
                                for (var item in config.properties) {
                                    if (targetProperties[item] === undefined) {
                                        targetProperties[item] = config.properties[item];
                                    }
                                }
                            }

                            targetConfig.properties = targetProperties;
                            fs.writeFileSync(targetFile, JSON.stringify(targetConfig, null, '\t'), 'utf-8');
                        }
                    }
                }
            }
        }

    }
};

var deployFiles = function (source, target) {
    if (!fs.existsSync(target)) {
        fs.create(target);
    }

    fs.copy(source, target, false);
    diffLessFile(source + '/css/_.less', target + '/css/_.less');
};

var diffLessFile = function (source, target) {
    if (fs.existsSync(source)) {
        if (fs.existsSync(target)) {
            var sourceLines = fs.readFileSync(source, 'utf-8').split('\n');
            var targetLines = fs.readFileSync(target, 'utf-8').split('\n');
            var changed = false;

            for (var i = 0; i < sourceLines.length; i++) {
                if (targetLines.indexOf(sourceLines[i]) == -1) {
                    targetLines.push(sourceLines[i]);
                    changed = true;
                }
            }

            if (changed) {
                fs.writeFileSync(target, targetLines.join('\n'), 'utf-8');
            }
        }
    }
};<commit_msg>deploy dpd events<commit_after>var fs = require('./utils/fs.js');
var path = require('path');
var server = require('server-root');
var _ = require('underscore');

_.str = require('underscore.string');

module.exports = {

    bower: {
        install: function (name, force) {
            if (!fs.existsSync(server.getPath('bower/' + name)))
                throw new Error('bower module not found: ' + name);

            if (!fs.lstatSync(server.getPath('bower/' + name)).isDirectory())
                throw new Error('bower module not a directory: ' + name);

            if (fs.existsSync(server.getPath('bower_components/' + name)))
                if (force === true)
                    fs.delete(server.getPath('bower_components/' + name));

            if (!fs.existsSync(server.getPath('bower_components/' + name)))
                fs.copy(server.getPath('bower/' + name), server.getPath('bower_components/' + name));
        }
    },

    npm: {

        getInstalled: function () {
            var installed = [];
            var found = fs.readdirSync(server.getPath('node_modules'));
            for (var i in found) {
                if (_.str.startsWith(found[i], 'jsnbt')) {
                    installed.push(found[i]);
                }
            }
            return installed;
        },

        install: function (name, force) {
            if (!fs.existsSync(server.getPath('npm/' + name)))
                throw new Error('npm module not found: ' + name);

            if (!fs.lstatSync(server.getPath('npm/' + name)).isDirectory())
                throw new Error('npm module not a directory: ' + name);

            if (fs.existsSync(server.getPath('node_modules/' + name)))
                if (force === true)
                    fs.delete(server.getPath('node_modules/' + name));

            if (!fs.existsSync(server.getPath('node_modules/' + name)))
                fs.copy(server.getPath('npm/' + name), server.getPath('node_modules/' + name));

            this.pack(name, force);
        },

        pack: function (name, force) {
            if (fs.existsSync(server.getPath('node_modules/' + name))) {
                var sourcePath = server.getPath('node_modules/' + name + '/src');
                var targetPath = server.getPath('src/pck/' + name);

                if (fs.existsSync(sourcePath)) {
                    if (fs.existsSync(targetPath)) {
                        if (force) {
                            fs.delete(targetPath, true);
                            fs.copy(sourcePath, targetPath);
                        }
                    }
                    else {
                        fs.copy(sourcePath, targetPath);
                    }
                }
            }
        },

        unpack: function (name) {
            var targetPath = server.getPath('src/pck/' + name);
            if (fs.existsSync(targetPath)) {
                fs.delete(targetPath, true);
            }
        },

        deploy: function (name, folder) {
            if (fs.existsSync(server.getPath('src/pck/' + name))) {

                if (fs.existsSync(server.getPath('src/pck/' + name + '/web/public'))) {
                    deployFiles(server.getPath('src/pck/' + name + '/web/public'), server.getPath(folder + '/public'));
                }

                if (fs.existsSync(server.getPath('src/pck/' + name + '/web/admin'))) {
                    deployFiles(server.getPath('src/pck/' + name + '/web/admin'), server.getPath(folder + '/public/admin'));
                }

                var resourcesFolder = server.getPath('src/pck/' + name + '/dpd/resources');
                if (fs.existsSync(resourcesFolder)) {

                    if (!fs.existsSync(server.getPath(folder + '/resources'))) {
                        fs.create(server.getPath(folder + '/resources'));
                    }

                    var resources = fs.readdirSync(resourcesFolder);
                    for (var ii in resources) {
                        var resourceName = resources[ii];
                        var configFile = resourcesFolder + '/' + resourceName + '/config.json';
                        var config = JSON.parse(fs.readFileSync(configFile));

                        var targetFolder = server.getPath(folder + '/resources/' + resourceName);
                        var targetFile = server.getPath(folder + '/resources/' + resourceName + '/config.json');
                        if (!fs.existsSync(targetFolder)) {
                            fs.mkdirSync(targetFolder);
                            fs.writeFileSync(targetFile, JSON.stringify(config, null, '\t'), 'utf-8');
                        }
                        else {
                            var targetConfig = JSON.parse(fs.readFileSync(targetFile));
                            var targetProperties = targetConfig.properties || {};

                            if (config.properties) {
                                for (var item in config.properties) {
                                    if (targetProperties[item] === undefined) {
                                        targetProperties[item] = config.properties[item];
                                    }
                                }
                            }

                            targetConfig.properties = targetProperties;
                            fs.writeFileSync(targetFile, JSON.stringify(targetConfig, null, '\t'), 'utf-8');
                        }

                        var resourceContents = fs.readdirSync(resourcesFolder + '/' + resourceName);
                        _.each(resourceContents, function (resourceContentFile) {
                            if (_.str.endsWith(resourceContentFile, '.js')) {
                                var targetFile = server.getPath(folder + '/resources/' + resourceName + '/' + resourceContentFile);
                                if (!fs.existsSync(targetFile)) {
                                    fs.writeFileSync(targetFile, fs.readFileSync(resourcesFolder + '/' + resourceName + '/' + resourceContentFile), 'utf-8');
                                }
                                else {
                                    fs.appendFileSync(targetFile, fs.readFileSync(resourcesFolder + '/' + resourceName + '/' + resourceContentFile), 'utf-8');
                                }
                            }
                        });
                    }
                }
            }
        }

    }
};

var deployFiles = function (source, target) {
    if (!fs.existsSync(target)) {
        fs.create(target);
    }

    fs.copy(source, target, false);
    diffLessFile(source + '/css/_.less', target + '/css/_.less');
};

var diffLessFile = function (source, target) {
    if (fs.existsSync(source)) {
        if (fs.existsSync(target)) {
            var sourceLines = fs.readFileSync(source, 'utf-8').split('\n');
            var targetLines = fs.readFileSync(target, 'utf-8').split('\n');
            var changed = false;

            for (var i = 0; i < sourceLines.length; i++) {
                if (targetLines.indexOf(sourceLines[i]) == -1) {
                    targetLines.push(sourceLines[i]);
                    changed = true;
                }
            }

            if (changed) {
                fs.writeFileSync(target, targetLines.join('\n'), 'utf-8');
            }
        }
    }
};<|endoftext|>