<commit_before>(function($, $TD, TD){
  //
  // Variable: Current highlighted column jQuery object.
  //
  var highlightedColumnEle;

  //
  // Variable: Currently highlighted tweet jQuery object.
  //
  var highlightedTweetEle;

  //
  // Function: Initializes TweetD*ck events. Called after the website app is loaded.
  //
  var initializeTweetDck = function(){
    // Settings button hook
    $("[data-action='settings-menu']").click(function(){
      setTimeout(function(){
        var menu = $(".js-dropdown-content").children("ul").first();
        if (menu.length === 0)return;

        menu.children(".drp-h-divider").last().after([
          '
'+$TD.brandName+' settings
',
          '
'+$TD.brandName+' plugins
',
          '

'
        ].join(""));

        var buttons = menu.children("[data-std]");

        buttons.on("click", "a", function(){
          var action = $(this).attr("data-action");

          if (action === "td-settings"){
            $TD.openSettingsMenu();
          }
          else if (action === "td-plugins"){
            $TD.openPluginsMenu();
          }
        });

        buttons.hover(function(){
          $(this).addClass("is-selected");
        }, function(){
          $(this).removeClass("is-selected");
        });
      }, 0);
    });

    // Notification handling
    $.subscribe("/notifications/new", function(obj){
      for(let index = obj.items.length-1; index >= 0; index--){
        onNewTweet(obj.column, obj.items[index]);
      }
    });

    // Setup video element replacement and fix missing target in user links
    new MutationObserver(function(){
      $("video").each(function(){
        $(this).parent().replaceWith("► Open video in browser");
      });

      $("a[rel='user']").attr("target", "_blank");
    }).observe($(".js-app-columns")[0], {
      childList: true,
      subtree: true
    });

    // Finish init and load plugins
    $TD.loadFontSizeClass(TD.settings.getFontSize());
    $TD.loadNotificationHeadContents(getNotificationHeadContents());

    window.TD_APP_READY = true;

    if (window.TD_PLUGINS){
      window.TD_PLUGINS.onReady();
    }
  };

  //
  // Function: Prepends code at the beginning of a function. If the prepended function returns true, execution of the original function is cancelled.
  //
  var prependToFunction = function(func, extension){
    return function(){
      return extension.apply(this, arguments) === true ? undefined : func.apply(this, arguments);
    };
  };

  //
  // Function: Appends code at the end of a function.
  //
  var appendToFunction = function(func, extension){
    return function(){
      var res = func.apply(this, arguments);
      extension.apply(this, arguments);
      return res;
    };
  };

  //
  // Function: Event callback for a new tweet.
  //
  var onNewTweet = function(column, tweet){
    if (column.model.getHasNotification()){
      var html = $(tweet.render({
        withFooter: false,
        withTweetActions: false,
        withMediaPreview: true,
        isMediaPreviewOff: true,
        isMediaPreviewSmall: false,
        isMediaPreviewLarge: false
      }));

      html.css("border", "0");
      html.find("footer").last().remove(); // apparently withTweetActions breaks for certain tweets, nice

      var url = html.find("time").first().children("a").first().attr("href") || "";

      $TD.onTweetPopup(html.html(), url, tweet.text.length); // TODO column
    }

    if (column.model.getHasSound()){
      $TD.onTweetSound();
    }
  };

  //
  // Function: Retrieves the tags to be put into  for notification HTML code.
  //
  var getNotificationHeadContents = function(){
    var tags = [];

    $(document.head).children("link[rel='stylesheet'],meta[charset],meta[http-equiv]").each(function(){
      tags.push($(this)[0].outerHTML);
    });

    return tags.join("");
  };

  //
  // Block: Observe the app
 element and initialize TweetD*ck whenever possible.
  //
  var app = $("body").children(".js-app");

  new MutationObserver(function(){
    if (window.TD_APP_READY && app.hasClass("is-hidden")){
      window.TD_APP_READY = false;
    }
    else if (!window.TD_APP_READY && !app.hasClass("is-hidden")){
      initializeTweetDck();
    }
  }).observe(app[0], {
    attributes: true,
    attributeFilter: [ "class" ]
  });

  //
  // Block: Hook into settings object to detect when the settings change.
  //
  TD.settings.setFontSize = appendToFunction(TD.settings.setFontSize, function(name){
    $TD.loadFontSizeClass(name);
  });

  TD.settings.setTheme = appendToFunction(TD.settings.setTheme, function(){
    setTimeout(function(){
      $TD.loadNotificationHeadContents(getNotificationHeadContents());
    }, 0);
  });

  //
  // Block: Force popup notification settings.
  //
  TD.controller.notifications.hasNotifications = function(){
    return true;
  };

  TD.controller.notifications.isPermissionGranted = function(){
    return true;
  };

  //
  // Block: Expand shortened links on hover or display tooltip.
  //
  (function(){
    var cutStart = function(str, search){
      return str.startsWith(search) ? str.substr(search.length) : str;
    };

    var prevMouseX = -1, prevMouseY = -1;
    var tooltipTimer, tooltipDisplayed;

    $(document.body).delegate("a[data-full-url]", "mouseenter mouseleave mousemove", function(e){
      var me = $(this);

      if (e.type === "mouseenter"){
        var text = me.text();

        if (text.charCodeAt(text.length-1) !== 8230){ // horizontal ellipsis
          return;
        }

        if ($TD.expandLinksOnHover){
          tooltipTimer = window.setTimeout(function(){
            var expanded = me.attr("data-full-url");
            expanded = cutStart(expanded, "https://");
            expanded = cutStart(expanded, "http://");
            expanded = cutStart(expanded, "www.");

            me.attr("td-prev-text", text);
            me.text(expanded);
          }, 200);
        }
        else{
          tooltipTimer = window.setTimeout(function(){
            $TD.displayTooltip(me.attr("data-full-url"), false);
            tooltipDisplayed = true;
          }, 400);
        }
      }
      else if (e.type === "mouseleave"){
        if ($TD.expandLinksOnHover){
          var prevText = me.attr("td-prev-text");

          if (prevText){
            me.text(prevText);
          }
        }

        window.clearTimeout(tooltipTimer);

        if (tooltipDisplayed){
          tooltipDisplayed = false;
          $TD.displayTooltip(null, false);
        }
      }
      else if (e.type === "mousemove"){
        if (tooltipDisplayed && (prevMouseX !== e.clientX || prevMouseY !== e.clientY)){
          $TD.displayTooltip(me.attr("data-full-url"), false);
          prevMouseX = e.clientX;
          prevMouseY = e.clientY;
        }
      }
    });
  })();

  //
  // Block: Allow bypassing of t.co in context menus.
  //
  $(document.body).delegate("a", "contextmenu", function(){
    $TD.setLastRightClickedLink($(this).attr("data-full-url") || "");
  });

  //
  // Block: Hook into the notification sound effect.
  //
  (function(){
    var soundEle = document.getElementById("update-sound");

    soundEle.play = prependToFunction(soundEle.play, function(){
      return $TD.muteNotifications || $TD.hasCustomNotificationSound;
    });
  })();

  /* TODO document.getElementById("update-sound").play = function(){
    $TD.onTweetSound();
  };*/

  //
  // Block: Update highlighted column.
  //
  app.delegate("section", "mouseenter mouseleave", function(e){
    if (e.type === "mouseenter"){
      highlightedColumnEle = $(this);
    }
    else if (e.type === "mouseleave"){
      highlightedColumnEle = null;
    }
  });

  //
  // Block: Copy tweet address and update highlighted tweet.
  //
  (function(){
    var lastTweet = "";

    var updateHighlightedTweet = function(link, embeddedLink){
      if (lastTweet !== link){
        $TD.setLastHighlightedTweet(link, embeddedLink);
        lastTweet = link;
      }
    };

    app.delegate("article.js-stream-item", "mouseenter mouseleave", function(e){
      if (e.type === "mouseenter"){
        highlightedTweetEle = $(this);

        var link = $(this).find("time").first().children("a").first();
        var embedded = $(this).find(".quoted-tweet[data-tweet-id]").first();

        updateHighlightedTweet(link.length > 0 ? link.attr("href") : "", embedded.length > 0 ? embedded.find(".account-link").first().attr("href")+"/status/"+embedded.attr("data-tweet-id") : "");
      }
      else if (e.type === "mouseleave"){
        highlightedTweetEle = null;
        updateHighlightedTweet("", "");
      }
    });
  })();

  //
  // Block: Screenshot tweet to clipboard.
  //
  (function(){
    var selectedTweet;

    app.delegate("article.js-stream-item", "contextmenu", function(){
      selectedTweet = $(this);
    });

    window.TDGF_triggerScreenshot = function(){
      if (selectedTweet){
        var realWidth = selectedTweet.width();
        var realHeight = selectedTweet.height()-selectedTweet.find("footer").last().height();

        selectedTweet = selectedTweet.clone();
        selectedTweet.children().first().addClass($(document.documentElement).attr("class"));
        selectedTweet.find("footer").last().remove();

        $TD.screenshotTweet(selectedTweet.html(), realWidth, realHeight);
      }
    };
  })();

  //
  // Block: Paste images when tweeting.
  //
  (function(){
    var lastPasteElement;
    var prevScrollTop;

    var getScroller = function(){
      return $(".js-drawer").find(".js-compose-scroller").first().children().first();
    };

    var clickUpload = function(){
      var button = $(".js-add-image-button").first();

      var scroller = getScroller();
      prevScrollTop = scroller.scrollTop();

      scroller.scrollTop(0);
      scroller.scrollTop(button.offset().top); // scrolls the button into view

      var buttonPos = button.children().first().offset(); // finds the camera icon offset
      $TD.clickUploadImage(Math.floor(buttonPos.left), Math.floor(buttonPos.top));
    };

    $(".js-app").delegate(".js-compose-text,.js-reply-tweetbox", "paste", function(){
      lastPasteElement = $(this);
      $TD.tryPasteImage();
    });

    window.TDGF_tryPasteImage = function(){
      if (lastPasteElement){
        var parent = lastPasteElement.parent();

        if (parent.siblings(".js-add-image-button").length === 0){
          var pop = parent.closest(".js-inline-reply,.rpl").find(".js-inline-compose-pop,.js-reply-popout");

          if (pop.length === 0){
            lastPasteElement = null;
            return;
          }

          pop.click();

          var drawer = $(".js-drawer");
          var counter = 0;

          var interval = setInterval(function(){
            if (drawer.offset().left >= 195){
              clickUpload();
              clearInterval(interval);
            }
            else if (++counter >= 10){
              clearInterval(interval);
            }
          }, 51);
        }
        else{
          clickUpload();
        }

        lastPasteElement = null;
      }
    };

    window.TDGF_tryPasteImageFinish = function(){
      setTimeout(function(){
        getScroller().scrollTop(prevScrollTop);
        $(".js-drawer").find(".js-compose-text").first()[0].focus();
      }, 10);
    };
  })();

  //
  // Block: Support for extra mouse buttons.
  //
  (function(){
    var tryClickSelector = function(selector, parent){
      return $(selector, parent).click().length;
    };

    var tryCloseModal = function(){
      var modal = $("#open-modal");
      return modal.is(":visible") && tryClickSelector("a[rel=dismiss]", modal);
    };

    var tryCloseHighlightedColumn = function(){
      if (highlightedColumnEle){
        var column = highlightedColumnEle.closest(".js-column");
        return (column.is(".is-shifted-2") && tryClickSelector(".js-tweet-social-proof-back", column)) || (column.is(".is-shifted-1") && tryClickSelector(".js-column-back", column));
      }
    };

    window.TDGF_onMouseClickExtra = function(button){
      if (button === 1){ // back button
        tryCloseModal() ||
        tryClickSelector(".js-inline-compose-close") ||
        tryCloseHighlightedColumn() ||
        tryClickSelector(".js-app-content.is-open .js-drawer-close:visible") ||
        tryClickSelector(".is-shifted-2 .js-tweet-social-proof-back, .is-shifted-2 .js-dm-participants-back") ||
        $(".js-column-back").click();
      }
      else if (button === 2){ // forward button
        if (highlightedTweetEle){
          highlightedTweetEle.children().first().click();
        }
      }
    };
  })();

  //
  // Block: Fix scheduled tweets not showing up sometimes.
  //
  $(document).on("dataTweetSent", function(e, data){
    if (data.response.state && data.response.state === "scheduled"){
      var column = Object.values(TD.controller.columnManager.getAll()).find(column => column.model.state.type === "scheduled");

      if (column){
        setTimeout(function(){
          column.reloadTweets();
        }, 1000);
      }
    }
  });

  //
  // Block: Hold Shift to reset cleared column.
  //
  (function(){
    var holdingShift = false;

    var updateShiftState = (pressed) => {
      if (pressed != holdingShift){
        holdingShift = pressed;
        $("button[data-action='clear']").children("span").text(holdingShift ? "Reset" : "Clear");
      }
    };

    var resetActiveFocus = () => {
      document.activeElement.blur();
    };

    $(document).keydown(function(e){
      if (e.shiftKey && (document.activeElement === null || !("value" in document.activeElement))){
        updateShiftState(true);
      }
    }).keyup(function(e){
      if (!e.shiftKey){
        updateShiftState(false);
      }
    });

    TD.vo.Column.prototype.clear = prependToFunction(TD.vo.Column.prototype.clear, function(){
      window.setTimeout(resetActiveFocus, 0); // unfocuses the Clear button, otherwise it steals keyboard input

      if (holdingShift){
        this.model.setClearedTimestamp(0);
        this.reloadTweets();
        return true;
      }
    });
  })();

  //
  // Block: Inject custom CSS and layout into the page.
  //
  (function(){
    var styleOfficial = document.createElement("style");
    document.head.appendChild(styleOfficial);

    styleOfficial.sheet.insertRule("a[data-full-url] { word-break: break-all; }", 0); // break long urls
    styleOfficial.sheet.insertRule(".column-nav-link .attribution { position: absolute; }", 0); // fix cut off account names
    styleOfficial.sheet.insertRule(".txt-base-smallest .badge-verified:before { height: 13px !important; }", 0); // fix cut off badge icon
    styleOfficial.sheet.insertRule(".keyboard-shortcut-list { vertical-align: top; }", 0); // fix keyboard navigation alignment

    if ($TD.hasCustomBrowserCSS){
      var styleCustom = document.createElement("style");
      styleCustom.innerHTML = $TD.customBrowserCSS;
      document.head.appendChild(styleCustom);
    }

    TD.services.TwitterActionRetweetedRetweet.prototype.iconClass = "icon-retweet icon-retweet-color txt-base-medium"; // fix retweet icon mismatch
  })();
})($, $TD, TD);
<commit_msg>Fix highlighted tweet context menu for tweet detail view<commit_after>(function($, $TD, TD){
  //
  // Variable: Current highlighted column jQuery object.
  //
  var highlightedColumnEle;

  //
  // Variable: Currently highlighted tweet jQuery object.
  //
  var highlightedTweetEle;

  //
  // Function: Initializes TweetD*ck events. Called after the website app is loaded.
  //
  var initializeTweetDck = function(){
    // Settings button hook
    $("[data-action='settings-menu']").click(function(){
      setTimeout(function(){
        var menu = $(".js-dropdown-content").children("ul").first();
        if (menu.length === 0)return;

        menu.children(".drp-h-divider").last().after([
          '
'+$TD.brandName+' settings
',
          '
'+$TD.brandName+' plugins
',
          '

'
        ].join(""));

        var buttons = menu.children("[data-std]");

        buttons.on("click", "a", function(){
          var action = $(this).attr("data-action");

          if (action === "td-settings"){
            $TD.openSettingsMenu();
          }
          else if (action === "td-plugins"){
            $TD.openPluginsMenu();
          }
        });

        buttons.hover(function(){
          $(this).addClass("is-selected");
        }, function(){
          $(this).removeClass("is-selected");
        });
      }, 0);
    });

    // Notification handling
    $.subscribe("/notifications/new", function(obj){
      for(let index = obj.items.length-1; index >= 0; index--){
        onNewTweet(obj.column, obj.items[index]);
      }
    });

    // Setup video element replacement and fix missing target in user links
    new MutationObserver(function(){
      $("video").each(function(){
        $(this).parent().replaceWith("► Open video in browser");
      });

      $("a[rel='user']").attr("target", "_blank");
    }).observe($(".js-app-columns")[0], {
      childList: true,
      subtree: true
    });

    // Finish init and load plugins
    $TD.loadFontSizeClass(TD.settings.getFontSize());
    $TD.loadNotificationHeadContents(getNotificationHeadContents());

    window.TD_APP_READY = true;

    if (window.TD_PLUGINS){
      window.TD_PLUGINS.onReady();
    }
  };

  //
  // Function: Prepends code at the beginning of a function. If the prepended function returns true, execution of the original function is cancelled.
  //
  var prependToFunction = function(func, extension){
    return function(){
      return extension.apply(this, arguments) === true ? undefined : func.apply(this, arguments);
    };
  };

  //
  // Function: Appends code at the end of a function.
  //
  var appendToFunction = function(func, extension){
    return function(){
      var res = func.apply(this, arguments);
      extension.apply(this, arguments);
      return res;
    };
  };

  //
  // Function: Event callback for a new tweet.
  //
  var onNewTweet = function(column, tweet){
    if (column.model.getHasNotification()){
      var html = $(tweet.render({
        withFooter: false,
        withTweetActions: false,
        withMediaPreview: true,
        isMediaPreviewOff: true,
        isMediaPreviewSmall: false,
        isMediaPreviewLarge: false
      }));

      html.css("border", "0");
      html.find("footer").last().remove(); // apparently withTweetActions breaks for certain tweets, nice

      var url = html.find("time").first().children("a").first().attr("href") || "";

      $TD.onTweetPopup(html.html(), url, tweet.text.length); // TODO column
    }

    if (column.model.getHasSound()){
      $TD.onTweetSound();
    }
  };

  //
  // Function: Retrieves the tags to be put into  for notification HTML code.
  //
  var getNotificationHeadContents = function(){
    var tags = [];

    $(document.head).children("link[rel='stylesheet'],meta[charset],meta[http-equiv]").each(function(){
      tags.push($(this)[0].outerHTML);
    });

    return tags.join("");
  };

  //
  // Block: Observe the app
 element and initialize TweetD*ck whenever possible.
  //
  var app = $("body").children(".js-app");

  new MutationObserver(function(){
    if (window.TD_APP_READY && app.hasClass("is-hidden")){
      window.TD_APP_READY = false;
    }
    else if (!window.TD_APP_READY && !app.hasClass("is-hidden")){
      initializeTweetDck();
    }
  }).observe(app[0], {
    attributes: true,
    attributeFilter: [ "class" ]
  });

  //
  // Block: Hook into settings object to detect when the settings change.
  //
  TD.settings.setFontSize = appendToFunction(TD.settings.setFontSize, function(name){
    $TD.loadFontSizeClass(name);
  });

  TD.settings.setTheme = appendToFunction(TD.settings.setTheme, function(){
    setTimeout(function(){
      $TD.loadNotificationHeadContents(getNotificationHeadContents());
    }, 0);
  });

  //
  // Block: Force popup notification settings.
  //
  TD.controller.notifications.hasNotifications = function(){
    return true;
  };

  TD.controller.notifications.isPermissionGranted = function(){
    return true;
  };

  //
  // Block: Expand shortened links on hover or display tooltip.
  //
  (function(){
    var cutStart = function(str, search){
      return str.startsWith(search) ? str.substr(search.length) : str;
    };

    var prevMouseX = -1, prevMouseY = -1;
    var tooltipTimer, tooltipDisplayed;

    $(document.body).delegate("a[data-full-url]", "mouseenter mouseleave mousemove", function(e){
      var me = $(this);

      if (e.type === "mouseenter"){
        var text = me.text();

        if (text.charCodeAt(text.length-1) !== 8230){ // horizontal ellipsis
          return;
        }

        if ($TD.expandLinksOnHover){
          tooltipTimer = window.setTimeout(function(){
            var expanded = me.attr("data-full-url");
            expanded = cutStart(expanded, "https://");
            expanded = cutStart(expanded, "http://");
            expanded = cutStart(expanded, "www.");

            me.attr("td-prev-text", text);
            me.text(expanded);
          }, 200);
        }
        else{
          tooltipTimer = window.setTimeout(function(){
            $TD.displayTooltip(me.attr("data-full-url"), false);
            tooltipDisplayed = true;
          }, 400);
        }
      }
      else if (e.type === "mouseleave"){
        if ($TD.expandLinksOnHover){
          var prevText = me.attr("td-prev-text");

          if (prevText){
            me.text(prevText);
          }
        }

        window.clearTimeout(tooltipTimer);

        if (tooltipDisplayed){
          tooltipDisplayed = false;
          $TD.displayTooltip(null, false);
        }
      }
      else if (e.type === "mousemove"){
        if (tooltipDisplayed && (prevMouseX !== e.clientX || prevMouseY !== e.clientY)){
          $TD.displayTooltip(me.attr("data-full-url"), false);
          prevMouseX = e.clientX;
          prevMouseY = e.clientY;
        }
      }
    });
  })();

  //
  // Block: Allow bypassing of t.co in context menus.
  //
  $(document.body).delegate("a", "contextmenu", function(){
    $TD.setLastRightClickedLink($(this).attr("data-full-url") || "");
  });

  //
  // Block: Hook into the notification sound effect.
  //
  (function(){
    var soundEle = document.getElementById("update-sound");

    soundEle.play = prependToFunction(soundEle.play, function(){
      return $TD.muteNotifications || $TD.hasCustomNotificationSound;
    });
  })();

  /* TODO document.getElementById("update-sound").play = function(){
    $TD.onTweetSound();
  };*/

  //
  // Block: Update highlighted column.
  //
  app.delegate("section", "mouseenter mouseleave", function(e){
    if (e.type === "mouseenter"){
      highlightedColumnEle = $(this);
    }
    else if (e.type === "mouseleave"){
      highlightedColumnEle = null;
    }
  });

  //
  // Block: Copy tweet address and update highlighted tweet.
  //
  (function(){
    var lastTweet = "";

    var updateHighlightedTweet = function(link, embeddedLink){
      if (lastTweet !== link){
        $TD.setLastHighlightedTweet(link, embeddedLink);
        lastTweet = link;
      }
    };

    app.delegate("article.js-stream-item", "mouseenter mouseleave", function(e){
      if (e.type === "mouseenter"){
        highlightedTweetEle = $(this);

        var link = $(this).parent().hasClass("js-tweet-detail") ? $(this).find("a[rel='url']").first() : $(this).find("time").first().children("a").first();
        var embedded = $(this).find(".quoted-tweet[data-tweet-id]").first();

        updateHighlightedTweet(link.length > 0 ? link.attr("href") : "", embedded.length > 0 ? embedded.find(".account-link").first().attr("href")+"/status/"+embedded.attr("data-tweet-id") : "");
      }
      else if (e.type === "mouseleave"){
        highlightedTweetEle = null;
        updateHighlightedTweet("", "");
      }
    });
  })();

  //
  // Block: Screenshot tweet to clipboard.
  //
  (function(){
    var selectedTweet;

    app.delegate("article.js-stream-item", "contextmenu", function(){
      selectedTweet = $(this);
    });

    window.TDGF_triggerScreenshot = function(){
      if (selectedTweet){
        var realWidth = selectedTweet.width();
        var realHeight = selectedTweet.height()-selectedTweet.find("footer").last().height();

        selectedTweet = selectedTweet.clone();
        selectedTweet.children().first().addClass($(document.documentElement).attr("class"));
        selectedTweet.find("footer").last().remove();

        $TD.screenshotTweet(selectedTweet.html(), realWidth, realHeight);
      }
    };
  })();

  //
  // Block: Paste images when tweeting.
  //
  (function(){
    var lastPasteElement;
    var prevScrollTop;

    var getScroller = function(){
      return $(".js-drawer").find(".js-compose-scroller").first().children().first();
    };

    var clickUpload = function(){
      var button = $(".js-add-image-button").first();

      var scroller = getScroller();
      prevScrollTop = scroller.scrollTop();

      scroller.scrollTop(0);
      scroller.scrollTop(button.offset().top); // scrolls the button into view

      var buttonPos = button.children().first().offset(); // finds the camera icon offset
      $TD.clickUploadImage(Math.floor(buttonPos.left), Math.floor(buttonPos.top));
    };

    $(".js-app").delegate(".js-compose-text,.js-reply-tweetbox", "paste", function(){
      lastPasteElement = $(this);
      $TD.tryPasteImage();
    });

    window.TDGF_tryPasteImage = function(){
      if (lastPasteElement){
        var parent = lastPasteElement.parent();

        if (parent.siblings(".js-add-image-button").length === 0){
          var pop = parent.closest(".js-inline-reply,.rpl").find(".js-inline-compose-pop,.js-reply-popout");

          if (pop.length === 0){
            lastPasteElement = null;
            return;
          }

          pop.click();

          var drawer = $(".js-drawer");
          var counter = 0;

          var interval = setInterval(function(){
            if (drawer.offset().left >= 195){
              clickUpload();
              clearInterval(interval);
            }
            else if (++counter >= 10){
              clearInterval(interval);
            }
          }, 51);
        }
        else{
          clickUpload();
        }

        lastPasteElement = null;
      }
    };

    window.TDGF_tryPasteImageFinish = function(){
      setTimeout(function(){
        getScroller().scrollTop(prevScrollTop);
        $(".js-drawer").find(".js-compose-text").first()[0].focus();
      }, 10);
    };
  })();

  //
  // Block: Support for extra mouse buttons.
  //
  (function(){
    var tryClickSelector = function(selector, parent){
      return $(selector, parent).click().length;
    };

    var tryCloseModal = function(){
      var modal = $("#open-modal");
      return modal.is(":visible") && tryClickSelector("a[rel=dismiss]", modal);
    };

    var tryCloseHighlightedColumn = function(){
      if (highlightedColumnEle){
        var column = highlightedColumnEle.closest(".js-column");
        return (column.is(".is-shifted-2") && tryClickSelector(".js-tweet-social-proof-back", column)) || (column.is(".is-shifted-1") && tryClickSelector(".js-column-back", column));
      }
    };

    window.TDGF_onMouseClickExtra = function(button){
      if (button === 1){ // back button
        tryCloseModal() ||
        tryClickSelector(".js-inline-compose-close") ||
        tryCloseHighlightedColumn() ||
        tryClickSelector(".js-app-content.is-open .js-drawer-close:visible") ||
        tryClickSelector(".is-shifted-2 .js-tweet-social-proof-back, .is-shifted-2 .js-dm-participants-back") ||
        $(".js-column-back").click();
      }
      else if (button === 2){ // forward button
        if (highlightedTweetEle){
          highlightedTweetEle.children().first().click();
        }
      }
    };
  })();

  //
  // Block: Fix scheduled tweets not showing up sometimes.
  //
  $(document).on("dataTweetSent", function(e, data){
    if (data.response.state && data.response.state === "scheduled"){
      var column = Object.values(TD.controller.columnManager.getAll()).find(column => column.model.state.type === "scheduled");

      if (column){
        setTimeout(function(){
          column.reloadTweets();
        }, 1000);
      }
    }
  });

  //
  // Block: Hold Shift to reset cleared column.
  //
  (function(){
    var holdingShift = false;

    var updateShiftState = (pressed) => {
      if (pressed != holdingShift){
        holdingShift = pressed;
        $("button[data-action='clear']").children("span").text(holdingShift ? "Reset" : "Clear");
      }
    };

    var resetActiveFocus = () => {
      document.activeElement.blur();
    };

    $(document).keydown(function(e){
      if (e.shiftKey && (document.activeElement === null || !("value" in document.activeElement))){
        updateShiftState(true);
      }
    }).keyup(function(e){
      if (!e.shiftKey){
        updateShiftState(false);
      }
    });

    TD.vo.Column.prototype.clear = prependToFunction(TD.vo.Column.prototype.clear, function(){
      window.setTimeout(resetActiveFocus, 0); // unfocuses the Clear button, otherwise it steals keyboard input

      if (holdingShift){
        this.model.setClearedTimestamp(0);
        this.reloadTweets();
        return true;
      }
    });
  })();

  //
  // Block: Inject custom CSS and layout into the page.
  //
  (function(){
    var styleOfficial = document.createElement("style");
    document.head.appendChild(styleOfficial);

    styleOfficial.sheet.insertRule("a[data-full-url] { word-break: break-all; }", 0); // break long urls
    styleOfficial.sheet.insertRule(".column-nav-link .attribution { position: absolute; }", 0); // fix cut off account names
    styleOfficial.sheet.insertRule(".txt-base-smallest .badge-verified:before { height: 13px !important; }", 0); // fix cut off badge icon
    styleOfficial.sheet.insertRule(".keyboard-shortcut-list { vertical-align: top; }", 0); // fix keyboard navigation alignment

    if ($TD.hasCustomBrowserCSS){
      var styleCustom = document.createElement("style");
      styleCustom.innerHTML = $TD.customBrowserCSS;
      document.head.appendChild(styleCustom);
    }

    TD.services.TwitterActionRetweetedRetweet.prototype.iconClass = "icon-retweet icon-retweet-color txt-base-medium"; // fix retweet icon mismatch
  })();
})($, $TD, TD);
<|endoftext|>


<commit_before>Ext.define('devilry.jsfiledownload.JsFileDownload', {
    singleton: true,
    baseurl: Ext.String.format('{0}/jsfiledownload/', DevilrySettings.DEVILRY_URLPATH_PREFIX),

    getOpenUrl: function() {
        return this.baseurl + 'open';
    },

    getSaveAsUrl: function() {
        return this.baseurl + 'saveas';
    },

    open: function(content_type, content) {
        this._post_to_url(this.getOpenUrl(), {
            content_type: content_type,
            content: content
        });
    },

    saveas: function(filename, content, content_type) {
        var args = {
            filename: filename,
            content: content
        };
        if(content_type) {
            args.content_type = content_type;
        }
        console.log(args);
        this._post_to_url(this.getSaveAsUrl(), args);
    },

    _post_to_url: function(path, params) { // See: http://stackoverflow.com/questions/133925/javascript-post-request-like-a-form-submit
        var form = document.createElement("form");
        form.setAttribute("method", 'post');
        form.setAttribute("style", 'display: none');
        form.setAttribute("action", path);

        for(var key in params) {
            var hiddenField = document.createElement("input");
            hiddenField.setAttribute("type", "hidden");
            hiddenField.setAttribute("name", key);
            hiddenField.setAttribute("value", params[key]);
            form.appendChild(hiddenField);
        }

        document.body.appendChild(form);
        form.submit();
    }
});
<commit_msg>apps.jsfiledownload: Removed console.log.<commit_after>Ext.define('devilry.jsfiledownload.JsFileDownload', {
    singleton: true,
    baseurl: Ext.String.format('{0}/jsfiledownload/', DevilrySettings.DEVILRY_URLPATH_PREFIX),

    getOpenUrl: function() {
        return this.baseurl + 'open';
    },

    getSaveAsUrl: function() {
        return this.baseurl + 'saveas';
    },

    open: function(content_type, content) {
        this._post_to_url(this.getOpenUrl(), {
            content_type: content_type,
            content: content
        });
    },

    saveas: function(filename, content, content_type) {
        var args = {
            filename: filename,
            content: content
        };
        if(content_type) {
            args.content_type = content_type;
        }
        this._post_to_url(this.getSaveAsUrl(), args);
    },

    _post_to_url: function(path, params) { // See: http://stackoverflow.com/questions/133925/javascript-post-request-like-a-form-submit
        var form = document.createElement("form");
        form.setAttribute("method", 'post');
        form.setAttribute("style", 'display: none');
        form.setAttribute("action", path);

        for(var key in params) {
            var hiddenField = document.createElement("input");
            hiddenField.setAttribute("type", "hidden");
            hiddenField.setAttribute("name", key);
            hiddenField.setAttribute("value", params[key]);
            form.appendChild(hiddenField);
        }

        document.body.appendChild(form);
        form.submit();
    }
});
<|endoftext|>


<commit_before>(function($) {

	var RoomsRoom = this.RoomsRoom = Room.extend({
		minWidth: 320,
		maxWidth: 1024,
		type: 'rooms',
		title: 'Rooms',
		isSideRoom: true,
		events: {
			'click .ilink': 'clickLink'
		},
		initialize: function() {
			this.$el.addClass('ps-room-light').addClass('scrollable');
			app.on('response:rooms', this.update, this);
			app.send('/cmd rooms');
			app.user.on('change:named', this.updateUser, this);
			this.update();
		},
		clickLink: function(e) {
			if (e.cmdKey || e.metaKey || e.ctrlKey) return;
			e.preventDefault();
			e.stopPropagation();
			var roomid = $(e.currentTarget).attr('href').substr(app.root.length);
			app.tryJoinRoom(roomid);
		},
		updateUser: function() {
			this.update();
		},
		focus: function() {
			if (new Date().getTime() - this.lastUpdate > 60*1000) {
				app.send('/cmd rooms');
				this.lastUpdate = new Date().getTime();
			}
			// this.$('button[name=joinRoomPopup]').focus();
		},
		joinRoomPopup: function() {
			app.addPopupPrompt("Room name:", "Join room", function(room) {
				if (!room) return;
				app.tryJoinRoom(room);
			});
		},
		update: function(rooms) {
			if (rooms) {
				this.lastUpdate = new Date().getTime();
				app.roomsData = rooms;
			} else {
				rooms = app.roomsData;
			}
			var buf = '
<button style="float:right" name="close">Close</button>';
			if (!rooms) {
				buf += '
Loading...

';
				this.$el.html(buf);
				return;
			}
			buf += '
';

			if (rooms.userCount) {
				var userCount = Number(rooms.userCount);
				var battleCount = Number(rooms.battleCount);
				buf += '
';
				buf += ''+userCount+' '+(userCount==1?'user':'users')+' online ';
				buf += ''+battleCount+' active '+(battleCount==1?'battle':'battles')+'';
				buf += '
';
			}

			buf += '
Official chat rooms
';
			for (var i=0; i<rooms.official.length; i++) {
				var roomData = rooms.official[i];
				var id = toId(roomData.title);
				buf += '
(' + Number(roomData.userCount) + ' users) ' + Tools.escapeHTML(roomData.title) + '
' + Tools.escapeHTML(roomData.desc||'')+ '
';
			}

			buf += '
Chat rooms
';
			rooms.chat.sort(function(a, b) {
				return b.userCount - a.userCount;
			});
			for (var i=0; i<rooms.chat.length; i++) {
				var roomData = rooms.chat[i];
				var id = toId(roomData.title);
				var escapedDesc = Tools.escapeHTML(roomData.desc||'');
				buf += '
(' + Number(roomData.userCount) + ' users) ' + Tools.escapeHTML(roomData.title) + '
' + escapedDesc + '
';
			}

			buf += '
<button name="joinRoomPopup">Join other room</button>
'

			buf += '

';
			this.$el.html(buf);
		}
	});

}).call(this, jQuery);
<commit_msg>Use id of room in joinRoomPrompt<commit_after>(function($) {

	var RoomsRoom = this.RoomsRoom = Room.extend({
		minWidth: 320,
		maxWidth: 1024,
		type: 'rooms',
		title: 'Rooms',
		isSideRoom: true,
		events: {
			'click .ilink': 'clickLink'
		},
		initialize: function() {
			this.$el.addClass('ps-room-light').addClass('scrollable');
			app.on('response:rooms', this.update, this);
			app.send('/cmd rooms');
			app.user.on('change:named', this.updateUser, this);
			this.update();
		},
		clickLink: function(e) {
			if (e.cmdKey || e.metaKey || e.ctrlKey) return;
			e.preventDefault();
			e.stopPropagation();
			var roomid = $(e.currentTarget).attr('href').substr(app.root.length);
			app.tryJoinRoom(roomid);
		},
		updateUser: function() {
			this.update();
		},
		focus: function() {
			if (new Date().getTime() - this.lastUpdate > 60*1000) {
				app.send('/cmd rooms');
				this.lastUpdate = new Date().getTime();
			}
			// this.$('button[name=joinRoomPopup]').focus();
		},
		joinRoomPopup: function() {
			app.addPopupPrompt("Room name:", "Join room", function(room) {
				room = toId(room);
				if (!room) return;
				app.tryJoinRoom(room);
			});
		},
		update: function(rooms) {
			if (rooms) {
				this.lastUpdate = new Date().getTime();
				app.roomsData = rooms;
			} else {
				rooms = app.roomsData;
			}
			var buf = '
<button style="float:right" name="close">Close</button>';
			if (!rooms) {
				buf += '
Loading...

';
				this.$el.html(buf);
				return;
			}
			buf += '
';

			if (rooms.userCount) {
				var userCount = Number(rooms.userCount);
				var battleCount = Number(rooms.battleCount);
				buf += '
';
				buf += ''+userCount+' '+(userCount==1?'user':'users')+' online ';
				buf += ''+battleCount+' active '+(battleCount==1?'battle':'battles')+'';
				buf += '
';
			}

			buf += '
Official chat rooms
';
			for (var i=0; i<rooms.official.length; i++) {
				var roomData = rooms.official[i];
				var id = toId(roomData.title);
				buf += '
(' + Number(roomData.userCount) + ' users) ' + Tools.escapeHTML(roomData.title) + '
' + Tools.escapeHTML(roomData.desc||'')+ '
';
			}

			buf += '
Chat rooms
';
			rooms.chat.sort(function(a, b) {
				return b.userCount - a.userCount;
			});
			for (var i=0; i<rooms.chat.length; i++) {
				var roomData = rooms.chat[i];
				var id = toId(roomData.title);
				var escapedDesc = Tools.escapeHTML(roomData.desc||'');
				buf += '
(' + Number(roomData.userCount) + ' users) ' + Tools.escapeHTML(roomData.title) + '
' + escapedDesc + '
';
			}

			buf += '
<button name="joinRoomPopup">Join other room</button>
'

			buf += '

';
			this.$el.html(buf);
		}
	});

}).call(this, jQuery);
<|endoftext|>


<commit_before>'use strict'

const express = require('express')
const Slapp = require('slapp')
const ConvoStore = require('slapp-convo-beepboop')
const Context = require('slapp-context-beepboop')
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;

// use `PORT` env var on Beep Boop - default to 3000 locally
var port = process.env.PORT || 3000

var slapp = Slapp({
  // Beep Boop sets the SLACK_VERIFY_TOKEN env var
  verify_token: process.env.SLACK_VERIFY_TOKEN,
  convo_store: ConvoStore(),
  context: Context()
})

var HELP_TEXT = `
I will respond to the following messages 😎:
\`help\` - to see this message.
\`hi\` - to demonstrate a conversation that tracks state.
\`thanks\` - to demonstrate a simple response.
\`<type-any-other-text>\` - to demonstrate a random emoticon response, some of the time :wink:.
\`attachment\` - to see a Slack attachment message.
`

//*********************************************
// Setup different handlers for messages
//*********************************************

// response to the user typing "help"
function getReactionGif(msg){
	// msg.say("Fetching Reaction Gifs...");
	var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function(){
        if (xhr.readyState == 4 && xhr.status == 200){
            // console.log(xhr.responseText);
			var jsonResponse = JSON.parse(xhr.responseText);
			// console.log(jsonResponse);
			// msg.say(jsonResponse.data.image_url);

			msg.say({
		      text: '',
		      attachments: [{
		   	 text: '',
		   	 title: '',
			 //  	 image_url: jsonResponse.data.image_url,
		   	 image_url: jsonResponse.data.fixed_height_downsampled_url,
		   	 title_link: '',
		   	 color: ''
		      }]
		    })

			// msg.say(jsonResponse.data.url);

        }
    }
    xhr.open("GET", "http://api.giphy.com/v1/gifs/random?api_key=dc6zaTOxFJmzC&tag=reaction", true);
    xhr.send();

}
// slapp.event('file_shared', (msg) => {
//
// 	msg.say(":thumbsup:");
// 	getReactionGif(msg);
//
// })

// add a smile reaction by the bot for any message reacted to
slapp.event('file_shared', (msg) => {
	// console.log(msg);
	// msg.say(":thumbsup:");

	let token = msg.meta.bot_token
	let id = msg.body.event.event_ts
	let file = msg.body.event.file_id
	// let channel = msg.body.event.item.channel

	console.log(token+" | "+id+" | "+file);

	slapp.client.reactions.add({token, 'smile', file}, (err, data) => {
		console.log(err);
	})

	// slapp.client.reactions.add({token, 'smile',  file, '', '', ts });
})

slapp.message('help', ['mention', 'direct_message'], (msg) => {

	msg.say(HELP_TEXT);
})

// Catch all mentions of the bot
slapp.message('', ['mention'], (msg) => {
  // respond only 40% of the time

	// console.log("detected me");
	// msg.say("What?");
	getReactionGif(msg);
})

// slapp.message('Robert', ['mention', 'direct_message'], (msg) => {
//   msg.say('What a dick!')
// })

// slapp.message('jens', ['mention', 'direct_message'], (msg) => {
//   msg.say('What a dick!')
// })

slapp.message(/^(Ralf|Jens|Robert|Frank|Isa|Jan|Daniel)/i, ['mention', 'direct_message'], (msg) => {
	// var text = (msg.body.event && msg.body.event.text) || '';
	msg.say([
      '🚀',
      '💩',
      ':+1: That\'s my man',
      'Best colleague :sun_with_face: :full_moon_with_face:'
    ])
})

// "Conversation" flow that tracks state - kicks off when user says hi, hello or hey
slapp
  .message('^(hi|hello|hey)$', ['direct_mention', 'direct_message'], (msg, text) => {
    msg
      .say(`${text}, how are you?`)
      // sends next event from user to this route, passing along state
      .route('how-are-you', { greeting: text })
  })
  .route('how-are-you', (msg, state) => {
    var text = (msg.body.event && msg.body.event.text) || ''

    // user may not have typed text as their next action, ask again and re-route
    if (!text) {
      return msg
        .say("Whoops, I'm still waiting to hear how you're doing.")
        .say('How are you?')
        .route('how-are-you', state)
    }

    // add their response to state
    state.status = text

    msg
      .say(`Ok then. What's your favorite color?`)
      .route('color', state)
  })
  .route('color', (msg, state) => {
    var text = (msg.body.event && msg.body.event.text) || ''

    // user may not have typed text as their next action, ask again and re-route
    if (!text) {
      return msg
        .say("I'm eagerly awaiting to hear your favorite color.")
        .route('color', state)
    }

    // add their response to state
    state.color = text

    msg
      .say('Thanks for sharing.')
      .say(`Here's what you've told me so far: \`\`\`${JSON.stringify(state)}\`\`\``)
    // At this point, since we don't route anywhere, the "conversation" is over
  })

// Can use a regex as well
slapp.message(/^(thanks|thank you)/i, ['mention', 'direct_message'], (msg) => {
  // You can provide a list of responses, and a random one will be chosen
  // You can also include slack emoji in your responses
  msg.say([
    "You're welcome :smile:",
    'You bet',
    ':+1: Of course',
    'Anytime :sun_with_face: :full_moon_with_face:'
  ])
})

// demonstrate returning an attachment...
slapp.message('attachment', ['mention', 'direct_message'], (msg) => {
  msg.say({
    text: 'Check out this amazing attachment! :confetti_ball: ',
    attachments: [{
      text: 'Slapp is a robust open source library that sits on top of the Slack APIs',
      title: 'Slapp Library - Open Source',
      image_url: 'https://storage.googleapis.com/beepboophq/_assets/bot-1.22f6fb.png',
      title_link: 'https://beepboophq.com/',
      color: '#7CD197'
    }]
  })
})

// Catch-all for any other responses not handled above
slapp.message('.*', ['direct_message','direct_mention'], (msg) => {
  // respond only 40% of the time

	if (Math.random() < 0.4) {
		msg.say([':wave:', ':pray:', ':raised_hands:', ':kissing_heart:',':monkey:'])
	}else{
		// getReactionGif(msg);
	}
})

// attach Slapp to express server
var server = slapp.attachToExpress(express())

// start http server
server.listen(port, (err) => {
  if (err) {
    return console.error(err)
  }
  console.log(`Listening on port ${port}`)
})
<commit_msg>bugfix reaction<commit_after>'use strict'

const express = require('express')
const Slapp = require('slapp')
const ConvoStore = require('slapp-convo-beepboop')
const Context = require('slapp-context-beepboop')
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;

// use `PORT` env var on Beep Boop - default to 3000 locally
var port = process.env.PORT || 3000

var slapp = Slapp({
  // Beep Boop sets the SLACK_VERIFY_TOKEN env var
  verify_token: process.env.SLACK_VERIFY_TOKEN,
  convo_store: ConvoStore(),
  context: Context()
})

var HELP_TEXT = `
I will respond to the following messages 😎:
\`help\` - to see this message.
\`hi\` - to demonstrate a conversation that tracks state.
\`thanks\` - to demonstrate a simple response.
\`<type-any-other-text>\` - to demonstrate a random emoticon response, some of the time :wink:.
\`attachment\` - to see a Slack attachment message.
`

//*********************************************
// Setup different handlers for messages
//*********************************************

// response to the user typing "help"
function getReactionGif(msg){
	// msg.say("Fetching Reaction Gifs...");
	var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function(){
        if (xhr.readyState == 4 && xhr.status == 200){
            // console.log(xhr.responseText);
			var jsonResponse = JSON.parse(xhr.responseText);
			// console.log(jsonResponse);
			// msg.say(jsonResponse.data.image_url);

			msg.say({
		      text: '',
		      attachments: [{
		   	 text: '',
		   	 title: '',
			 //  	 image_url: jsonResponse.data.image_url,
		   	 image_url: jsonResponse.data.fixed_height_downsampled_url,
		   	 title_link: '',
		   	 color: ''
		      }]
		    })

			// msg.say(jsonResponse.data.url);

        }
    }
    xhr.open("GET", "http://api.giphy.com/v1/gifs/random?api_key=dc6zaTOxFJmzC&tag=reaction", true);
    xhr.send();

}
// slapp.event('file_shared', (msg) => {
//
// 	msg.say(":thumbsup:");
// 	getReactionGif(msg);
//
// })

// add a smile reaction by the bot for any message reacted to
slapp.event('file_shared', (msg) => {
	// console.log(msg);
	// msg.say(":thumbsup:");

	let token = msg.meta.bot_token
	let id = msg.body.event.event_ts
	let file = msg.body.event.file_id
	// let channel = msg.body.event.item.channel+
	let reaction_name = 'smile';

	console.log(token+" | "+id+" | "+file);

	slapp.client.reactions.add({token, reaction_name , file }, (err, data) => {
		console.log(err);
	})

	// slapp.client.reactions.add({token, 'smile',  file, '', '', ts });
})

slapp.message('help', ['mention', 'direct_message'], (msg) => {

	msg.say(HELP_TEXT);
})

// Catch all mentions of the bot
slapp.message('', ['mention'], (msg) => {
  // respond only 40% of the time

	// console.log("detected me");
	// msg.say("What?");
	getReactionGif(msg);
})

// slapp.message('Robert', ['mention', 'direct_message'], (msg) => {
//   msg.say('What a dick!')
// })

// slapp.message('jens', ['mention', 'direct_message'], (msg) => {
//   msg.say('What a dick!')
// })

slapp.message(/^(Ralf|Jens|Robert|Frank|Isa|Jan|Daniel)/i, ['mention', 'direct_message'], (msg) => {
	// var text = (msg.body.event && msg.body.event.text) || '';
	msg.say([
      '🚀',
      '💩',
      ':+1: That\'s my man',
      'Best colleague :sun_with_face: :full_moon_with_face:'
    ])
})

// "Conversation" flow that tracks state - kicks off when user says hi, hello or hey
slapp
  .message('^(hi|hello|hey)$', ['direct_mention', 'direct_message'], (msg, text) => {
    msg
      .say(`${text}, how are you?`)
      // sends next event from user to this route, passing along state
      .route('how-are-you', { greeting: text })
  })
  .route('how-are-you', (msg, state) => {
    var text = (msg.body.event && msg.body.event.text) || ''

    // user may not have typed text as their next action, ask again and re-route
    if (!text) {
      return msg
        .say("Whoops, I'm still waiting to hear how you're doing.")
        .say('How are you?')
        .route('how-are-you', state)
    }

    // add their response to state
    state.status = text

    msg
      .say(`Ok then. What's your favorite color?`)
      .route('color', state)
  })
  .route('color', (msg, state) => {
    var text = (msg.body.event && msg.body.event.text) || ''

    // user may not have typed text as their next action, ask again and re-route
    if (!text) {
      return msg
        .say("I'm eagerly awaiting to hear your favorite color.")
        .route('color', state)
    }

    // add their response to state
    state.color = text

    msg
      .say('Thanks for sharing.')
      .say(`Here's what you've told me so far: \`\`\`${JSON.stringify(state)}\`\`\``)
    // At this point, since we don't route anywhere, the "conversation" is over
  })

// Can use a regex as well
slapp.message(/^(thanks|thank you)/i, ['mention', 'direct_message'], (msg) => {
  // You can provide a list of responses, and a random one will be chosen
  // You can also include slack emoji in your responses
  msg.say([
    "You're welcome :smile:",
    'You bet',
    ':+1: Of course',
    'Anytime :sun_with_face: :full_moon_with_face:'
  ])
})

// demonstrate returning an attachment...
slapp.message('attachment', ['mention', 'direct_message'], (msg) => {
  msg.say({
    text: 'Check out this amazing attachment! :confetti_ball: ',
    attachments: [{
      text: 'Slapp is a robust open source library that sits on top of the Slack APIs',
      title: 'Slapp Library - Open Source',
      image_url: 'https://storage.googleapis.com/beepboophq/_assets/bot-1.22f6fb.png',
      title_link: 'https://beepboophq.com/',
      color: '#7CD197'
    }]
  })
})

// Catch-all for any other responses not handled above
slapp.message('.*', ['direct_message','direct_mention'], (msg) => {
  // respond only 40% of the time

	if (Math.random() < 0.4) {
		msg.say([':wave:', ':pray:', ':raised_hands:', ':kissing_heart:',':monkey:'])
	}else{
		// getReactionGif(msg);
	}
})

// attach Slapp to express server
var server = slapp.attachToExpress(express())

// start http server
server.listen(port, (err) => {
  if (err) {
    return console.error(err)
  }
  console.log(`Listening on port ${port}`)
})
<|endoftext|>


<commit_before>// add an event listener to the shorten button for when the user clicks it
$('.btn-shorten').on('click', function(){
  // AJAX call to /api/shorten with the URL that the user entered in the input box
  $.ajax({
    url: '/api/urlshorten',
    type: 'POST',
    headers: {
        'Content-Type':'application/json'
    },
    dataType: 'JSON',
    data: {
      'url': $('#url-field').val()
    },
    success: function(data){
        // display the shortened URL to the user that is returned by the server
        var resultHTML = ''
            + data.shortUrl + '';
        $('#link').html(resultHTML);
        $('#link').hide().fadeIn('slow');
    },
    error: function (textStatus, errorThrown) {
        console.log(textStatus, errorThrown);
    }
  });

});<commit_msg>Updated Code<commit_after>// add an event listener to the shorten button for when the user clicks it
$('.btn-shorten').on('click', function(){
  // AJAX call to /api/shorten with the URL that the user entered in the input box
  $.ajax({
    url: '/api/urlshorten',
    type: 'POST',
    headers: {
        'Content-Type':'application/json'
    },
    dataType: 'JSON',
    data: {
      url: $('#url-field').val()
    },
    success: function(data){
        // display the shortened URL to the user that is returned by the server
        var resultHTML = ''
            + data.shortUrl + '';
        $('#link').html(resultHTML);
        $('#link').hide().fadeIn('slow');
    },
    error: function (textStatus, errorThrown) {
        console.log(textStatus, errorThrown);
    }
  });

});<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var User = require('../models/user.js'),
  mongoose = require('../models/user.js'),
  utils = require('./utils.js'),
  _ = require('lodash');

var options = {
  includes: 'assign watchers project',
  defaults: {
    project: undefined,
    assign: undefined,
    watchers: []
  }
};

var crud = require('../controllers/crud.js');
var user = crud('users', options);

Object.keys(user).forEach(function(methodName) {
  exports[methodName] = user[methodName];
});

exports.filterProperties = function(req, res, next) {
  if(req.locals.error) {
    return next();
  }
  req.body = _.pick(req.body, ['name', 'username', 'password', 'email', 'profile', 'GetMailEveryWeekAboutMyTasks', 'GetMailEveryWeekAboutGivenTasks', 'GetMailEveryDayAboutMyTasks']);
  next();
};

exports.getByEntity = function(req, res, next) { //It is a temporary function. need to change this function to use elasticsearch!!!!
  res.status(200);
  return res.json([]);

// marking in comment below - because there is return statement above
  // var query = {
  //   _id: req.params.id
  // };
  // var model = (req.params.entity.charAt(0).toUpperCase() + req.params.entity.slice(1)).substring(0, req.params.entity.length - 1);
  // var Query = mongoose.model(model).findOne(query);
  // Query.exec(function(err, project) {
  //   if(err || !project) utils.checkAndHandleError(err ? err : !project, 'Failed to load project with id: ' + req.params.id, next);
  //   else {
  //     var userIds = project.watchers;
  //     userIds.push(project.creator);
  //     User.find({
  //       _id: {
  //         $in: userIds
  //       }
  //     }).exec(function(err, users) {
  //       res.status(200);
  //       return res.json(users);
  //     });
  //   }
  // });
};
<commit_msg>fixed bug with saving new email and name of user in profile modal<commit_after>'use strict';

/**
 * Module dependencies.
 */
var User = require('../models/user.js'),
  permissions = require('./permissions.js'),
  _ = require('lodash');

var options = {
  includes: 'assign watchers project',
  defaults: {
    project: undefined,
    assign: undefined,
    watchers: []
  }
};

var crud = require('../controllers/crud.js');
var user = crud('users', options);

Object.keys(user).forEach(function(methodName) {
  exports[methodName] = user[methodName];
});

exports.update = function(req, res, next) {
  if(req.user.username !== req.body.username){
    return throwError(permissions.permError.denied + ":" + permissions.permError.allowUpdateWatcher) ;
  }

  User.findById(req.user._id, function (err, user) {
    if (err){
      return console.error('Error: ', err);
    }

    user.set(_.pick(req.body, ['name', 'email']));
    user.save(function (err, updatedUser) {
      if (err){
        return console.error('Error: ', err);
      }
      res.send(updatedUser);
    });
  });
};

exports.filterProperties = function(req, res, next) {
  if(req.locals.error) {
    return next();
  }
  req.body = _.pick(req.body, ['name', 'username', 'password', 'email', 'profile', 'GetMailEveryWeekAboutMyTasks', 'GetMailEveryWeekAboutGivenTasks', 'GetMailEveryDayAboutMyTasks']);
  next();
};

exports.getByEntity = function(req, res, next) { //It is a temporary function. need to change this function to use elasticsearch!!!!
  res.status(200);
  return res.json([]);

// marking in comment below - because there is return statement above
  // var query = {
  //   _id: req.params.id
  // };
  // var model = (req.params.entity.charAt(0).toUpperCase() + req.params.entity.slice(1)).substring(0, req.params.entity.length - 1);
  // var Query = mongoose.model(model).findOne(query);
  // Query.exec(function(err, project) {
  //   if(err || !project) utils.checkAndHandleError(err ? err : !project, 'Failed to load project with id: ' + req.params.id, next);
  //   else {
  //     var userIds = project.watchers;
  //     userIds.push(project.creator);
  //     User.find({
  //       _id: {
  //         $in: userIds
  //       }
  //     }).exec(function(err, users) {
  //       res.status(200);
  //       return res.json(users);
  //     });
  //   }
  // });
};

function throwError(err) {
  let deffered = q.defer();
  deffered.reject(err);
  return deffered.promise;
}
<|endoftext|>


<commit_before>import env from '../env'
import originId from '../originId'

const defaultOptions = {
  method: 'GET',
  credentials: 'include'
}

const getOrCreateHeaders = optionsHeader => {
  if (optionsHeader) {
    if (optionsHeader instanceof Headers) {
      return optionsHeader
    } else if (typeof optionsHeader === 'object') {
      return new Headers(optionsHeader)
    }
  }
  return new Headers()
}

export const prepareHeaders = options => {
  const headers = getOrCreateHeaders(options.headers)
  const body = options.body

  if (!headers.has('Content-Type')) {
    if (typeof body === 'string') {
      headers.set('Content-Type', 'text/plain')
    } else if (!(body instanceof FormData)) {
      headers.set('Content-Type', 'application/json')
    }
  }

  if (!headers.has('X-Origin-Id')) {
    headers.set('X-Origin-Id', originId.getOriginId())
  }

  if (!headers.has('X-Business-Unit') && env.getBusinessUnit()) {
    headers.set('X-Business-Unit', env.getBusinessUnit())
  }

  return headers
}

export const prepareUrl = (backendUrl, resource) => {
  if (resource.startsWith('/')) {
    resource = resource.substring(1)
  }

  if (resource.startsWith('http')) {
    return resource
  } else if (resource.startsWith('nice2')) {
    return `${backendUrl}/${resource}`
  } else {
    return `${backendUrl}/nice2/${resource}`
  }
}

export const prepareOptions = options => ({
  ...defaultOptions,
  ...options,
  headers: prepareHeaders(options)
})

export const executeRequest = (resource, options = {}) => {
  const fetchOptions = prepareOptions(options)
  const backendUrl = env.getBackendUrl()
  const url = prepareUrl(backendUrl, resource)

  return fetch(url, fetchOptions)
}

export const extractBody = response => response.json()
<commit_msg>feat(app-extensions): set custom client type for rest requests<commit_after>import env from '../env'
import originId from '../originId'

const defaultOptions = {
  method: 'GET',
  credentials: 'include'
}

const getOrCreateHeaders = optionsHeader => {
  if (optionsHeader) {
    if (optionsHeader instanceof Headers) {
      return optionsHeader
    } else if (typeof optionsHeader === 'object') {
      return new Headers(optionsHeader)
    }
  }
  return new Headers()
}

export const prepareHeaders = options => {
  const headers = getOrCreateHeaders(options.headers)
  const body = options.body

  if (!headers.has('Content-Type')) {
    if (typeof body === 'string') {
      headers.set('Content-Type', 'text/plain')
    } else if (!(body instanceof FormData)) {
      headers.set('Content-Type', 'application/json')
    }
  }

  if (!headers.has('X-Origin-Id')) {
    headers.set('X-Origin-Id', originId.getOriginId())
  }

  if (!headers.has('X-Business-Unit') && env.getBusinessUnit()) {
    headers.set('X-Business-Unit', env.getBusinessUnit())
  }

  if (!headers.has('X-Client')) {
    headers.set('X-Client', 'client')
  }

  return headers
}

export const prepareUrl = (backendUrl, resource) => {
  if (resource.startsWith('/')) {
    resource = resource.substring(1)
  }

  if (resource.startsWith('http')) {
    return resource
  } else if (resource.startsWith('nice2')) {
    return `${backendUrl}/${resource}`
  } else {
    return `${backendUrl}/nice2/${resource}`
  }
}

export const prepareOptions = options => ({
  ...defaultOptions,
  ...options,
  headers: prepareHeaders(options)
})

export const executeRequest = (resource, options = {}) => {
  const fetchOptions = prepareOptions(options)
  const backendUrl = env.getBackendUrl()
  const url = prepareUrl(backendUrl, resource)

  return fetch(url, fetchOptions)
}

export const extractBody = response => response.json()
<|endoftext|>


<commit_before><commit_msg>Update trainer-cards.js<commit_after><|endoftext|>


<commit_before>"use strict";

(function () {
    angular
        .module("argo")
        .controller("Plugins", Plugins);

    Plugins.$inject = ["pluginsService"];
    function Plugins(pluginsService) {
        var vm = this;

        vm.engage = engage;

        activate();

        function activate() {
            pluginsService.getPlugins().then(function (plugins) {
                vm.plugins = plugins;
            });
        }

        function engage() {
            console.log(vm.plugins);
        }
    }

}());
<commit_msg>Add engage method to plugins view.<commit_after>"use strict";

(function () {
    angular
        .module("argo")
        .controller("Plugins", Plugins);

    Plugins.$inject = ["pluginsService"];
    function Plugins(pluginsService) {
        var vm = this;

        vm.engage = engage;

        activate();

        function activate() {
            pluginsService.getPlugins().then(function (plugins) {
                vm.plugins = plugins;
                vm.noPlugins = Object.keys(plugins).length === 0;
            });
        }

        function engage() {
            pluginsService.engagePlugins(vm.plugins);
        }
    }

}());
<|endoftext|>


<commit_before>/**
 * Created by Rajinda on 8/3/2015.
 */

var xlsx2json = require("xlsx2json");
var logger = require('DVP-Common/LogHandler/CommonLogHandler.js').logger;
var DbConn = require('DVP-DBModels');
var messageFormatter = require('DVP-Common/CommonMessageGenerator/ClientMessageJsonFormatter.js');
var readline = require('readline');

var rl = readline.createInterface(process.stdin, process.stdout);
var filePath = "";
var dataStartingRow = "";
console.log('Press "exit" to exit or d to start debugging');
rl.prompt();
var request = require('request');
var http = require('http');
http.post = require('http-post');

rl.on('line', function (line) {

try {
    if (line == "exit") {

    }
    else if (line == "u") {

        rl.question("\n Enter File Path \n", function (path) {

            console.log(path);
            filePath = path;
            rl.question("\n Enter data Starting Row \n", function (row) {
                console.log(row);
                dataStartingRow = row;
                rl.question("\n Enter campaignId \n", function (camId) {
                    console.log(camId);
                    rl.question("\n Enter camScheduleId \n", function (camsId) {
                        console.log(camsId);
                        rl.question("\n Enter tenantId \n", function (tenantId) {
                            console.log(tenantId);
                            rl.question("\n Enter companyId \n", function (companyId) {
                                console.log(companyId);
                                rl.question("\n Enter categoryID \n", function (categoryID) {
                                    console.log(categoryID);
                                    ReadFile(filePath, row, camId, camsId, tenantId, companyId, categoryID)
                                });
                            });
                        });
                    });
                });
            });
        });
    }
    else {
        switch (argsNum) {
            case 1:
                //console.log('\n Enter Application ID \n');
                //rl.prompt();
                AppID = line;
                console.log("App Id is : " + AppID);
                argsNum++;
                console.log('\n Enter Caller-Direction : ');
                rl.prompt();
                break;
        }
    }
}
catch (ex) {
    console.log("Error");
    logger.error('[DVP-campaignmanager] - [HTTP]  - Exception occurred -  Data - %s ', "authorization", ex);
}
}).on('close', function () {
    logger.debug('[DVP-HTTPProgrammingAPIDEBUG] - [%s] - [READLINE] - Read line closed ', reqIdX);
    process.exit(0);
});

function ReadFile(path, row,campaignId, camScheduleId, tenantId, companyId, categoryID) {
    try{
        console.log("Strat........................");

    xlsx2json(
        path,
        {
            dataStartingRow: row,
            mapping: {
                "No": "A"
            }
        }).done(function (jsonArray) {

            console.log(jsonArray);
            console.log(jsonArray.length);

            var jsonData = [];
            for (var i = 0, length = jsonArray.length; i < length; i++) {
                //console.log(jsonArray[i].No)
                jsonData.push('"'+jsonArray[i].No+'"');
            }

            console.log(jsonData);
            console.log("-------------------------Post--------------------------------");
            var data = '{"CampaignId":"'+campaignId+'","CamScheduleId":"'+camScheduleId+'","CategoryID":"'+categoryID+'","Contacts":['+jsonData+']}';
            console.log(data);
           /* http.post('http://localhost:2222/DVP/API/6.0/CampaignManager/Campaign/Numbers', data, function(res){
                console.log(res);
            });
*/

            //Lets configure and request
            request({
                url: 'http://localhost:2222/DVP/API/6.0/CampaignManager/Campaign/Numbers', //URL to hit
                qs: {from: 'blog example', time: +new Date()}, //Query string data
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Custom-Header': 'Custom Value'
                },
                body: data //Set the body as a string
            }, function(error, response, body){
                if(error) {
                    console.log(error);
                } else {
                    console.log(response.statusCode, body);
                    console.log("-------------------------Post-Done--------------------------------");
                }
            });
            //UploadContactsToCampaignWithSchedule(jsonData, campaignId, camScheduleId, tenantId, companyId, categoryID);
        });}
    catch (ex) {
        console.log("Error");
        logger.error('[DVP-campaignmanager] - [HTTP]  - Exception occurred -  Data - %s ', "authorization", ex);
    }
}

function UploadContactsToCampaignWithSchedule(contacts, campaignId, camScheduleId, tenantId, companyId, categoryID) {

    var ids = [];
    var j = 0;
    for (var i = 0; i < contacts.length; i++) {

        DbConn.CampContactInfo
            .create(
            {
                ContactId: contacts[i],
                Status: true,
                TenantId: tenantId,
                CompanyId: companyId,
                CategoryID: categoryID
            }
        ).complete(function (err, cmp) {
                j++;
                if (err) {

                    logger.error('[DVP-CampContactInfo.UploadContactsToCampaignWithSchedule] - [%s] - [PGSQL] - insertion[CampContactInfo]  failed- [%s]', contacts[j - 1], err);
                    ids.push(contacts[j - 1]);
                }
                else {
                    logger.info('[DVP-CampContactInfo.UploadContactsToCampaignWithSchedule] - [%s] - [PGSQL] - inserted[CampContactInfo] successfully ', contacts[j - 1]);

                    DbConn.CampContactSchedule
                        .create(
                        {
                            CampaignId: campaignId,
                            CamContactId: cmp.CamContactId,
                            CamScheduleId: camScheduleId
                        }
                    ).complete(function (err, cmp) {

                            if (err) {

                                logger.error('[DVP-CampContactInfo.UploadContactsToCampaignWithSchedule] - [%s] - [PGSQL] - insertion[CampContactSchedule]  failed- [%s]', contacts[j - 1], err);
                                ids.push(contacts[j - 1]);
                            }
                            else {
                                logger.info('[DVP-CampaignNumberUpload.UploadContactsToCampaignWithSchedule] - [%s] - [PGSQL] - inserted[CampContactSchedule] successfully ', contacts[j - 1]);
                            }

                        });
                }

                if (j >= contacts.length) {
                    var msg = undefined;
                    if (ids.length > 0) {
                        msg = new Error("Validation Error");
                    }
                    var jsonString = messageFormatter.FormatMessage(msg, "OPERATIONS COMPLETED", ids.length == 0, ids);
                    console.log("jsonString");
                }
            });

    }

}
<commit_msg>Update app.js<commit_after>/**
 * Created by Rajinda on 8/3/2015.
 */

var xlsx2json = require("xlsx2json");
var logger = require('DVP-Common/LogHandler/CommonLogHandler.js').logger;
var DbConn = require('db-models');
var messageFormatter = require('DVP-Common/CommonMessageGenerator/ClientMessageJsonFormatter.js');
var readline = require('readline');

var rl = readline.createInterface(process.stdin, process.stdout);
var filePath = "";
var dataStartingRow = "";
console.log('Press "exit" to exit or d to start debugging');
rl.prompt();
var request = require('request');
var http = require('http');
http.post = require('http-post');

rl.on('line', function (line) {

try {
    if (line == "exit") {

    }
    else if (line == "u") {

        rl.question("\n Enter File Path \n", function (path) {

            console.log(path);
            filePath = path;
            rl.question("\n Enter data Starting Row \n", function (row) {
                console.log(row);
                dataStartingRow = row;
                rl.question("\n Enter campaignId \n", function (camId) {
                    console.log(camId);
                    rl.question("\n Enter camScheduleId \n", function (camsId) {
                        console.log(camsId);
                        rl.question("\n Enter tenantId \n", function (tenantId) {
                            console.log(tenantId);
                            rl.question("\n Enter companyId \n", function (companyId) {
                                console.log(companyId);
                                rl.question("\n Enter categoryID \n", function (categoryID) {
                                    console.log(categoryID);
                                    ReadFile(filePath, row, camId, camsId, tenantId, companyId, categoryID)
                                });
                            });
                        });
                    });
                });
            });
        });
    }
    else {
        switch (argsNum) {
            case 1:
                //console.log('\n Enter Application ID \n');
                //rl.prompt();
                AppID = line;
                console.log("App Id is : " + AppID);
                argsNum++;
                console.log('\n Enter Caller-Direction : ');
                rl.prompt();
                break;
        }
    }
}
catch (ex) {
    console.log("Error");
    logger.error('[DVP-campaignmanager] - [HTTP]  - Exception occurred -  Data - %s ', "authorization", ex);
}
}).on('close', function () {
    logger.debug('[DVP-HTTPProgrammingAPIDEBUG] - [%s] - [READLINE] - Read line closed ', reqIdX);
    process.exit(0);
});

function ReadFile(path, row,campaignId, camScheduleId, tenantId, companyId, categoryID) {
    try{
        console.log("Strat........................");

    xlsx2json(
        path,
        {
            dataStartingRow: row,
            mapping: {
                "No": "A"
            }
        }).done(function (jsonArray) {

            console.log(jsonArray);
            console.log(jsonArray.length);

            var jsonData = [];
            for (var i = 0, length = jsonArray.length; i < length; i++) {
                //console.log(jsonArray[i].No)
                jsonData.push('"'+jsonArray[i].No+'"');
            }

            console.log(jsonData);
            console.log("-------------------------Post--------------------------------");
            var data = '{"CampaignId":"'+campaignId+'","CamScheduleId":"'+camScheduleId+'","CategoryID":"'+categoryID+'","Contacts":['+jsonData+']}';
            console.log(data);
           /* http.post('http://localhost:2222/DVP/API/6.0/CampaignManager/Campaign/Numbers', data, function(res){
                console.log(res);
            });
*/

            //Lets configure and request
            request({
                url: 'http://localhost:2222/DVP/API/6.0/CampaignManager/Campaign/Numbers', //URL to hit
                qs: {from: 'blog example', time: +new Date()}, //Query string data
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Custom-Header': 'Custom Value'
                },
                body: data //Set the body as a string
            }, function(error, response, body){
                if(error) {
                    console.log(error);
                } else {
                    console.log(response.statusCode, body);
                    console.log("-------------------------Post-Done--------------------------------");
                }
            });
            //UploadContactsToCampaignWithSchedule(jsonData, campaignId, camScheduleId, tenantId, companyId, categoryID);
        });}
    catch (ex) {
        console.log("Error");
        logger.error('[DVP-campaignmanager] - [HTTP]  - Exception occurred -  Data - %s ', "authorization", ex);
    }
}

function UploadContactsToCampaignWithSchedule(contacts, campaignId, camScheduleId, tenantId, companyId, categoryID) {

    var ids = [];
    var j = 0;
    for (var i = 0; i < contacts.length; i++) {

        DbConn.CampContactInfo
            .create(
            {
                ContactId: contacts[i],
                Status: true,
                TenantId: tenantId,
                CompanyId: companyId,
                CategoryID: categoryID
            }
        ).complete(function (err, cmp) {
                j++;
                if (err) {

                    logger.error('[DVP-CampContactInfo.UploadContactsToCampaignWithSchedule] - [%s] - [PGSQL] - insertion[CampContactInfo]  failed- [%s]', contacts[j - 1], err);
                    ids.push(contacts[j - 1]);
                }
                else {
                    logger.info('[DVP-CampContactInfo.UploadContactsToCampaignWithSchedule] - [%s] - [PGSQL] - inserted[CampContactInfo] successfully ', contacts[j - 1]);

                    DbConn.CampContactSchedule
                        .create(
                        {
                            CampaignId: campaignId,
                            CamContactId: cmp.CamContactId,
                            CamScheduleId: camScheduleId
                        }
                    ).complete(function (err, cmp) {

                            if (err) {

                                logger.error('[DVP-CampContactInfo.UploadContactsToCampaignWithSchedule] - [%s] - [PGSQL] - insertion[CampContactSchedule]  failed- [%s]', contacts[j - 1], err);
                                ids.push(contacts[j - 1]);
                            }
                            else {
                                logger.info('[DVP-CampaignNumberUpload.UploadContactsToCampaignWithSchedule] - [%s] - [PGSQL] - inserted[CampContactSchedule] successfully ', contacts[j - 1]);
                            }

                        });
                }

                if (j >= contacts.length) {
                    var msg = undefined;
                    if (ids.length > 0) {
                        msg = new Error("Validation Error");
                    }
                    var jsonString = messageFormatter.FormatMessage(msg, "OPERATIONS COMPLETED", ids.length == 0, ids);
                    console.log("jsonString");
                }
            });

    }

}
<|endoftext|>


<commit_before>import isBrowser from './isBrowser.macro';

const isIE = isBrowser ? !!document.documentElement.currentStyle : false;

const HIDDEN_TEXTAREA_STYLE = {
  'min-height': '0',
  'max-height': 'none',
  height: '0',
  visibility: 'hidden',
  overflow: 'hidden',
  position: 'absolute',
  'z-index': '-1000',
  top: '0',
  right: '0',
};

const SIZING_STYLE = [
  'letter-spacing',
  'line-height',
  'font-family',
  'font-weight',
  'font-size',
  'font-style',
  'tab-size',
  'text-rendering',
  'text-transform',
  'width',
  'text-indent',
  'padding-top',
  'padding-right',
  'padding-bottom',
  'padding-left',
  'border-top-width',
  'border-right-width',
  'border-bottom-width',
  'border-left-width',
  'box-sizing',
];

let computedStyleCache = {};
const hiddenTextarea = isBrowser && document.createElement('textarea');
hiddenTextarea.setAttribute('tab-index', '-1');
hiddenTextarea.setAttribute('aria-hidden', 'true');

const forceHiddenStyles = node => {
  Object.keys(HIDDEN_TEXTAREA_STYLE).forEach(key => {
    node.style.setProperty(key, HIDDEN_TEXTAREA_STYLE[key], 'important');
  });
};

if (isBrowser) {
  forceHiddenStyles(hiddenTextarea);
}

export default function calculateNodeHeight(
  uiTextNode,
  uid,
  useCache = false,
  minRows = null,
  maxRows = null,
) {
  if (hiddenTextarea.parentNode === null) {
    document.body.appendChild(hiddenTextarea);
  }

  // Copy all CSS properties that have an impact on the height of the content in
  // the textbox
  const nodeStyling = calculateNodeStyling(uiTextNode, uid, useCache);

  if (nodeStyling === null) {
    return null;
  }

  const { paddingSize, borderSize, boxSizing, sizingStyle } = nodeStyling;

  // Need to have the overflow attribute to hide the scrollbar otherwise
  // text-lines will not calculated properly as the shadow will technically be
  // narrower for content
  Object.keys(sizingStyle).forEach(key => {
    hiddenTextarea.style[key] = sizingStyle[key];
  });
  forceHiddenStyles(hiddenTextarea);
  hiddenTextarea.value = uiTextNode.value || uiTextNode.placeholder || 'x';

  let minHeight = -Infinity;
  let maxHeight = Infinity;
  let height = hiddenTextarea.scrollHeight;

  if (boxSizing === 'border-box') {
    // border-box: add border, since height = content + padding + border
    height = height + borderSize;
  } else if (boxSizing === 'content-box') {
    // remove padding, since height = content
    height = height - paddingSize;
  }

  // measure height of a textarea with a single row
  hiddenTextarea.value = 'x';
  const singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;

  // Stores the value's rows count rendered in `hiddenTextarea`,
  // regardless if `maxRows` or `minRows` props are passed
  const valueRowCount = Math.floor(height / singleRowHeight);

  if (minRows !== null) {
    minHeight = singleRowHeight * minRows;
    if (boxSizing === 'border-box') {
      minHeight = minHeight + paddingSize + borderSize;
    }
    height = Math.max(minHeight, height);
  }

  if (maxRows !== null) {
    maxHeight = singleRowHeight * maxRows;
    if (boxSizing === 'border-box') {
      maxHeight = maxHeight + paddingSize + borderSize;
    }
    height = Math.min(maxHeight, height);
  }

  const rowCount = Math.floor(height / singleRowHeight);

  return { height, minHeight, maxHeight, rowCount, valueRowCount };
}

function calculateNodeStyling(node, uid, useCache = false) {
  if (useCache && computedStyleCache[uid]) {
    return computedStyleCache[uid];
  }

  const style = window.getComputedStyle(node);

  if (style === null) {
    return null;
  }

  let sizingStyle = SIZING_STYLE.reduce((obj, name) => {
    obj[name] = style.getPropertyValue(name);
    return obj;
  }, {});

  const boxSizing = sizingStyle['box-sizing'];

  // probably node is detached from DOM, can't read computed dimensions
  if (boxSizing === '') {
    return null;
  }

  // IE (Edge has already correct behaviour) returns content width as computed width
  // so we need to add manually padding and border widths
  if (isIE && boxSizing === 'border-box') {
    sizingStyle.width =
      parseFloat(sizingStyle.width) +
      parseFloat(style['border-right-width']) +
      parseFloat(style['border-left-width']) +
      parseFloat(style['padding-right']) +
      parseFloat(style['padding-left']) +
      'px';
  }

  const paddingSize =
    parseFloat(sizingStyle['padding-bottom']) +
    parseFloat(sizingStyle['padding-top']);

  const borderSize =
    parseFloat(sizingStyle['border-bottom-width']) +
    parseFloat(sizingStyle['border-top-width']);

  const nodeInfo = {
    sizingStyle,
    paddingSize,
    borderSize,
    boxSizing,
  };

  if (useCache) {
    computedStyleCache[uid] = nodeInfo;
  }

  return nodeInfo;
}

export const purgeCache = uid => {
  delete computedStyleCache[uid];
};
<commit_msg>Try to set a11y attributes only in the browser (fixes SSR)<commit_after>import isBrowser from './isBrowser.macro';

const isIE = isBrowser ? !!document.documentElement.currentStyle : false;

const HIDDEN_TEXTAREA_STYLE = {
  'min-height': '0',
  'max-height': 'none',
  height: '0',
  visibility: 'hidden',
  overflow: 'hidden',
  position: 'absolute',
  'z-index': '-1000',
  top: '0',
  right: '0',
};

const SIZING_STYLE = [
  'letter-spacing',
  'line-height',
  'font-family',
  'font-weight',
  'font-size',
  'font-style',
  'tab-size',
  'text-rendering',
  'text-transform',
  'width',
  'text-indent',
  'padding-top',
  'padding-right',
  'padding-bottom',
  'padding-left',
  'border-top-width',
  'border-right-width',
  'border-bottom-width',
  'border-left-width',
  'box-sizing',
];

let computedStyleCache = {};
const hiddenTextarea = isBrowser && document.createElement('textarea');

const forceHiddenStyles = node => {
  Object.keys(HIDDEN_TEXTAREA_STYLE).forEach(key => {
    node.style.setProperty(key, HIDDEN_TEXTAREA_STYLE[key], 'important');
  });
};

if (isBrowser) {
  hiddenTextarea.setAttribute('tab-index', '-1');
  hiddenTextarea.setAttribute('aria-hidden', 'true');
  forceHiddenStyles(hiddenTextarea);
}

export default function calculateNodeHeight(
  uiTextNode,
  uid,
  useCache = false,
  minRows = null,
  maxRows = null,
) {
  if (hiddenTextarea.parentNode === null) {
    document.body.appendChild(hiddenTextarea);
  }

  // Copy all CSS properties that have an impact on the height of the content in
  // the textbox
  const nodeStyling = calculateNodeStyling(uiTextNode, uid, useCache);

  if (nodeStyling === null) {
    return null;
  }

  const { paddingSize, borderSize, boxSizing, sizingStyle } = nodeStyling;

  // Need to have the overflow attribute to hide the scrollbar otherwise
  // text-lines will not calculated properly as the shadow will technically be
  // narrower for content
  Object.keys(sizingStyle).forEach(key => {
    hiddenTextarea.style[key] = sizingStyle[key];
  });
  forceHiddenStyles(hiddenTextarea);
  hiddenTextarea.value = uiTextNode.value || uiTextNode.placeholder || 'x';

  let minHeight = -Infinity;
  let maxHeight = Infinity;
  let height = hiddenTextarea.scrollHeight;

  if (boxSizing === 'border-box') {
    // border-box: add border, since height = content + padding + border
    height = height + borderSize;
  } else if (boxSizing === 'content-box') {
    // remove padding, since height = content
    height = height - paddingSize;
  }

  // measure height of a textarea with a single row
  hiddenTextarea.value = 'x';
  const singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;

  // Stores the value's rows count rendered in `hiddenTextarea`,
  // regardless if `maxRows` or `minRows` props are passed
  const valueRowCount = Math.floor(height / singleRowHeight);

  if (minRows !== null) {
    minHeight = singleRowHeight * minRows;
    if (boxSizing === 'border-box') {
      minHeight = minHeight + paddingSize + borderSize;
    }
    height = Math.max(minHeight, height);
  }

  if (maxRows !== null) {
    maxHeight = singleRowHeight * maxRows;
    if (boxSizing === 'border-box') {
      maxHeight = maxHeight + paddingSize + borderSize;
    }
    height = Math.min(maxHeight, height);
  }

  const rowCount = Math.floor(height / singleRowHeight);

  return { height, minHeight, maxHeight, rowCount, valueRowCount };
}

function calculateNodeStyling(node, uid, useCache = false) {
  if (useCache && computedStyleCache[uid]) {
    return computedStyleCache[uid];
  }

  const style = window.getComputedStyle(node);

  if (style === null) {
    return null;
  }

  let sizingStyle = SIZING_STYLE.reduce((obj, name) => {
    obj[name] = style.getPropertyValue(name);
    return obj;
  }, {});

  const boxSizing = sizingStyle['box-sizing'];

  // probably node is detached from DOM, can't read computed dimensions
  if (boxSizing === '') {
    return null;
  }

  // IE (Edge has already correct behaviour) returns content width as computed width
  // so we need to add manually padding and border widths
  if (isIE && boxSizing === 'border-box') {
    sizingStyle.width =
      parseFloat(sizingStyle.width) +
      parseFloat(style['border-right-width']) +
      parseFloat(style['border-left-width']) +
      parseFloat(style['padding-right']) +
      parseFloat(style['padding-left']) +
      'px';
  }

  const paddingSize =
    parseFloat(sizingStyle['padding-bottom']) +
    parseFloat(sizingStyle['padding-top']);

  const borderSize =
    parseFloat(sizingStyle['border-bottom-width']) +
    parseFloat(sizingStyle['border-top-width']);

  const nodeInfo = {
    sizingStyle,
    paddingSize,
    borderSize,
    boxSizing,
  };

  if (useCache) {
    computedStyleCache[uid] = nodeInfo;
  }

  return nodeInfo;
}

export const purgeCache = uid => {
  delete computedStyleCache[uid];
};
<|endoftext|>


<commit_before>import Ember from 'ember';
import layout from './template';

/**
 * This component wraps form inputs.
 *
 * It can be used in a two-way-binding style like
 * {{validated-input model=model name='firstName'}} (model will be updated)
 *
 * or in a one-way-binding style
 * {{validated-input model=model name='firstName' on-update=(action "update"}}
 * (update action is called, model is not updated)
 */
export default Ember.Component.extend({
  layout,

  dirty: false,

  required: false,

  type: 'input',

  classNameBindings: ['showError:has-error:valid', 'dirty', 'config.css.group'],

  error: Ember.computed('model.error', function() {
    const error = this.get('model.error');
    return error ? error[this.get('name')] : null;
  }),

  isValid: Ember.computed('error', function() {
    return !this.get('error');
  }),

  firstError: Ember.computed('error', function() {
    return this.get('error.validation')[0];
  }),

  showError: Ember.computed('isValid', 'dirty', 'submitted', function() {
    return !this.get('isValid') && (this.get('dirty') || this.get('submitted'));
  }),

  init() {
    this._super(...arguments);
    // mark field as required if validation errors are present during init.
    if (this.get(`model.error.${this.get('name')}.validation`)) {
      this.set('required', true);
    }
  },

  actions: {
    setDirty() {
      this.set('dirty', true);
    },

    update(value) {
      if (this.attrs['on-update']) {
        this.attrs['on-update'](value);
      } else {
        this.set(`model.${this.get('name')}`, value);
      }
    }
  }
});
<commit_msg>also pass the model to on-update function<commit_after>import Ember from 'ember';
import layout from './template';

/**
 * This component wraps form inputs.
 *
 * It can be used in a two-way-binding style like
 * {{validated-input model=model name='firstName'}} (model will be updated)
 *
 * or in a one-way-binding style
 * {{validated-input model=model name='firstName' on-update=(action "update"}}
 * (update action is called, model is not updated)
 */
export default Ember.Component.extend({
  layout,

  dirty: false,

  required: false,

  type: 'input',

  classNameBindings: ['showError:has-error:valid', 'dirty', 'config.css.group'],

  error: Ember.computed('model.error', function() {
    const error = this.get('model.error');
    return error ? error[this.get('name')] : null;
  }),

  isValid: Ember.computed('error', function() {
    return !this.get('error');
  }),

  firstError: Ember.computed('error', function() {
    return this.get('error.validation')[0];
  }),

  showError: Ember.computed('isValid', 'dirty', 'submitted', function() {
    return !this.get('isValid') && (this.get('dirty') || this.get('submitted'));
  }),

  init() {
    this._super(...arguments);
    // mark field as required if validation errors are present during init.
    if (this.get(`model.error.${this.get('name')}.validation`)) {
      this.set('required', true);
    }
  },

  actions: {
    setDirty() {
      this.set('dirty', true);
    },

    update(value) {
      if (this.attrs['on-update']) {
        this.attrs['on-update'](value, this.get('model'));
      } else {
        this.set(`model.${this.get('name')}`, value);
      }
    }
  }
});
<|endoftext|>


<commit_before>import isomorphicFetch from 'isomorphic-fetch';
import is from 'is_js';
import Store from 'domain/Store';
import camelCase from 'camelcase';
import Config from 'domain/Config';

export const buildUrl = (path) => Config.get('apiBase') + path;

class FetchTimedOutError extends Error { }

export class ApiError extends Error {
  constructor(response = {}, message) {
    super(message || response.statusText);
    // https://fetch.spec.whatwg.org/#responses
    this.ok = response.ok;
    this.url = response.url;
    this.type = response.type;
    this.status = response.status;
    this.statusText = response.statusText;
    this.headers = response.headers;
    this.body = response.body;
  }
}

export const fetch = (url, options = {}) => {
  const finalOptions = Object.assign({}, options, getTokenHeader(options));
  return new Promise((resolve, reject) => {
    const onTimeout = () => reject(new FetchTimedOutError(`Call to ${url} has taken too long!`));
    const timeout = setTimeout(onTimeout, Config.get('fetchTimeout'));

    isomorphicFetch(url, finalOptions)
      .then(checkResponseStatus)
      .then(parseResponse)
      .then((response) => {
        clearTimeout(timeout);
        resolve(response);
      })
      .catch((error) => {
        clearTimeout(timeout);
        reject(error);
      });
  });
};

function parseResponse(rawResponse) {
  return rawResponse.text()
      .then((response) => {
        try {
          return JSON.parse(response);
        } catch (e) {
          return response;
        }
      });
}

function checkResponseStatus(response) {
  if (response.ok) {
    return response;
  }

  const error = new ApiError(response);
  try {
    return parseResponse(response)
        .then((apiResponse) => {
          if (is.not.object(apiResponse)) {
            error.response = apiResponse;
          } else {
            error.response = {};
            Object.keys(apiResponse).forEach((key) => {
              error.response[camelCase(key)] = apiResponse[key];
            });
          }

          throw error;
        });
  } catch (e) {
    throw error;
  }
}

function getTokenHeader(options) {
  const user = Store.get().getState().get('singleSignOn').user || {};
  const token = user.access_token;
  return {
    headers: Object.assign(
      {
        Accept: 'application/json; charset=utf-8',
        Authorization: `Bearer ${token}`,
      },
      is.not.object(options) ? undefined : options.headers
    ),
  };
}

class Api { }

Api.fetch = fetch;
Api.buildUrl = buildUrl;

export default Api;
<commit_msg>Api: Don't send a content-type header in GETs<commit_after>import isomorphicFetch from 'isomorphic-fetch';
import is from 'is_js';
import Store from 'domain/Store';
import camelCase from 'camelcase';
import Config from 'domain/Config';

export const buildUrl = (path) => Config.get('apiBase') + path;

class FetchTimedOutError extends Error { }

export class ApiError extends Error {
  constructor(response = {}, message) {
    super(message || response.statusText);
    // https://fetch.spec.whatwg.org/#responses
    this.ok = response.ok;
    this.url = response.url;
    this.type = response.type;
    this.status = response.status;
    this.statusText = response.statusText;
    this.headers = response.headers;
    this.body = response.body;
  }
}

export const fetch = (url, options = {}) => {
  const finalOptions = Object.assign({}, options, getTokenHeader(options));
  return new Promise((resolve, reject) => {
    const onTimeout = () => reject(new FetchTimedOutError(`Call to ${url} has taken too long!`));
    const timeout = setTimeout(onTimeout, Config.get('fetchTimeout'));

    isomorphicFetch(url, finalOptions)
      .then(checkResponseStatus)
      .then(parseResponse)
      .then((response) => {
        clearTimeout(timeout);
        resolve(response);
      })
      .catch((error) => {
        clearTimeout(timeout);
        reject(error);
      });
  });
};

function parseResponse(rawResponse) {
  return rawResponse.text()
      .then((response) => {
        try {
          return JSON.parse(response);
        } catch (e) {
          return response;
        }
      });
}

function checkResponseStatus(response) {
  if (response.ok) {
    return response;
  }

  const error = new ApiError(response);
  try {
    return parseResponse(response)
        .then((apiResponse) => {
          if (is.not.object(apiResponse)) {
            error.response = apiResponse;
          } else {
            error.response = {};
            Object.keys(apiResponse).forEach((key) => {
              error.response[camelCase(key)] = apiResponse[key];
            });
          }

          throw error;
        });
  } catch (e) {
    throw error;
  }
}

function getTokenHeader(options) {
  const user = Store.get().getState().get('singleSignOn').user || {};
  const token = user.access_token;
  return {
    headers: Object.assign(
      {
        Accept: 'application/json; charset=utf-8',
        Authorization: `Bearer ${token}`,
      },
      is.object(options) && options.method && options.method.toUpperCase() !== 'GET' ? {
        'Content-Type': 'application/json; charset=utf-8',
      } : {},
      is.not.object(options) ? undefined : options.headers
    ),
  };
}

class Api { }

Api.fetch = fetch;
Api.buildUrl = buildUrl;

export default Api;
<|endoftext|>


<commit_before>'use strict';

var connect = require('connect')
  , path = require('path')
  , app = connect()
  , server
  , port = process.argv[2] || 3000
  , auth = require('./auth')
  , config = require('./config')
  , routes
  ;

if (!connect.router) {
  connect.router = require('connect_router');
}

function route(rest) {
  function getPublic(reqUser) {
    if (!reqUser) {
      return null;
    }
    return {
      currentLoginId: reqUser.currentUser.id
    , accounts: reqUser.accounts
    , profiles: reqUser.profiles.map(function (authN) { authN.profile.pkey = authN.id; return authN.profile; })
    };
  }

  rest.post('/api/session', function (req, res) {
    res.send(getPublic(req.user) || { role: 'guest', as: 'post' });
  });
  rest.get('/api/session', function (req, res) {
    res.send(getPublic(req.user) || { role: 'guest', as: 'get' });
  });
  rest.delete('/api/session', function (req, res) {
    req.logout();
    res.send({ role: 'guest', as: 'delete' });
  });
}

app
  .use(function (req, res, next) {
      res.redirect = function (code, href) {
        if (!href) {
          href = code;
          code = 302;
        }

        res.statusCode = code;
        res.setHeader('Location', href);
        res.end();
      };
      res.send = function (data) {
        if (data) {
          data = JSON.stringify(data);
        } else {
          data = undefined;
        }

        res.end(data);
      };
      next();
    })
  .use(connect.query())
  .use(connect.json())
  .use(connect.urlencoded())
  .use(connect.compress())
  .use(connect.cookieParser())
  .use(connect.session({ secret: 'fzzysnthbeeeeaith' }))
  //.use(express.router)
  ;
  //route(app);

routes = auth.init(app, config);
routes.forEach(function (fn) {
  app.use(connect.router(fn));
});

app
  .use(connect.router(route))
  .use(connect.static(path.join(__dirname, 'data')))
  //.use(connect.static(path.join(__dirname, 'dist')))
  //.use(connect.static(path.join(__dirname, '.tmp', 'concat')))
  .use(connect.static(path.join(__dirname, 'app')))
  .use(connect.static(path.join(__dirname, '.tmp')))
  ;

if (require.main === module) {
  server = app.listen(port, function () {
    console.log('Listening on http://' + server.address().address + ':' + server.address().port + '/#/');
  });
  return;
}

module.exports = app;
<commit_msg>encapsulate 'run'<commit_after>'use strict';

var connect = require('connect')
  , path = require('path')
  , app = connect()
  , auth = require('./auth')
  , config = require('./config')
  , routes
  ;

if (!connect.router) {
  connect.router = require('connect_router');
}

function route(rest) {
  function getPublic(reqUser) {
    if (!reqUser) {
      return null;
    }
    return {
      currentLoginId: reqUser.currentUser.id
    , accounts: reqUser.accounts
    , profiles: reqUser.profiles.map(function (authN) { authN.profile.pkey = authN.id; return authN.profile; })
    };
  }

  rest.post('/api/session', function (req, res) {
    res.send(getPublic(req.user) || { role: 'guest', as: 'post' });
  });
  rest.get('/api/session', function (req, res) {
    res.send(getPublic(req.user) || { role: 'guest', as: 'get' });
  });
  rest.delete('/api/session', function (req, res) {
    req.logout();
    res.send({ role: 'guest', as: 'delete' });
  });
}

app
  .use(function (req, res, next) {
      res.redirect = function (code, href) {
        if (!href) {
          href = code;
          code = 302;
        }

        res.statusCode = code;
        res.setHeader('Location', href);
        res.end();
      };
      res.send = function (data) {
        if (data) {
          data = JSON.stringify(data);
        } else {
          data = undefined;
        }

        res.end(data);
      };
      next();
    })
  .use(connect.query())
  .use(connect.json())
  .use(connect.urlencoded())
  .use(connect.compress())
  .use(connect.cookieParser())
  .use(connect.session({ secret: 'fzzysnthbeeeeaith' }))
  //.use(express.router)
  ;
  //route(app);

routes = auth.init(app, config);
routes.forEach(function (fn) {
  app.use(connect.router(fn));
});

app
  .use(connect.router(route))
  .use(connect.static(path.join(__dirname, 'data')))
  //.use(connect.static(path.join(__dirname, 'dist')))
  //.use(connect.static(path.join(__dirname, '.tmp', 'concat')))
  .use(connect.static(path.join(__dirname, 'app')))
  .use(connect.static(path.join(__dirname, '.tmp')))
  ;

function run() {
  var port = process.argv[2] || 3000
    , server
    ;

  server = app.listen(port, function () {
    console.log('Listening on http://' + server.address().address + ':' + server.address().port + '/#/');
  });
}

if (require.main === module) {
  run();
  return;
}

module.exports = app;
<|endoftext|>