<commit_before>(function($) {

  'use strict';

  $.coreSlider = function(container, options) {

    this.defaults = {
      interval: 5000,                                         // Interval of time between slide changes
      loop: true,                                             // When slider finish, should it loop again from first slide?
      slideshow: true,                                        // Enable/Disable automatic slideshow
      resize: true,                                           // Should be slider responsive on screen resize
      pauseOnHover: true,                                     // Pause the slideshow when hovering over slider
      startOnHover: false,                                    // Start the slideshow when hovering over slider
      sliderSelector: '.core-slider_list',                    // List selector (all items are inside this container)
      viewportSelector: '.core-slider_viewport',              // Viewport selector
      itemSelector: '.core-slider_item',                      // Slider items selector
      navEnabled: true,                                       // Enable/Disable navigation arrows
      navSelector: '.core-slider_nav',                        // Selector for navigation arrows container
      navItemNextSelector: '.core-slider_arrow__right',       // 'Next' arrow selector
      navItemPrevSelector: '.core-slider_arrow__left',        // 'Prev' arrow selector
      controlNavEnabled: false,                               // Enable/Disable control navigation (dots)
      controlNavSelector: '.core-slider_control-nav',         // Control navigation container selector (inside will be created dots items)
      controlNavItemSelector: 'core-slider_control-nav-item', // Single control nav dot (created dynamically. Write without dot. If you need more that one class - add them with space separator)
      loadedClass: 'is-loaded',                               // Classname, that will be added when slider is fully loaded
      clonedClass: 'is-cloned',                               // Classname, that will be added to cloned slides (see option 'clone')
      disabledClass: 'is-disabled',                           // Classname, that will be added it item is disabled (in most of cases - item will be display: noned)
      activeClass: 'is-active',                               // Classname, that will be added to active items (for example control navs, etc.)
      reloadGif: false,                                       // Reload gif's on slide change for replaying cycled animation inside current slide
      clone: false,                                           // Indicates, that at begin and at end of slider carousel items will be cloned to create 'infitite' carousel illusion
      items: 1,                                               // How mutch items will be placed inside viewport. Leave 1 if this is slider, 2 ot more - it will look like a carousel
      itemsPerSlide: 1,                                       // How many items must be slided by one action (NOTE: Must be less than 'items' option)
      cloneItems: 0                                           // How mutch items will be cloned at begin and at end of slider
    };

    // Extend defaults with settings passed on init
    this.settings = $.extend({}, this.defaults, options);

    var self = this,
        animateInterval,
        $sliderContainer = $(container),
        $sliderViewport = $sliderContainer.find(self.settings.viewportSelector),
        $slider = $sliderContainer.find(self.settings.sliderSelector),
        $sliderItems = $sliderContainer.find(self.settings.itemSelector),
        $clonedSliderItems = null,
        $sliderNav = $sliderContainer.find(self.settings.navSelector),
        $sliderPrevBtn = $sliderContainer.find(self.settings.navItemPrevSelector),
        $sliderNextBtn = $sliderContainer.find(self.settings.navItemNextSelector),
        $sliderControlNav = $sliderContainer.find(self.settings.controlNavSelector),
        $sliderControlNavItems,
        slideCount = $sliderItems.length - 1, // Count, with counter, starting from zero
        slideCountTotal = $sliderItems.length,
        slideWidth, // Single slide width
        currentSlide = 0,
        transformPrefix = getVendorPrefixes(["transform", "msTransform", "MozTransform", "WebkitTransform"]),
        resizeTimeout,
        currentUrl = null,
        currentTags = null,
        remainingItems = {
          left: 0,
          right: 0
        }, // Number of items remaining on left and on right around visible part
        isFirstLoad = true; // Indicates, that slider was first loaded

    // Getter for vendor prefixes
    function getVendorPrefixes(prefixes) {
      var tmp = document.createElement("div"),
          result = ""; // Store results here
      for (var i = 0; i < prefixes.length; i++) {
        if (typeof tmp.style[prefixes[i]] != 'undefined') {
          result = prefixes[i];
          break;
        } else {
          result = null;
        }
      }
      return result;
    }

    function getTranslateX(offset) {
      return 'translateX(' + offset + 'px)';
    }

    // Initialization of slider
    this.init = function() {
      // Set inner container sizes
      $sliderContainer.addClass(self.settings.loadedClass);
      if (self.settings.clone) {
        // If clone is enabled - clone slides on end and on begin of slider
        self.cloneSlides();
      }
      self.setSizes();
      self.setSlide(currentSlide, false);
      if (self.settings.slideshow) {
        self.play();
      }
      if (self.settings.resize) {
        self.resize();
      }
      // Pause on hover events
      if (self.settings.pauseOnHover && self.settings.slideshow) {
        $sliderContainer.mouseenter(function() {
          self.stop();
        });
        $sliderContainer.mouseleave(function() {
          self.play();
        });
      }
      // Start on hover events
      if (self.settings.startOnHover && self.settings.slideshow) {
        $sliderContainer.mouseenter(function() {
          self.play();
        });
        $sliderContainer.mouseleave(function() {
          self.stop();
        });
      }

      // Add handlers for slider navs (prev/next)
      if (self.settings.navEnabled) {
        $sliderPrevBtn.on('click', function() {
          if(!$(this).hasClass(self.settings.disabledClass)) {
            self.setSlide(currentSlide - self.settings.itemsPerSlide, true);
          }
        });
        $sliderNextBtn.on('click', function() {
          if(!$(this).hasClass(self.settings.disabledClass)) {
            self.setSlide(currentSlide + self.settings.itemsPerSlide, true);
          }
        });
      } else {
        // Add disabled class for navigration arrows
        $sliderNav.addClass(self.settings.disabledClass);
      }

      // Add handlers and init slider control navs
      if (self.settings.controlNavEnabled) {
        // Create dynamically dot items and append them to container
        var buffer = []; // Container of all dot items that will be created later
        for (var i = 0; i < slideCount + 1; i++) {
          if (i == currentSlide) {
            // Make current item active from begin
            buffer.push('

');
          } else {
            buffer.push('

');
          }
        }
        $sliderControlNav.append(buffer.join(''));
        // Cache all items in variable
        $sliderControlNavItems = $sliderControlNav.children();
        // Add event handlers to container
        $sliderControlNav.on('click', $sliderControlNavItems, function(e) {
          self.setSlide($(e.target).index(), false);
        });
        // If items are less then viewport - add disabled classes
        if (slideCountTotal <= self.settings.items) {
          $sliderNextBtn.addClass(self.settings.disabledClass);
          $sliderPrevBtn.addClass(self.settings.disabledClass);
        }
      } else {
        // Add disabled class for navigration dots
        $sliderControlNav.addClass(self.settings.disabledClass);
      }
    };

    this.cloneSlides = function() {
      // Prepend first last items at begin of slider and first elements on end of slider
      $slider.append($sliderItems.slice(0, self.settings.cloneItems).clone().addClass(self.settings.clonedClass));
      $slider.prepend($sliderItems.slice(slideCount - self.settings.cloneItems + 1, slideCount + 1).clone().addClass(self.settings.clonedClass));
      // Cache cloned items in variable
      $clonedSliderItems = $sliderContainer.find(self.settings.itemSelector).filter('.' + self.settings.clonedClass);
    };

    // Function for setting sizes for each item in slider
    this.setSizes = function() {
      slideWidth = $sliderViewport.width() / self.settings.items;
      $sliderItems.add($clonedSliderItems).css('width', slideWidth);
      $slider.css('width', slideWidth * (slideCount + self.settings.cloneItems*2 +  1));
      // $slider.css('width', slideWidth * (slideCount + self.settings.items * 2 + 1) * 1.3);
    };

    // Main function that moves slides. Set slide by passed index as parameter
    this.setSlide = function(index, isDirectionNav) {
      var isDirectionNavClick = isDirectionNav; // Indicates that click was from arrow navigation

      self.stop();

      // Play animation only if total number of items is less than number visible in viewport
      if (slideCountTotal > self.settings.items && isDirectionNavClick) {
        // Get number of remaining on right items
        remainingItems.right = (slideCount + 1) - currentSlide - self.settings.items;
        remainingItems.left = currentSlide;

        $sliderNextBtn.removeClass(self.settings.disabledClass);
        $sliderPrevBtn.removeClass(self.settings.disabledClass);

        // Get direction
        if (currentSlide - index < 0) {
          // Right direction
          if (remainingItems.right < self.settings.itemsPerSlide) {
            index = slideCount - self.settings.items + 1;
            // Set disabled class for nav
            if (!self.settings.loop) {
              $sliderNextBtn.addClass(self.settings.disabledClass);
            }
          }
          if (remainingItems.right == 0) {
            index = 0; // If loop enavled - rollup to first slide
          }
        } else {
          // Left direction
          if (remainingItems.left < self.settings.itemsPerSlide) {
            index = 0;
            if (!self.settings.loop) {
              $sliderPrevBtn.addClass(self.settings.disabledClass);
            }
          }
          if (remainingItems.left == 0 && !isFirstLoad) {
            index = slideCount - self.settings.items + 1;
          }
        }

        // Replay possible animations in gif's
        if (self.settings.reloadGif) {
          currentTags = $sliderItems.eq(index).find('img');
          currentTags.each(function() {
            var $this = $(this);
            currentUrl = $this.attr('src');
            $this.attr('src', '');
            $this.attr('src', currentUrl);
          });
        }
      }

      if(!isDirectionNavClick && self.settings.controlNavEnabled) {
        // Set slide directly (for example from dots or from extenal API), including items cloning
        index = (index > slideCount - self.settings.items + self.settings.cloneItems + 1) ? slideCount - self.settings.items + self.settings.cloneItems + 1 : index;
      }

      // Set active new control nav item
      if (self.settings.controlNavEnabled && typeof $sliderControlNavItems !== 'undefined') {
        $sliderControlNavItems.removeClass(self.settings.activeClass);
        $sliderControlNavItems.eq(index).addClass(self.settings.activeClass);
      }

      // Apply CSS transition to block
      $slider.css(transformPrefix, getTranslateX(-(index + self.settings.cloneItems) * slideWidth));

      currentSlide = index;
      isFirstLoad = false; // Change loaded indicator
    };

    // Resize handler function
    this.resize = function() {
      $(window).resize(function() {
        if (resizeTimeout) {
          clearTimeout(resizeTimeout);
          resizeTimeout = null;
        }
        resizeTimeout = setTimeout(function() {
          self.setSizes();
          self.setSlide(currentSlide, false);
        }, 250);
      });
    };

    this.destroy = function() {
      $sliderContainer.removeClass(self.settings.loadedClass);
      clearInterval(animateInterval);
    };

    this.play = function() {
      animateInterval = setInterval(function() {
        self.setSlide(currentSlide + self.settings.itemsPerSlide, true);
        self.play();
      }, self.settings.interval);
    };

    this.stop = function() {
      clearInterval(animateInterval);
    };

    // Finally init slider
    this.init();
  };

  $.fn.coreSlider = function(options) {
    if (options === undefined) {
      options = {};
    }
    if (typeof options === 'object') {
      return this.each(function() {
        new $.coreSlider(this, options);
      });
    }
  }

})(jQuery);
<commit_msg>Add manifest<commit_after>/*
 * CoreSlider v1.0.0
 * Copyright 2016 Pavel Davydov
 *
 * Licensed under MIT (http://opensource.org/licenses/MIT)
 */

(function($) {

  'use strict';

  $.coreSlider = function(container, options) {

    this.defaults = {
      interval: 5000,                                         // Interval of time between slide changes
      loop: true,                                             // When slider finish, should it loop again from first slide?
      slideshow: true,                                        // Enable/Disable automatic slideshow
      resize: true,                                           // Should be slider responsive on screen resize
      pauseOnHover: true,                                     // Pause the slideshow when hovering over slider
      startOnHover: false,                                    // Start the slideshow when hovering over slider
      sliderSelector: '.core-slider_list',                    // List selector (all items are inside this container)
      viewportSelector: '.core-slider_viewport',              // Viewport selector
      itemSelector: '.core-slider_item',                      // Slider items selector
      navEnabled: true,                                       // Enable/Disable navigation arrows
      navSelector: '.core-slider_nav',                        // Selector for navigation arrows container
      navItemNextSelector: '.core-slider_arrow__right',       // 'Next' arrow selector
      navItemPrevSelector: '.core-slider_arrow__left',        // 'Prev' arrow selector
      controlNavEnabled: false,                               // Enable/Disable control navigation (dots)
      controlNavSelector: '.core-slider_control-nav',         // Control navigation container selector (inside will be created dots items)
      controlNavItemSelector: 'core-slider_control-nav-item', // Single control nav dot (created dynamically. Write without dot. If you need more that one class - add them with space separator)
      loadedClass: 'is-loaded',                               // Classname, that will be added when slider is fully loaded
      clonedClass: 'is-cloned',                               // Classname, that will be added to cloned slides (see option 'clone')
      disabledClass: 'is-disabled',                           // Classname, that will be added it item is disabled (in most of cases - item will be display: noned)
      activeClass: 'is-active',                               // Classname, that will be added to active items (for example control navs, etc.)
      reloadGif: false,                                       // Reload gif's on slide change for replaying cycled animation inside current slide
      clone: false,                                           // Indicates, that at begin and at end of slider carousel items will be cloned to create 'infitite' carousel illusion
      items: 1,                                               // How mutch items will be placed inside viewport. Leave 1 if this is slider, 2 ot more - it will look like a carousel
      itemsPerSlide: 1,                                       // How many items must be slided by one action (NOTE: Must be less than 'items' option)
      cloneItems: 0                                           // How mutch items will be cloned at begin and at end of slider
    };

    // Extend defaults with settings passed on init
    this.settings = $.extend({}, this.defaults, options);

    var self = this,
        animateInterval,
        $sliderContainer = $(container),
        $sliderViewport = $sliderContainer.find(self.settings.viewportSelector),
        $slider = $sliderContainer.find(self.settings.sliderSelector),
        $sliderItems = $sliderContainer.find(self.settings.itemSelector),
        $clonedSliderItems = null,
        $sliderNav = $sliderContainer.find(self.settings.navSelector),
        $sliderPrevBtn = $sliderContainer.find(self.settings.navItemPrevSelector),
        $sliderNextBtn = $sliderContainer.find(self.settings.navItemNextSelector),
        $sliderControlNav = $sliderContainer.find(self.settings.controlNavSelector),
        $sliderControlNavItems,
        slideCount = $sliderItems.length - 1, // Count, with counter, starting from zero
        slideCountTotal = $sliderItems.length,
        slideWidth, // Single slide width
        currentSlide = 0,
        transformPrefix = getVendorPrefixes(["transform", "msTransform", "MozTransform", "WebkitTransform"]),
        resizeTimeout,
        currentUrl = null,
        currentTags = null,
        remainingItems = {
          left: 0,
          right: 0
        }, // Number of items remaining on left and on right around visible part
        isFirstLoad = true; // Indicates, that slider was first loaded

    // Getter for vendor prefixes
    function getVendorPrefixes(prefixes) {
      var tmp = document.createElement("div"),
          result = ""; // Store results here
      for (var i = 0; i < prefixes.length; i++) {
        if (typeof tmp.style[prefixes[i]] != 'undefined') {
          result = prefixes[i];
          break;
        } else {
          result = null;
        }
      }
      return result;
    }

    function getTranslateX(offset) {
      return 'translateX(' + offset + 'px)';
    }

    // Initialization of slider
    this.init = function() {
      // Set inner container sizes
      $sliderContainer.addClass(self.settings.loadedClass);
      if (self.settings.clone) {
        // If clone is enabled - clone slides on end and on begin of slider
        self.cloneSlides();
      }
      self.setSizes();
      self.setSlide(currentSlide, false);
      if (self.settings.slideshow) {
        self.play();
      }
      if (self.settings.resize) {
        self.resize();
      }
      // Pause on hover events
      if (self.settings.pauseOnHover && self.settings.slideshow) {
        $sliderContainer.mouseenter(function() {
          self.stop();
        });
        $sliderContainer.mouseleave(function() {
          self.play();
        });
      }
      // Start on hover events
      if (self.settings.startOnHover && self.settings.slideshow) {
        $sliderContainer.mouseenter(function() {
          self.play();
        });
        $sliderContainer.mouseleave(function() {
          self.stop();
        });
      }

      // Add handlers for slider navs (prev/next)
      if (self.settings.navEnabled) {
        $sliderPrevBtn.on('click', function() {
          if(!$(this).hasClass(self.settings.disabledClass)) {
            self.setSlide(currentSlide - self.settings.itemsPerSlide, true);
          }
        });
        $sliderNextBtn.on('click', function() {
          if(!$(this).hasClass(self.settings.disabledClass)) {
            self.setSlide(currentSlide + self.settings.itemsPerSlide, true);
          }
        });
      } else {
        // Add disabled class for navigration arrows
        $sliderNav.addClass(self.settings.disabledClass);
      }

      // Add handlers and init slider control navs
      if (self.settings.controlNavEnabled) {
        // Create dynamically dot items and append them to container
        var buffer = []; // Container of all dot items that will be created later
        for (var i = 0; i < slideCount + 1; i++) {
          if (i == currentSlide) {
            // Make current item active from begin
            buffer.push('

');
          } else {
            buffer.push('

');
          }
        }
        $sliderControlNav.append(buffer.join(''));
        // Cache all items in variable
        $sliderControlNavItems = $sliderControlNav.children();
        // Add event handlers to container
        $sliderControlNav.on('click', $sliderControlNavItems, function(e) {
          self.setSlide($(e.target).index(), false);
        });
        // If items are less then viewport - add disabled classes
        if (slideCountTotal <= self.settings.items) {
          $sliderNextBtn.addClass(self.settings.disabledClass);
          $sliderPrevBtn.addClass(self.settings.disabledClass);
        }
      } else {
        // Add disabled class for navigration dots
        $sliderControlNav.addClass(self.settings.disabledClass);
      }
    };

    this.cloneSlides = function() {
      // Prepend first last items at begin of slider and first elements on end of slider
      $slider.append($sliderItems.slice(0, self.settings.cloneItems).clone().addClass(self.settings.clonedClass));
      $slider.prepend($sliderItems.slice(slideCount - self.settings.cloneItems + 1, slideCount + 1).clone().addClass(self.settings.clonedClass));
      // Cache cloned items in variable
      $clonedSliderItems = $sliderContainer.find(self.settings.itemSelector).filter('.' + self.settings.clonedClass);
    };

    // Function for setting sizes for each item in slider
    this.setSizes = function() {
      slideWidth = $sliderViewport.width() / self.settings.items;
      $sliderItems.add($clonedSliderItems).css('width', slideWidth);
      $slider.css('width', slideWidth * (slideCount + self.settings.cloneItems*2 +  1));
      // $slider.css('width', slideWidth * (slideCount + self.settings.items * 2 + 1) * 1.3);
    };

    // Main function that moves slides. Set slide by passed index as parameter
    this.setSlide = function(index, isDirectionNav) {
      var isDirectionNavClick = isDirectionNav; // Indicates that click was from arrow navigation

      self.stop();

      // Play animation only if total number of items is less than number visible in viewport
      if (slideCountTotal > self.settings.items && isDirectionNavClick) {
        // Get number of remaining on right items
        remainingItems.right = (slideCount + 1) - currentSlide - self.settings.items;
        remainingItems.left = currentSlide;

        $sliderNextBtn.removeClass(self.settings.disabledClass);
        $sliderPrevBtn.removeClass(self.settings.disabledClass);

        // Get direction
        if (currentSlide - index < 0) {
          // Right direction
          if (remainingItems.right < self.settings.itemsPerSlide) {
            index = slideCount - self.settings.items + 1;
            // Set disabled class for nav
            if (!self.settings.loop) {
              $sliderNextBtn.addClass(self.settings.disabledClass);
            }
          }
          if (remainingItems.right == 0) {
            index = 0; // If loop enavled - rollup to first slide
          }
        } else {
          // Left direction
          if (remainingItems.left < self.settings.itemsPerSlide) {
            index = 0;
            if (!self.settings.loop) {
              $sliderPrevBtn.addClass(self.settings.disabledClass);
            }
          }
          if (remainingItems.left == 0 && !isFirstLoad) {
            index = slideCount - self.settings.items + 1;
          }
        }

        // Replay possible animations in gif's
        if (self.settings.reloadGif) {
          currentTags = $sliderItems.eq(index).find('img');
          currentTags.each(function() {
            var $this = $(this);
            currentUrl = $this.attr('src');
            $this.attr('src', '');
            $this.attr('src', currentUrl);
          });
        }
      }

      if(!isDirectionNavClick && self.settings.controlNavEnabled) {
        // Set slide directly (for example from dots or from extenal API), including items cloning
        index = (index > slideCount - self.settings.items + self.settings.cloneItems + 1) ? slideCount - self.settings.items + self.settings.cloneItems + 1 : index;
      }

      // Set active new control nav item
      if (self.settings.controlNavEnabled && typeof $sliderControlNavItems !== 'undefined') {
        $sliderControlNavItems.removeClass(self.settings.activeClass);
        $sliderControlNavItems.eq(index).addClass(self.settings.activeClass);
      }

      // Apply CSS transition to block
      $slider.css(transformPrefix, getTranslateX(-(index + self.settings.cloneItems) * slideWidth));

      currentSlide = index;
      isFirstLoad = false; // Change loaded indicator
    };

    // Resize handler function
    this.resize = function() {
      $(window).resize(function() {
        if (resizeTimeout) {
          clearTimeout(resizeTimeout);
          resizeTimeout = null;
        }
        resizeTimeout = setTimeout(function() {
          self.setSizes();
          self.setSlide(currentSlide, false);
        }, 250);
      });
    };

    this.destroy = function() {
      $sliderContainer.removeClass(self.settings.loadedClass);
      clearInterval(animateInterval);
    };

    this.play = function() {
      animateInterval = setInterval(function() {
        self.setSlide(currentSlide + self.settings.itemsPerSlide, true);
        self.play();
      }, self.settings.interval);
    };

    this.stop = function() {
      clearInterval(animateInterval);
    };

    // Finally init slider
    this.init();
  };

  $.fn.coreSlider = function(options) {
    if (options === undefined) {
      options = {};
    }
    if (typeof options === 'object') {
      return this.each(function() {
        new $.coreSlider(this, options);
      });
    }
  }

})(jQuery);
<|endoftext|>


<commit_before>const http = require("http")

const SEC = 1000
const MIN = 60 * SEC
const HOUR = 60 * MIN
const DAY = 24 * HOUR

const tasks = [
  {interval: 30 * SEC, job: require('./syncGitHubToJira')}, // 6 api calls, 12/min, 720/hr
  {interval: 1 * MIN, job: require('./pingHonestbeeProd')},
  {interval: 5 * SEC, job: require('./GitHubActivityToSlack')}, // 1 api call, 12/min, 720/hr
  {interval: 30 * SEC, job: require('./syncTravisCIToJira')},
  {interval: 1 * MIN, job: require('./informEBDeployed')},
]

const heartbeat = () => {
  setInterval(() => {
    tasks.forEach(task => {
      const lastRun = new Date(task.lastRun || null).getTime()
      if (Date.now() - lastRun > task.interval) {
        task.job().catch(err => console.log(err))
        task.lastRun = Date.now()
      }
    })
  }, SEC)
}

module.exports = heartbeat
<commit_msg>Disable EB notification<commit_after>const http = require("http")

const SEC = 1000
const MIN = 60 * SEC
const HOUR = 60 * MIN
const DAY = 24 * HOUR

const tasks = [
  {interval: 30 * SEC, job: require('./syncGitHubToJira')}, // 6 api calls, 12/min, 720/hr
  {interval: 1 * MIN, job: require('./pingHonestbeeProd')},
  {interval: 5 * SEC, job: require('./GitHubActivityToSlack')}, // 1 api call, 12/min, 720/hr
  {interval: 30 * SEC, job: require('./syncTravisCIToJira')},
  // {interval: 30 * SEC, job: require('./informEBDeployed')},
]

const heartbeat = () => {
  setInterval(() => {
    tasks.forEach(task => {
      const lastRun = new Date(task.lastRun || null).getTime()
      if (Date.now() - lastRun > task.interval) {
        task.job().catch(err => console.log(err))
        task.lastRun = Date.now()
      }
    })
  }, SEC)
}

module.exports = heartbeat
<|endoftext|>


<commit_before>var mouseDown = false;
var lastMouse = null;
var lastPhi = 0.;
var lastTheta = 0.;
var activeIndex = 0;
getCanvasCoordinates = function(event) {
    var coordinates = {};
    var rect = core.canvas.getBoundingClientRect();
    coordinates.x = event.clientX - rect.left;
    coordinates.y = event.clientY - rect.top;
    return coordinates;
}
function handleMouseDown(event) {
    mouseDown = true;
    var canvasCoordinates = getCanvasCoordinates(event);

    var vpm = core.projectionMatrix[0].inverse();
    // var vm = core.viewProjectionMatrix[0].inverse();

    var solarCoordinates = vpm.multiply($V([ 2. * (canvasCoordinates.x / core.viewport.totalWidth - 0.5), 2. * (canvasCoordinates.y / core.viewport.totalWidth - 0.5), 0., 0. ]));
    var solarCoordinates3Dz = Math.sqrt(1 - solarCoordinates.dot(solarCoordinates));
    var solarCoordinates3D = solarCoordinates.dup();
    solarCoordinates3D.elements[2] = solarCoordinates3Dz;
    // var solarCoordinates3D = vm.multiply($V([ 2. * (canvasCoordinates.x /
    // core.viewport.totalWidth - 0.5), 2. * (canvasCoordinates.y /
    // core.viewport.totalWidth - 0.5), 0., 0. ]));
    // solarCoordinates3D = $V([ solarCoordinates3D.elements[0],
    // solarCoordinates3D.elements[1], solarCoordinates3D.elements[2] ]);
    document.getElementById("canvasCoordinates").innerHTML = "" + canvasCoordinates.x + " " + canvasCoordinates.x;
    document.getElementById("solarCoordinates").innerHTML = "" + solarCoordinates.elements[0] + " " + solarCoordinates.elements[1] + " " + solarCoordinates.elements[2] + " " + solarCoordinates.elements[3];
    document.getElementById("solarCoordinates3D").innerHTML = "" + solarCoordinates3D.elements[0] + " " + solarCoordinates3D.elements[1] + " " + solarCoordinates3D.elements[2];
    varsign = 1.;

    lastPhi = Math.atan(solarCoordinates3D.elements[0] / solarCoordinates3D.elements[2]);
    lastTheta = Math.acos(solarCoordinates3D.elements[1]) - Math.PI / 2.;
    document.getElementById("thetaPhi").innerHTML = "phi:" + (lastPhi + core.phi) * 180. / Math.PI + " theta:" + (lastTheta + core.theta) * 180. / Math.PI;
    document.getElementById("L0B0").innerHTML = "L0:" + (lastPhi + core.phi + core.L0) * 180. / Math.PI + " B0:" + (lastTheta + core.theta + core.B0) * 180. / Math.PI;

    lastMouse = solarCoordinates;

    var vcl = core.viewport.totalWidth / core.viewport.columns;
    var vrl = core.viewport.totalHeight / core.viewport.rows;
    var quit = false;

    for (var ll = 0; !quit && ll < core.viewport.columns; ll++) {
        for (var rr = 0; !quit && rr < core.viewport.rows; rr++) {
            if (canvasCoordinates.x >= ll * vcl && canvasCoordinates.x <= ll * vcl + vcl && canvasCoordinates.y >= rr * vrl && canvasCoordinates.y <= rr * vrl + vrl) {
                quit = true;
                activeIndex = core.viewport.columns * (core.viewport.rows - 1 - rr) + ll;
                console.log(activeIndex);
            }
        }
    }
}

function handleMouseUp(event) {
    mouseDown = false;
}

function handleMouseMove(event) {
    if (!mouseDown) {
        return;
    }
    if (core.mode)
        var canvasCoordinates = getCanvasCoordinates(event);
    var vpm = core.projectionMatrix[activeIndex].inverse();

    var solarCoordinates = vpm.multiply($V([ 2. * (canvasCoordinates.x / core.viewport.totalWidth - 0.5), 2. * (canvasCoordinates.y / core.viewport.totalWidth - 0.5), 0., 0. ]));
    var solarCoordinates3Dz = Math.sqrt(1 - solarCoordinates.dot(solarCoordinates));

    var solarCoordinates3D = solarCoordinates.dup();
    solarCoordinates3D.elements[2] = solarCoordinates3Dz;

    var deltaX = solarCoordinates.elements[0] - lastMouse.elements[0];
    var deltaY = -(solarCoordinates.elements[1] - lastMouse.elements[1]);
    phi = Math.atan(solarCoordinates3D.elements[0] / solarCoordinates3D.elements[2]);
    theta = Math.acos(solarCoordinates3D.elements[1]) - Math.PI / 2.;
    M1 = Matrix.Rotation((lastTheta - theta), $V([ 1, 0, 0 ]));
    M2 = Matrix.Rotation(-(lastPhi - phi), $V([ 0, 1, 0 ]));
    M = M2.x(M1);
    if (!(isNaN(phi) || isNaN(lastPhi) || isNaN(theta) || isNaN(lastTheta))) {
        core.theta += (lastTheta - theta);
        core.phi += (lastPhi - phi);
    }

    lastPhi = phi;
    lastTheta = theta;
}

handleMouseWheel = function(event) {

    var target = event.target || event.srcElement;
    var rect = target.getBoundingClientRect();
    var offsetX = event.clientX - rect.left;
    offsetY = event.clientY - rect.top;

    var newX = offsetX;
    var newY = core.viewport.totalHeight - offsetY;

    var vcl = core.viewport.totalWidth / core.viewport.columns;
    var vrl = core.viewport.totalHeight / core.viewport.rows;
    var index;
    var quit = false;

    for (var ll = 0; !quit && ll < core.viewport.columns; ll++) {
        for (var rr = 0; !quit && rr < core.viewport.rows; rr++) {
            if (newX >= ll * vcl && newX <= ll * vcl + vcl && newY >= rr * vrl && newY <= rr * vrl + vrl) {
                quit = true;
                index = core.viewport.columns * (core.viewport.rows - 1 - rr) + ll;
            }
        }
    }

    var wheel = event.wheelDelta / 120;// n or -n

    var zoom = 1 + wheel / 2;
    core.zoom[index] = core.zoom[index] * zoom;
    if (core.zoom[index] < 0.1) {
        core.zoom[index] = 0.1;
    }
    if (core.zoom[index] > 5.0) {
        core.zoom[index] = 5.0;
    }
    core.computeProjectionMatrix(index);
    event.preventDefault();
}<commit_msg>update mouse interaction<commit_after>var mouseDown = false;
var lastMouse = null;
var lastPhi = 0.;
var lastTheta = 0.;
var activeIndex = 0;
getCanvasCoordinates = function(event) {
    var coordinates = {};
    var rect = core.canvas.getBoundingClientRect();
    coordinates.x = event.clientX - rect.left;
    coordinates.y = event.clientY - rect.top;
    return coordinates;
}
function handleMouseDown(event) {
    mouseDown = true;
    var canvasCoordinates = getCanvasCoordinates(event);

    var vpm = core.projectionMatrix[0].inverse();
    // var vm = core.viewProjectionMatrix[0].inverse();

    var solarCoordinates = vpm.multiply($V([ 2. * (canvasCoordinates.x / core.viewport.totalWidth - 0.5), 2. * (canvasCoordinates.y / core.viewport.totalWidth - 0.5), 0., 0. ]));
    var solarCoordinates3Dz = Math.sqrt(1 - solarCoordinates.dot(solarCoordinates));
    var solarCoordinates3D = solarCoordinates.dup();
    solarCoordinates3D.elements[2] = solarCoordinates3Dz;
    // var solarCoordinates3D = vm.multiply($V([ 2. * (canvasCoordinates.x /
    // core.viewport.totalWidth - 0.5), 2. * (canvasCoordinates.y /
    // core.viewport.totalWidth - 0.5), 0., 0. ]));
    // solarCoordinates3D = $V([ solarCoordinates3D.elements[0],
    // solarCoordinates3D.elements[1], solarCoordinates3D.elements[2] ]);
    document.getElementById("canvasCoordinates").innerHTML = "" + canvasCoordinates.x + " " + canvasCoordinates.x;
    document.getElementById("solarCoordinates").innerHTML = "" + solarCoordinates.elements[0] + " " + solarCoordinates.elements[1] + " " + solarCoordinates.elements[2] + " " + solarCoordinates.elements[3];
    document.getElementById("solarCoordinates3D").innerHTML = "" + solarCoordinates3D.elements[0] + " " + solarCoordinates3D.elements[1] + " " + solarCoordinates3D.elements[2];
    varsign = 1.;

    lastPhi = Math.atan(solarCoordinates3D.elements[0] / solarCoordinates3D.elements[2]);
    lastTheta = Math.acos(solarCoordinates3D.elements[1]) - Math.PI / 2.;
    document.getElementById("thetaPhi").innerHTML = "phi:" + (lastPhi + core.phi) * 180. / Math.PI + " theta:" + (lastTheta + core.theta) * 180. / Math.PI;
    document.getElementById("L0B0").innerHTML = "L0:" + (lastPhi + core.phi + core.L0) * 180. / Math.PI + " B0:" + (lastTheta + core.theta + core.B0) * 180. / Math.PI;

    lastMouse = solarCoordinates;

    var vcl = core.viewport.totalWidth / core.viewport.columns;
    var vrl = core.viewport.totalHeight / core.viewport.rows;
    var quit = false;

    for (var ll = 0; !quit && ll < core.viewport.columns; ll++) {
        for (var rr = 0; !quit && rr < core.viewport.rows; rr++) {
            if (canvasCoordinates.x >= ll * vcl && canvasCoordinates.x <= ll * vcl + vcl && canvasCoordinates.y >= rr * vrl && canvasCoordinates.y <= rr * vrl + vrl) {
                quit = true;
                activeIndex = core.viewport.columns * (core.viewport.rows - 1 - rr) + ll;
                console.log(activeIndex);
            }
        }
    }
}

function handleMouseUp(event) {
    mouseDown = false;
}

function handleMouseMove(event) {
    if (!mouseDown) {
        return;
    }
    if (core.viewport.modes[activeIndex] === '3D') {
        handelMouseMove3D(event);
    }
}

function handelMouseMove3D(event) {
    var canvasCoordinates = getCanvasCoordinates(event);
    var vpm = core.projectionMatrix[activeIndex].inverse();

    var solarCoordinates = vpm.multiply($V([ 2. * (canvasCoordinates.x / core.viewport.totalWidth - 0.5), 2. * (canvasCoordinates.y / core.viewport.totalWidth - 0.5), 0., 0. ]));
    var solarCoordinates3Dz = Math.sqrt(1 - solarCoordinates.dot(solarCoordinates));

    var solarCoordinates3D = solarCoordinates.dup();
    solarCoordinates3D.elements[2] = solarCoordinates3Dz;

    var deltaX = solarCoordinates.elements[0] - lastMouse.elements[0];
    var deltaY = -(solarCoordinates.elements[1] - lastMouse.elements[1]);
    phi = Math.atan(solarCoordinates3D.elements[0] / solarCoordinates3D.elements[2]);
    theta = Math.acos(solarCoordinates3D.elements[1]) - Math.PI / 2.;
    M1 = Matrix.Rotation((lastTheta - theta), $V([ 1, 0, 0 ]));
    M2 = Matrix.Rotation(-(lastPhi - phi), $V([ 0, 1, 0 ]));
    M = M2.x(M1);
    if (!(isNaN(phi) || isNaN(lastPhi) || isNaN(theta) || isNaN(lastTheta))) {
        core.theta += (lastTheta - theta);
        core.phi += (lastPhi - phi);
    }

    lastPhi = phi;
    lastTheta = theta;
}

handleMouseWheel = function(event) {

    var target = event.target || event.srcElement;
    var rect = target.getBoundingClientRect();
    var offsetX = event.clientX - rect.left;
    offsetY = event.clientY - rect.top;

    var newX = offsetX;
    var newY = core.viewport.totalHeight - offsetY;

    var vcl = core.viewport.totalWidth / core.viewport.columns;
    var vrl = core.viewport.totalHeight / core.viewport.rows;
    var index;
    var quit = false;

    for (var ll = 0; !quit && ll < core.viewport.columns; ll++) {
        for (var rr = 0; !quit && rr < core.viewport.rows; rr++) {
            if (newX >= ll * vcl && newX <= ll * vcl + vcl && newY >= rr * vrl && newY <= rr * vrl + vrl) {
                quit = true;
                index = core.viewport.columns * (core.viewport.rows - 1 - rr) + ll;
            }
        }
    }

    var wheel = event.wheelDelta / 120;// n or -n

    var zoom = 1 + wheel / 2;
    core.zoom[index] = core.zoom[index] * zoom;
    if (core.zoom[index] < 0.1) {
        core.zoom[index] = 0.1;
    }
    if (core.zoom[index] > 5.0) {
        core.zoom[index] = 5.0;
    }
    core.computeProjectionMatrix(index);
    event.preventDefault();
}<|endoftext|>


<commit_before>var fs = require('fs');

var text = fs.readFileSync(__dirname + '/../index.js', 'utf8');

var textarea = document.querySelector('textarea#doc');
var crate = require('crate-core');

var diffsToOps = (diffs) => {
  var ops = [];
  var pointer = 0;
  var oldPointer = pointer;

  diffs.forEach(([type, text]) => {
    switch (type) {
      case 0:
        pointer += text.length;
        break;
      case 1:
        pointer += text.length;
        ops.push({
          text,
          action: 'insert',
          start: oldPointer,
          end: pointer
        });
        break;
      case -1:
        ops.push({
          text,
          action: 'remove',
          start: oldPointer,
          end: oldPointer + text.length
        });
        break;
    }
    oldPointer = pointer;
  });
  return ops;
};

module.exports = stream => {
  var doc = new crate(null, stream);
  window.doc = doc;

  if (stream.server) {
    console.log('Sending initial state');

    // Sssshhhh
    var send = stream.send;
    stream.send = () => {};

    // Start off doc with contents of textarea.
    doc.insert('\n', 0);

    stream.send = send;

    var initial = JSON.stringify(doc.sequence);
    doc.broadcast.send(initial);
    edit(doc);
  } else {
    console.log('Awaiting initial state...');
    var cb = (initial) => {
      console.log('Received initial state...');
      doc.sequence.fromJSON(JSON.parse(initial));
      edit(doc);
    };
    doc.broadcast.once('message', cb);
  }
}

function getStringChildNode(childNode){
  var result = '';
  if (childNode.e !== null){ result = childNode.e; };
  for (var i=0; i<childNode.children.length; ++i){
    result += getStringChildNode(childNode.children[i]);
  };
  return result;
};

var docToString = (doc) => getStringChildNode(doc.sequence.root);

var isConsistent = (doc, editor) => docToString(doc) === editor.getContent();

var edit = (doc) => {
  var editor = window.editor;

  editor.setContent(docToString(doc));

  var debug = () => {
    console.log(docToString(doc));

    if(!isConsistent(doc, editor)) {
      console.warn('INCONSISTENT');
      debugger;
    }
  };

  window.docAsString = () => docToString(doc);
  window.debug = debug;

  // var insertRemoveOp = false;
  //
  // editor.getSession().getSelection().on('changeCursor', function(e, sel){
  //   if (!insertRemoveOp){
  //     var range = sel.getRange();
  //     doc.caretMoved({
  //       start: editor.getSession().getDocument().positionToIndex(range.start),
  //       end: editor.getSession().getDocument().positionToIndex(range.end)
  //     });
  //   }
  //   insertRemoveOp = false;
  // });

  editor.on('contentChangedExt', (_, diffs) => {
    var ops = diffsToOps(diffs);
    console.log(ops);

    ops.forEach(({ start, end, text, action }) => {
      var j = 0;
      for (var i=start; i<end; ++i){
        switch (action){
          case 'insert':
            console.log('insert: ' + text[j] + ' at ' + i);
            doc.insert(text[j], i);
            break;
          case 'remove':
            console.log('remove: ' + text[j] + ' at ' + start);
            doc.remove(start);
            break;
        };
        ++j;
      };
    });

    debug();
  });

  doc.on('remoteInsert', (element, index) => {
    if (index === -1) { return; }
    editor.watcherExt.noWatch(() => {
      console.log('remote insert: ' + element + ' at ' + index);
      editor.replace(index-1, index-1, element);
    });
    debug();
  });

  doc.on('remoteRemove', (index) => {
    if (index === -1) { return; }
    editor.watcherExt.noWatch(() => {
      console.log('remote remove: ' + editor.getContent().split('')[index-1] + ' at ' + (index-1));
      editor.replace(index-1, index, '');
    });
    debug();
  });
};
<commit_msg>Disable autoscroll when receiving updates from remote.<commit_after>var fs = require('fs');

var text = fs.readFileSync(__dirname + '/../index.js', 'utf8');

var textarea = document.querySelector('textarea#doc');
var crate = require('crate-core');

var diffsToOps = (diffs) => {
  var ops = [];
  var pointer = 0;
  var oldPointer = pointer;

  diffs.forEach(([type, text]) => {
    switch (type) {
      case 0:
        pointer += text.length;
        break;
      case 1:
        pointer += text.length;
        ops.push({
          text,
          action: 'insert',
          start: oldPointer,
          end: pointer
        });
        break;
      case -1:
        ops.push({
          text,
          action: 'remove',
          start: oldPointer,
          end: oldPointer + text.length
        });
        break;
    }
    oldPointer = pointer;
  });
  return ops;
};

module.exports = stream => {
  var doc = new crate(null, stream);
  window.doc = doc;

  if (stream.server) {
    console.log('Sending initial state');

    // Sssshhhh
    var send = stream.send;
    stream.send = () => {};

    // Start off doc with contents of textarea.
    doc.insert('\n', 0);

    stream.send = send;

    var initial = JSON.stringify(doc.sequence);
    doc.broadcast.send(initial);
    edit(doc);
  } else {
    console.log('Awaiting initial state...');
    var cb = (initial) => {
      console.log('Received initial state...');
      doc.sequence.fromJSON(JSON.parse(initial));
      edit(doc);
    };
    doc.broadcast.once('message', cb);
  }
}

function getStringChildNode(childNode){
  var result = '';
  if (childNode.e !== null){ result = childNode.e; };
  for (var i=0; i<childNode.children.length; ++i){
    result += getStringChildNode(childNode.children[i]);
  };
  return result;
};

var docToString = (doc) => getStringChildNode(doc.sequence.root);

var isConsistent = (doc, editor) => docToString(doc) === editor.getContent();

var edit = (doc) => {
  var editor = window.editor;

  editor.setContent(docToString(doc));

  var debug = () => {
    console.log(docToString(doc));

    if(!isConsistent(doc, editor)) {
      console.warn('INCONSISTENT');
      debugger;
    }
  };

  window.docAsString = () => docToString(doc);
  window.debug = debug;

  // var insertRemoveOp = false;
  //
  // editor.getSession().getSelection().on('changeCursor', function(e, sel){
  //   if (!insertRemoveOp){
  //     var range = sel.getRange();
  //     doc.caretMoved({
  //       start: editor.getSession().getDocument().positionToIndex(range.start),
  //       end: editor.getSession().getDocument().positionToIndex(range.end)
  //     });
  //   }
  //   insertRemoveOp = false;
  // });

  editor.on('contentChangedExt', (_, diffs) => {
    var ops = diffsToOps(diffs);
    console.log(ops);

    ops.forEach(({ start, end, text, action }) => {
      var j = 0;
      for (var i=start; i<end; ++i){
        switch (action){
          case 'insert':
            console.log('insert: ' + text[j] + ' at ' + i);
            doc.insert(text[j], i);
            break;
          case 'remove':
            console.log('remove: ' + text[j] + ' at ' + start);
            doc.remove(start);
            break;
        };
        ++j;
      };
    });

    debug();
  });

  doc.on('remoteInsert', (element, index) => {
    if (index === -1) { return; }
    editor.watcherExt.noWatch(() => {
      console.log('remote insert: ' + element + ' at ' + index);
      editor.replace(index-1, index-1, element, false);
    });
    debug();
  });

  doc.on('remoteRemove', (index) => {
    if (index === -1) { return; }
    editor.watcherExt.noWatch(() => {
      console.log('remote remove: ' + editor.getContent().split('')[index-1] + ' at ' + (index-1));
      editor.replace(index-1, index, '', false);
    });
    debug();
  });
};
<|endoftext|>


<commit_before>const safeExtend = require('../lib/safe-extend')
const Issuers = require('../models/issuer');

exports = module.exports = function applyIssuerRoutes (server) {

  server.get('/issuers', showAllIssuers);
  function showAllIssuers(req, res, next) {
    Issuers.get({}, function foundRows(error, rows) {
      if (error)
        return handleError(error, null, res, next)

      return res.send({issuers: rows.map(issuerFromDb)});
    });
  }

  server.post('/issuers', saveIssuer);
  function saveIssuer(req, res, next) {
    const row = fromPostToRow(req.body);
    const validationErrors = Issuers.validateRow(row);

    if (validationErrors.length) {
      return res.send(400, {
        code: 'ValidationError',
        message: 'Could not validate required fields',
        details: validationErrors,
      });
    }

    Issuers.put(row, function savedRow(error, result) {
      if (error)
        return handleError(error, row, res, next)

      return res.send(201, {status: 'created', issuer: row})
    });
  }

  server.get('/issuers/:issuerId', showOneIssuer);
  function showOneIssuer(req, res, next) {
    getIssuer(req, res, next, function (row) {
      return res.send({issuer: issuerFromDb(row)});
    });
  }

  server.del('/issuers/:issuerId', deleteIssuer);
  function deleteIssuer(req, res, next) {
    getIssuer(req, res, next, function (row) {
      Issuers.del(row, function deletedRow(error, result) {
        if (error)
          return handleError(error, row, req, next)
        return res.send({status: 'deleted', issuer: row});
      });
    });
  }

  server.put('/issuers/:issuerId', updateIssuer);
  function updateIssuer(req, res, next) {
    getIssuer(req, res, next, function (row) {
      const updated = safeExtend(row, req.body)
      Issuers.put(updated, function updatedRow(error, result) {
        if (error)
          return handleError(error, row, res, next)
        return res.send({status: 'updated', issuer: row})
      })
    });
  }

};

function getIssuer(req, res, next, callback) {
  const query = {slug: req.params.issuerId};
  Issuers.getOne(query, function foundIssuer(error, row) {
    if (error)
      return handleError(error, row, res, next)

    if (!row)
      return res.send(404, {error: 'not found'});

    return callback(row)
  });
}

const errorCodes = {
  ER_DUP_ENTRY: [409, {error: 'An issuer with that `slug` already exists'}]
}

function handleError(error, row, res, next) {
  const expected = knownError(error, row)
  if (!expected) return next(error)
  res.send.apply(res, expected)
  return next()
}

function knownError(error, row) {
  const err = errorCodes[error.code];
  if (!err) return;
  if (row) err[1].received = row
  return err;
}

function fromPostToRow(post) {
  return {
    slug: post.slug,
    url: post.url,
    name: post.name,
    email: post.email,
  }
}

function issuerFromDb(row) {
  return {
    id: row.id,
    slug: row.slug,
    url: row.url,
    name: row.name,
    email: row.email,
  }
}
<commit_msg>Better issuer 404<commit_after>const restify = require('restify')
const safeExtend = require('../lib/safe-extend')
const Issuers = require('../models/issuer');

exports = module.exports = function applyIssuerRoutes (server) {

  server.get('/issuers', showAllIssuers);
  function showAllIssuers(req, res, next) {
    Issuers.get({}, function foundRows(error, rows) {
      if (error)
        return handleError(error, null, res, next)

      return res.send({issuers: rows.map(issuerFromDb)});
    });
  }

  server.post('/issuers', saveIssuer);
  function saveIssuer(req, res, next) {
    const row = fromPostToRow(req.body);
    const validationErrors = Issuers.validateRow(row);

    if (validationErrors.length) {
      return res.send(400, {
        code: 'ValidationError',
        message: 'Could not validate required fields',
        details: validationErrors,
      });
    }

    Issuers.put(row, function savedRow(error, result) {
      if (error)
        return handleError(error, row, res, next)

      return res.send(201, {status: 'created', issuer: row})
    });
  }

  server.get('/issuers/:issuerId', showOneIssuer);
  function showOneIssuer(req, res, next) {
    getIssuer(req, res, next, function (row) {
      return res.send({issuer: issuerFromDb(row)});
    });
  }

  server.del('/issuers/:issuerId', deleteIssuer);
  function deleteIssuer(req, res, next) {
    getIssuer(req, res, next, function (row) {
      Issuers.del(row, function deletedRow(error, result) {
        if (error)
          return handleError(error, row, req, next)
        return res.send({status: 'deleted', issuer: row});
      });
    });
  }

  server.put('/issuers/:issuerId', updateIssuer);
  function updateIssuer(req, res, next) {
    getIssuer(req, res, next, function (row) {
      const updated = safeExtend(row, req.body)
      Issuers.put(updated, function updatedRow(error, result) {
        if (error)
          return handleError(error, row, res, next)
        return res.send({status: 'updated', issuer: row})
      })
    });
  }

};

function getIssuer(req, res, next, callback) {
  const query = {slug: req.params.issuerId};
  Issuers.getOne(query, function foundIssuer(error, row) {
    if (error)
      return handleError(error, row, res, next)

    if (!row) {
      const notFoundErr = new restify.ResourceNotFoundError('Could not find issuer with slug `'+query.slug+'`')
      return next(notFoundErr);
    }

    return callback(row)
  });
}

const errorCodes = {
  ER_DUP_ENTRY: [409, {error: 'An issuer with that `slug` already exists'}]
}

function handleError(error, row, res, next) {
  const expected = knownError(error, row)
  if (!expected) return next(error)
  res.send.apply(res, expected)
  return next()
}

function knownError(error, row) {
  const err = errorCodes[error.code];
  if (!err) return;
  if (row) err[1].received = row
  return err;
}

function fromPostToRow(post) {
  return {
    slug: post.slug,
    url: post.url,
    name: post.name,
    email: post.email,
  }
}

function issuerFromDb(row) {
  return {
    id: row.id,
    slug: row.slug,
    url: row.url,
    name: row.name,
    email: row.email,
  }
}
<|endoftext|>


<commit_before>'use strict';
var athena = (function() {
  var ORNAGAI = 'http://www.ornagai.com/index.php/api/word/q/';
  var $tooltip;

  var makeRequest = function(word, success, error) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', ORNAGAI + word);
    xhr.addEventListener('load', function() {
      if(xhr.status === 200) {
        success(xhr.status, xhr.responseText);
      } else {
        if(error) {
          error(xhr.status, xhr);
        }
      }
    });
    xhr.send();
  };

  var inOrnagai = function(word, callback) {
    var result;
    makeRequest(word, function(status, response) {
      var words = JSON.parse(response);
      result = words.filter(function(value) {
        return value.word.toLowerCase() === word.toLowerCase();
      });
      callback(result);
    }, function() {
      result = [];
      callback(result);
    });
  };

  // var inOxford = function(word) {
    // console.log('looking up ' + word + ' in Oxford');
  // };

  var renderTooltip = function(word, result, selected, e) {
    $tooltip = document.getElementById('athena-tooltip') ||
                    document.createElement('div');
    $tooltip.innerHTML = '';
    $tooltip.id = 'athena-tooltip';

    var $word = document.createElement('div');
    $word.classList.add('word');
    $word.innerText = word;

    var $icon = document.createElement('div');
    $icon.classList.add('audio-icon');
    $icon.innerHTML = '';

    var $mmdef = document.createElement('div');
    $mmdef.classList.add('mmdef');
    if(result.length > 0) {
      for(var r in result) {
        var $r = document.createElement('span');
        $r.innerText = result[r].def;
        $mmdef.appendChild($r);
      }
    } else {
      var $span = document.createElement('span');
      $span.innerText = 'No Myanmar definition';
      $mmdef.appendChild($span);
    }

    $tooltip.appendChild($word);
    $tooltip.appendChild($icon);
    $tooltip.appendChild($mmdef);
    $tooltip.style.position = 'absolute';
    document.body.appendChild($tooltip);
    $tooltip.style.top = (e.pageY - $tooltip.clientHeight - 27) + 'px';
    $tooltip.style.left = (e.pageX - ($tooltip.clientWidth/2)) + 'px';
    $tooltip.style.visibility = 'visible';
    $tooltip.style.opacity = 1;
  };

  return {
    lookUp: function(word, selected, e) {
      inOrnagai(word, function(result) {
        renderTooltip(word, result, selected, e);
      });
    },
  };

})();

var on = false;

document.addEventListener('dblclick', function(e) {
  e.stopPropagation();
  var selected = window.getSelection();
  var selectedText = selected.toString();
  athena.lookUp(selectedText, selected.getRangeAt(0), e);
  on = true;
});

window.addEventListener('resize', function() {
});

document.body.addEventListener('click', function(e) {
  // walk up the DOM tree to check whether
  // you are clicking inside the tooltip
  var tooltipPresent = false;
  for(var element = e.target; element; element = element.parentNode) {
    if(element.id === 'athena-tooltip') {
      tooltipPresent = true;
      break;
    }
  }

  if(!tooltipPresent) {
    var $tooltip = document.getElementById('athena-tooltip');
    if($tooltip !== null) {
      $tooltip.parentNode.removeChild($tooltip);
    }
  }
});
<commit_msg>Dismiss tooltip on window resize<commit_after>'use strict';
var athena = (function() {
  var ORNAGAI = 'http://www.ornagai.com/index.php/api/word/q/';
  var $tooltip;

  var makeRequest = function(word, success, error) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', ORNAGAI + word);
    xhr.addEventListener('load', function() {
      if(xhr.status === 200) {
        success(xhr.status, xhr.responseText);
      } else {
        if(error) {
          error(xhr.status, xhr);
        }
      }
    });
    xhr.send();
  };

  var inOrnagai = function(word, callback) {
    var result;
    makeRequest(word, function(status, response) {
      var words = JSON.parse(response);
      result = words.filter(function(value) {
        return value.word.toLowerCase() === word.toLowerCase();
      });
      callback(result);
    }, function() {
      result = [];
      callback(result);
    });
  };

  // var inOxford = function(word) {
    // console.log('looking up ' + word + ' in Oxford');
  // };

  var renderTooltip = function(word, result, selected, e) {
    $tooltip = document.getElementById('athena-tooltip') ||
                    document.createElement('div');
    $tooltip.innerHTML = '';
    $tooltip.id = 'athena-tooltip';

    var $word = document.createElement('div');
    $word.classList.add('word');
    $word.innerText = word;

    var $icon = document.createElement('div');
    $icon.classList.add('audio-icon');
    $icon.innerHTML = '';

    var $mmdef = document.createElement('div');
    $mmdef.classList.add('mmdef');
    if(result.length > 0) {
      for(var r in result) {
        var $r = document.createElement('span');
        $r.innerText = result[r].def;
        $mmdef.appendChild($r);
      }
    } else {
      var $span = document.createElement('span');
      $span.innerText = 'No Myanmar definition';
      $mmdef.appendChild($span);
    }

    $tooltip.appendChild($word);
    $tooltip.appendChild($icon);
    $tooltip.appendChild($mmdef);
    $tooltip.style.position = 'absolute';
    document.body.appendChild($tooltip);
    $tooltip.style.top = (e.pageY - $tooltip.clientHeight - 20) + 'px';
    $tooltip.style.left = (e.pageX - ($tooltip.clientWidth/2)) + 'px';
    $tooltip.style.visibility = 'visible';
    $tooltip.style.opacity = 1;
  };

  return {
    lookUp: function(word, selected, e) {
      inOrnagai(word, function(result) {
        renderTooltip(word, result, selected, e);
      });
    },
  };

})();

var on = false;

document.addEventListener('dblclick', function(e) {
  e.stopPropagation();
  var selected = window.getSelection();
  var selectedText = selected.toString();
  athena.lookUp(selectedText, selected.getRangeAt(0), e);
  on = true;
});

window.addEventListener('resize', function() {
  var $tooltip = document.getElementById('athena-tooltip');
  if($tooltip !== null) {
    $tooltip.parentNode.removeChild($tooltip);
    return false;
  }
});

document.body.addEventListener('click', function(e) {
  // walk up the DOM tree to check whether
  // you are clicking inside the tooltip
  var tooltipPresent = false;
  for(var element = e.target; element; element = element.parentNode) {
    if(element.id === 'athena-tooltip') {
      tooltipPresent = true;
      break;
    }
  }

  if(!tooltipPresent) {
    var $tooltip = document.getElementById('athena-tooltip');
    if($tooltip !== null) {
      $tooltip.parentNode.removeChild($tooltip);
    }
  }
});
<|endoftext|>


<commit_before>'use strict';

/**
 * Rhizome - The API that feeds grassroots movements
 *
 * @file logging.js
 * @description Logging helpers
 * @module System
 * @author Chris Bates-Keegan
 *
 * @todo Centralise this logging across the app
 * @todo Add other log targets ie to datastore
 *
 */

const logFormat = Date.ISO8601_DATETIME;
var logPrefix = module.exports.logPrefix = () => {
  Date.create().format(logFormat);
};

/**
 *
 * @param {string} string - log entry
 * @private
 */
function _log(string) {
  console.log(string);
}

/**
 * STANDARD LOGGING
 */

/**
 * @param {string} log - Text to log
 * @return {function(*)} - returns a function for chaining into a promise
 */
module.exports.log = log => {
  return res => {
    _log(`${logPrefix()} - ${log}: ${res}`);
    return res;
  };
};

/**
 * @param {string} log - Text to log
 * @param {*} val - value to test `res` against
 * @return {function(*)} - returns a function for chaining into a promise
 */
module.exports.logIf = (log, val) => {
  return res => {
    if (val === res) {
      _log(`${logPrefix()} - ${log}: ${res}`);
    }
    return res;
  };
};

/**
 * @param {string} log - Text to log
 * @param {*} val - value to test `res` against
 * @return {function(*)} - returns a function for chaining into a promise
 */
module.exports.logIfNot = (log, val) => {
  return res => {
    if (val !== res) {
      _log(`${logPrefix()} - ${log}: ${res}`);
    }
    return res;
  };
};

/**
 * PROPERTY LOGGING
 */

/**
 * @param {string} log - Text to log
 * @param {string} prop - Name of the `res` property to log
 * @return {function(*)} - returns a function for chaining into a promise
 */
module.exports.logProp = (log, prop) => {
  return res => {
    _log(`${logPrefix()} - ${log}: ${res[prop]}`);
    return res;
  };
};

/**
 * @param {string} log - Text to log
 * @param {string} prop - Name of the `res` property to log
 * @param {*} val - value to test `res` against
 * @return {function(*)} - returns a function for chaining into a promise
 */
module.exports.logPropIf = (log, prop, val) => {
  return res => {
    if (val === res[prop]) {
      _log(`${logPrefix()} - ${log}: ${res[prop]}`);
    }
    return res;
  };
};

/**
 * @param {string} log - Text to log
 * @param {string} prop - Name of the `res` property to log
 * @param {*} val - value to test `res` against
 * @return {function(*)} - returns a function for chaining into a promise
 */
module.exports.logPropIfNot = (log, prop, val) => {
  return res => {
    if (val !== res[prop]) {
      _log(`${logPrefix()} - ${log}: ${res[prop]}`);
    }
    return res;
  };
};

/**
 * ARRAY LOGGING
 */

/**
 * @param {string} log - Text to log
 * @return {function(*)} - returns a function for chaining into a promise
 */
module.exports.logArray = log => {
  return res => {
    console.log(`${logPrefix()} - ${log}: ${res.length}`);
    res.forEach(r => {
      _log(r);
    });
    return res;
  };
};

/**
 * @param {string} log - Text to log
 * @param {string} prop - Name of the `res[]` property to log
 * @return {function(*)} - returns a function for chaining into a promise
 */
module.exports.logArrayProp = (log, prop) => {
  return res => {
    console.log(`${logPrefix()} - ${log}: ${res.length}`);
    res.forEach(r => {
      _log(r[prop]);
    });
    return res;
  };
};
<commit_msg>- UPDATED: Added log level framework.<commit_after>'use strict';

/**
 * Rhizome - The API that feeds grassroots movements
 *
 * @file logging.js
 * @description Logging helpers
 * @module System
 * @author Chris Bates-Keegan
 *
 * @todo Centralise this logging across the app
 * @todo Add other log targets ie to datastore
 *
 */

const logFormat = Date.ISO8601_DATETIME;
var logPrefix = module.exports.logPrefix = () => {
  return Date.create().format(logFormat);
};

var LogLevel = {
  NONE: 0,
  ERR: 1,
  WARN: 2,
  INFO: 3,
  VERBOSE: 4,
  DEFAULT: 3
};

module.exports.Constants = {
  LogLevel: LogLevel
};
var _logLevel = LogLevel.INFO;

/**
 *
 * @param {string} log - log entry
 * @param {integer} level - level to log at
 * @private
 */
function _log(log, level) {
  if (_logLevel >= level) {
    console.log(log);
  }
}

/**
 * STANDARD LOGGING
 */

module.exports.setLogLevel = level => {
  _logLevel = level;
};

/**
 * @param {string} log - Text to log
 * @param {integer} level - level to log at
 */
module.exports.stdLog = (log, level) => {
  level = level || LogLevel.DEFAULT;
  _log(`${logPrefix()} - ${log}`, level);
};

/**
 * PROMISE LOGGING
 */

/**
 * @param {string} log - Text to log
 * @param {integer} level - level to log at
 * @return {function(*)} - returns a function for chaining into a promise
 */
module.exports.log = (log, level) => {
  level = level || LogLevel.DEFAULT;
  return res => {
    _log(`${logPrefix()} - ${log}: ${res}`, level);
    return res;
  };
};

/**
 * @param {string} log - Text to log
 * @param {*} val - value to test `res` against
 * @param {integer} level - level to log at
 * @return {function(*)} - returns a function for chaining into a promise
 */
module.exports.logIf = (log, val, level) => {
  level = level || LogLevel.DEFAULT;
  return res => {
    if (val === res) {
      _log(`${logPrefix()} - ${log}: ${res}`, level);
    }
    return res;
  };
};

/**
 * @param {string} log - Text to log
 * @param {*} val - value to test `res` against
 * @param {integer} level - level to log at
 * @return {function(*)} - returns a function for chaining into a promise
 */
module.exports.logIfNot = (log, val, level) => {
  level = level || LogLevel.DEFAULT;
  return res => {
    if (val !== res) {
      _log(`${logPrefix()} - ${log}: ${res}`, level);
    }
    return res;
  };
};

/**
 * PROPERTY LOGGING
 */

/**
 * @param {string} log - Text to log
 * @param {string} prop - Name of the `res` property to log
 * @param {integer} level - level to log at
 * @return {function(*)} - returns a function for chaining into a promise
 */
module.exports.logProp = (log, prop, level) => {
  level = level || LogLevel.DEFAULT;
  return res => {
    _log(`${logPrefix()} - ${log}: ${res[prop]}`, level);
    return res;
  };
};

/**
 * @param {string} log - Text to log
 * @param {string} prop - Name of the `res` property to log
 * @param {*} val - value to test `res` against
 * @param {integer} level - level to log at
 * @return {function(*)} - returns a function for chaining into a promise
 */
module.exports.logPropIf = (log, prop, val, level) => {
  level = level || LogLevel.DEFAULT;
  return res => {
    if (val === res[prop]) {
      _log(`${logPrefix()} - ${log}: ${res[prop]}`, level);
    }
    return res;
  };
};

/**
 * @param {string} log - Text to log
 * @param {string} prop - Name of the `res` property to log
 * @param {*} val - value to test `res` against
 * @param {integer} level - level to log at
 * @return {function(*)} - returns a function for chaining into a promise
 */
module.exports.logPropIfNot = (log, prop, val, level) => {
  level = level || LogLevel.DEFAULT;
  return res => {
    if (val !== res[prop]) {
      _log(`${logPrefix()} - ${log}: ${res[prop]}`, level);
    }
    return res;
  };
};

/**
 * ARRAY LOGGING
 */

/**
 * @param {string} log - Text to log
 * @param {integer} level - level to log at
 * @return {function(*)} - returns a function for chaining into a promise
 */
module.exports.logArray = (log, level) => {
  level = level || LogLevel.DEFAULT;
  return res => {
    console.log(`${logPrefix()} - ${log}: ${res.length}`, level);
    res.forEach(r => {
      _log(r);
    });
    return res;
  };
};

/**
 * @param {string} log - Text to log
 * @param {string} prop - Name of the `res[]` property to log
 * @param {integer} level - level to log at
 * @return {function(*)} - returns a function for chaining into a promise
 */
module.exports.logArrayProp = (log, prop, level) => {
  level = level || LogLevel.DEFAULT;
  return res => {
    console.log(`${logPrefix()} - ${log}: ${res.length}`, level);
    res.forEach(r => {
      _log(r[prop]);
    });
    return res;
  };
};
<|endoftext|>


<commit_before>// server (priveleged) methods, can run sync.
// so: files, shareJS, and top-level functions
// dlog is debugger log, see server/setup.js

Meteor.methods({

  //////////////////
  // FILE MANAGEMENT
  //////////////////

  newFile: function() { // create a new unnamed file
    return Meteor.call('createFile', {path: 'untitled'});
  },

  createFile: function(file) { // create or update a file, make sjs doc

    // handle null fields (from inserting locally)
    // (isMember ? "$2.00" : "$10.00")
    file.content = file.content || '';
    file.src = file.raw || '';
    file.raw = file.src || '';

    var fs = Files.upsert({
      repo: Meteor.user().profile.repo,
      branch: Meteor.user().profile.repoBranch,
      title: file.path,
    },{ $set: {
      content: file.content,
      cache: file.content,
      src: file.raw, // linked to for unsupported filetypes
      raw: file.src, // used for rendering images
    }});

    if (fs.insertedId) { // if a new file made, create sharejs
      Meteor.call('addMessage', ' created file - ' + file.path);
      Meteor.call('newShareJSDoc', fs.insertedId);
      return fs.insertedId;
    }
  },

  deleteFile: function(id) { // with id, delete a file from system
    ShareJS.model.delete(id);
    Files.remove(id);
    Docs.remove(id);
  },

  resetFile: function(id) { // reset file back to cached version
    var o = Files.findOne(id); // overwrite content
    if (o) {
      Files.update(id, {$set: {content: o.cache}});
      var n = Files.findOne(id); // get new version
      Meteor.call('postShareJS', n); // load into sharejs
    }
  },

  resetFiles: function() { // reset db and hard code simple website structure
    Files.find({}).map(function delFile(f){ Meteor.call('deleteFile', f._id)});
    var base = [{'title':'site.html'},{'title':'site.css'},{'title':'site.js'}];
    base.map(function(f){ Meteor.call('createFile', f) });
  },

  /////////////////////
  // SHAREJS MANAGEMENT
  /////////////////////

  newShareJSDoc: function(id) { // create sharejs document with same id
    var time = Math.round( new Date() / 1000 );
    ShareJS.model.create(id, 'text', { mtime: time, ctime: time });
  },

  getShareJSDoc: function(file) { // give live editor copy, v and snapshot
    if(! file._id ) return null;
    var sjs = Docs.findOne( file._id );
    if (sjs)
      return sjs.data;
    else
      Meteor.call('newShareJSDoc', file._id);
    return Meteor.call('getShareJSDoc', file._id);
  },

  postShareJS: function(file) { // update files with their ids
    var sjs = Meteor.call('getShareJSDoc', file) // get doc and version
    if (!sjs) return null; // if file id broke, don't propagate error
    ShareJS.model.applyOp( file._id, {
      op: [
        { p:0, d: sjs.snapshot }, // delete old content
        { p:0, i: file.content } // insert new blob content
      ],
      meta: null,
      v: sjs.v // apply it to last seen version
    });
  },

  postAllShareJS: function(file) { // update all project sjs files
    Files.find({
      repo:  Meteor.user().profile.repo,
      branch: Meteor.user().profile.repoBranch,
    }).map(function setSJS(file) {
      Meteor.call('postShareJS', file);
    });
  },

  testShareJS: function() { // update file.content from sjs
    Files.find({
      repo:  Meteor.user().profile.repo,
      branch: Meteor.user().profile.repoBranch,
    }).map(function readSJS(file) {
      Files.update(
        file._id,
        {$set: {
          content: Meteor.call('getShareJSDoc', file).snapshot
        }});
    });
  },

  ///////////////////
  // ISSUE MANAGEMENT
  ///////////////////

  initIssues: function() { // re-populating git repo issues
    var repo = Repos.findOne(Meteor.user().profile.repo);
    if (repo) {
      Meteor.call('getAllIssues', repo).map(function load(issue) {
        Issues.upsert({
          repo: repo._id,
          ghid: issue.id // (from github)
        },{
          $set: {issue: issue},
        });
      });
    }
  },

  addIssue: function(feedback){ // adds a feedback issue to github

    feedback.imglink = Async.runSync(function(done) { // save screens, give id
      Screens.insert({img: feedback.img}, function(err, id){
        done(err, id);
      });
    }).result; // attach screenshot to this issue
    delete feedback.img; // delete redundant png

    // insert a dummy issue to get id, use later in GH issue body txt
    var issueId = Async.runSync(function(done){
      Issues.insert({issue: null}, function(err, id){
        done(err, id);
      });
    }).result; // get the id of the newly inserted issue

    // construct and append the text of the github issue, including links to screenshot and demo
    var imglink = '[issue screenshot](http://codepilot.meteor.com/screenshot/' + feedback.imglink + ')\n';
    var livelink = '[live code here](http://codepilot.meteor.com/render/' + issueId + ')\n';
    var htmllink = 'html:\n```html\n' + feedback.html + '\n```\n';
    var csslink = 'css:\n```css\n' + feedback.css + '\n```\n';
    var jslink = 'js:\n```js\n' + feedback.js + '\n```\n';
    var loglink = 'console log:\n```\n' + feedback.log + '```\n';
    feedback.body = imglink + livelink + htmllink + csslink + jslink + loglink;

    // post the issue to github, and get the GH generated content
    var issue = Meteor.call('postIssue', feedback);
    var ghIssue = { // the entire issue object
      _id: issueId,
      ghid: issue.id, // (from github)
      repo: feedback.repo, // attach repo forming data
      feedback: feedback, // attach feedback issue data
      issue: issue // returned from github call
    };

    // insert complete issue, and add it to the feed
    Issues.update(issueId, ghIssue);
    Meteor.call(
      'addUserMessage',
      feedback.user,
      'opened issue - ' + feedback.note
    );
  },

  closeIssue: function(issue){ // close an issue on github by number
    Meteor.call('ghAuth');
    Meteor.call('addMessage', 'closed issue - ' + issue.issue.title);
    github.issues.edit({
      user: Meteor.user().profile.repoOwner,
      repo: Meteor.user().profile.repoName,
      number: issue.issue.number,
      state: 'closed'
    });
    Issues.remove(issue._id); // remove from the local database
  },

  /////////////////////
  // COMMIT MANAGEMENT
  /////////////////////

  initCommits: function() { // re-populating the commit log
    Meteor.call('getAllCommits').map(function(c){
      Meteor.call('addCommit', c);
    });
  },

  addCommit: function(c) { // adds a commit, links to repo + branch
    Commits.upsert({
      repo: Meteor.user().profile.repo,
      branch: Meteor.user().profile.repoBranch,
      sha: c.sha
    },{
      $set: { commit: c }
    });
  },

  loadHead: function(bname) { // load head of branch, from sha
    var sha =  Meteor.call('getBranch', bname).commit.sha;
    if (sha) Meteor.call('loadCommit', sha);
  },

  loadCommit: function(sha) { // takes commit sha, loads into sjs
    var commitResults = Meteor.call('getCommit', sha);
    var treeSHA = commitResults.commit.tree.sha;
    var treeResults = Meteor.call('getTree', treeSHA);
    treeResults.tree.forEach(function updateBlob(blob) {

      if (blob.type === 'blob') { // only load files, not folders/trees

        var image = /\.(gif|jpg|jpeg|tiff|png|bmp)$/i;

        if (image.test(blob.path)) { // get the encoded file content

          var img = Async.runSync(function(done) { // wait on github response
            var content = Meteor.call('getContent', blob.path);
            done(content, content);
          }).result;

          blob.src = image.html_url;
          blob.raw = image.download_url;
          blob.content = '';

        } else { // get the raw file content

          blob.content = Async.runSync(function(done) { // wait on github response
            var content = Meteor.call('getBlob', blob);
            done(content, content);
          }).result;

        }

        Meteor.call('createFile', blob);
      }

    });

    Meteor.call('postAllShareJS');

  },

  ////////////////////////////////////////////////////////
  // top level function, grab files and commit to github
  ////////////////////////////////////////////////////////

  newCommit: function(msg) { // grab sjs contents, commit to github

    // getting all file ids, names, and content
    var user = Meteor.user().profile;
    var bname = user.repoBranch;
    var blobs = Files.find({
      repo: user.repo,
      branch: bname,
    }).map(function getBlob(file){ // update file cache too
      Files.update(file._id, {$set: {cache: file.content}});
      return {
        path: file.title,
        mode: '100644',
        type: 'blob',
        content: file.content
      };
    });

    // get old tree and update it with new shas, post and get that sha
    var branch = Meteor.call('getBranch', bname);
    var oldTree = Meteor.call('getTree', branch.commit.commit.tree.sha);
    var newTree = {base: oldTree.sha, tree: blobs};
    var treeSHA = Meteor.call('postTree', newTree);

    // specify author of this commit
    var commitAuthor = {
      name: user.name,
      email: user.email,
      date: new Date()
    };

    // make the new commit result object
    var commitResult = Meteor.call('postCommit', {
      message: msg, // passed in
      author: commitAuthor,
      parents: [ branch.commit.sha ],
      tree: treeSHA
    });

    // update the ref, point to new commmit
    Meteor.call('postRef', commitResult);

    // get the latest commit from the branch head
    var lastCommit = Meteor.call('getBranch', bname).commit;

    // post into commit db with repo tag
    Meteor.call('addCommit', lastCommit);

    // update the feed with new commit
    Meteor.call('addMessage', 'commited - ' + msg);

  },

  ///////////////////////////
  // helper & testing methods
  ///////////////////////////

  //http://stackoverflow.com/questions/10677491/how-to-get-meteor-call-to-return-value-for-template
  getCollabs: function(repo) { // get a users profile based on their id
    return repo.users.map(function(uid){
      var user = Meteor.users.findOne(uid);
      if (user.profile.repo === repo._id) { // only return users currently working on project
        return user.profile;
      }
    });
  },

  resetAllData: function() { // detroy everything
    Messages.remove({});
    Commits.remove({});
    Screens.remove({});
    Issues.remove({});
    Repos.remove({});
    Tasks.remove({});
    Files.remove({});
    Docs.remove({});
  },

});
<commit_msg>fix typo<commit_after>// server (priveleged) methods, can run sync.
// so: files, shareJS, and top-level functions
// dlog is debugger log, see server/setup.js

Meteor.methods({

  //////////////////
  // FILE MANAGEMENT
  //////////////////

  newFile: function() { // create a new unnamed file
    return Meteor.call('createFile', {path: 'untitled'});
  },

  createFile: function(file) { // create or update a file, make sjs doc

    // handle null fields (from inserting locally)
    // (isMember ? "$2.00" : "$10.00")
    file.content = file.content || '';
    file.src = file.raw || '';
    file.raw = file.src || '';

    var fs = Files.upsert({
      repo: Meteor.user().profile.repo,
      branch: Meteor.user().profile.repoBranch,
      title: file.path,
    },{ $set: {
      content: file.content,
      cache: file.content,
      src: file.raw, // linked to for unsupported filetypes
      raw: file.src, // used for rendering images
    }});

    if (fs.insertedId) { // if a new file made, create sharejs
      Meteor.call('addMessage', ' created file - ' + file.path);
      Meteor.call('newShareJSDoc', fs.insertedId);
      return fs.insertedId;
    }
  },

  deleteFile: function(id) { // with id, delete a file from system
    ShareJS.model.delete(id);
    Files.remove(id);
    Docs.remove(id);
  },

  resetFile: function(id) { // reset file back to cached version
    var o = Files.findOne(id); // overwrite content
    if (o) {
      Files.update(id, {$set: {content: o.cache}});
      var n = Files.findOne(id); // get new version
      Meteor.call('postShareJS', n); // load into sharejs
    }
  },

  resetFiles: function() { // reset db and hard code simple website structure
    Files.find({}).map(function delFile(f){ Meteor.call('deleteFile', f._id)});
    var base = [{'title':'site.html'},{'title':'site.css'},{'title':'site.js'}];
    base.map(function(f){ Meteor.call('createFile', f) });
  },

  /////////////////////
  // SHAREJS MANAGEMENT
  /////////////////////

  newShareJSDoc: function(id) { // create sharejs document with same id
    var time = Math.round( new Date() / 1000 );
    ShareJS.model.create(id, 'text', { mtime: time, ctime: time });
  },

  getShareJSDoc: function(file) { // give live editor copy, v and snapshot
    if(! file._id ) return null;
    var sjs = Docs.findOne( file._id );
    if (sjs)
      return sjs.data;
    else
      Meteor.call('newShareJSDoc', file._id);
    return Meteor.call('getShareJSDoc', file._id);
  },

  postShareJS: function(file) { // update files with their ids
    var sjs = Meteor.call('getShareJSDoc', file) // get doc and version
    if (!sjs) return null; // if file id broke, don't propagate error
    ShareJS.model.applyOp( file._id, {
      op: [
        { p:0, d: sjs.snapshot }, // delete old content
        { p:0, i: file.content } // insert new blob content
      ],
      meta: null,
      v: sjs.v // apply it to last seen version
    });
  },

  postAllShareJS: function(file) { // update all project sjs files
    Files.find({
      repo:  Meteor.user().profile.repo,
      branch: Meteor.user().profile.repoBranch,
    }).map(function setSJS(file) {
      Meteor.call('postShareJS', file);
    });
  },

  testShareJS: function() { // update file.content from sjs
    Files.find({
      repo:  Meteor.user().profile.repo,
      branch: Meteor.user().profile.repoBranch,
    }).map(function readSJS(file) {
      Files.update(
        file._id,
        {$set: {
          content: Meteor.call('getShareJSDoc', file).snapshot
        }});
    });
  },

  ///////////////////
  // ISSUE MANAGEMENT
  ///////////////////

  initIssues: function() { // re-populating git repo issues
    var repo = Repos.findOne(Meteor.user().profile.repo);
    if (repo) {
      Meteor.call('getAllIssues', repo).map(function load(issue) {
        Issues.upsert({
          repo: repo._id,
          ghid: issue.id // (from github)
        },{
          $set: {issue: issue},
        });
      });
    }
  },

  addIssue: function(feedback){ // adds a feedback issue to github

    feedback.imglink = Async.runSync(function(done) { // save screens, give id
      Screens.insert({img: feedback.img}, function(err, id){
        done(err, id);
      });
    }).result; // attach screenshot to this issue
    delete feedback.img; // delete redundant png

    // insert a dummy issue to get id, use later in GH issue body txt
    var issueId = Async.runSync(function(done){
      Issues.insert({issue: null}, function(err, id){
        done(err, id);
      });
    }).result; // get the id of the newly inserted issue

    // construct and append the text of the github issue, including links to screenshot and demo
    var imglink = '[issue screenshot](http://codepilot.meteor.com/screenshot/' + feedback.imglink + ')\n';
    var livelink = '[live code here](http://codepilot.meteor.com/render/' + issueId + ')\n';
    var htmllink = 'html:\n```html\n' + feedback.html + '\n```\n';
    var csslink = 'css:\n```css\n' + feedback.css + '\n```\n';
    var jslink = 'js:\n```js\n' + feedback.js + '\n```\n';
    var loglink = 'console log:\n```\n' + feedback.log + '```\n';
    feedback.body = imglink + livelink + htmllink + csslink + jslink + loglink;

    // post the issue to github, and get the GH generated content
    var issue = Meteor.call('postIssue', feedback);
    var ghIssue = { // the entire issue object
      _id: issueId,
      ghid: issue.id, // (from github)
      repo: feedback.repo, // attach repo forming data
      feedback: feedback, // attach feedback issue data
      issue: issue // returned from github call
    };

    // insert complete issue, and add it to the feed
    Issues.update(issueId, ghIssue);
    Meteor.call(
      'addUserMessage',
      feedback.user,
      'opened issue - ' + feedback.note
    );
  },

  closeIssue: function(issue){ // close an issue on github by number
    Meteor.call('ghAuth');
    Meteor.call('addMessage', 'closed issue - ' + issue.issue.title);
    github.issues.edit({
      user: Meteor.user().profile.repoOwner,
      repo: Meteor.user().profile.repoName,
      number: issue.issue.number,
      state: 'closed'
    });
    Issues.remove(issue._id); // remove from the local database
  },

  /////////////////////
  // COMMIT MANAGEMENT
  /////////////////////

  initCommits: function() { // re-populating the commit log
    Meteor.call('getAllCommits').map(function(c){
      Meteor.call('addCommit', c);
    });
  },

  addCommit: function(c) { // adds a commit, links to repo + branch
    Commits.upsert({
      repo: Meteor.user().profile.repo,
      branch: Meteor.user().profile.repoBranch,
      sha: c.sha
    },{
      $set: { commit: c }
    });
  },

  loadHead: function(bname) { // load head of branch, from sha
    var sha =  Meteor.call('getBranch', bname).commit.sha;
    if (sha) Meteor.call('loadCommit', sha);
  },

  loadCommit: function(sha) { // takes commit sha, loads into sjs
    var commitResults = Meteor.call('getCommit', sha);
    var treeSHA = commitResults.commit.tree.sha;
    var treeResults = Meteor.call('getTree', treeSHA);
    treeResults.tree.forEach(function updateBlob(blob) {

      if (blob.type === 'blob') { // only load files, not folders/trees

        var image = /\.(gif|jpg|jpeg|tiff|png|bmp)$/i;

        if (image.test(blob.path)) { // get the encoded file content

          var img = Async.runSync(function(done) { // wait on github response
            var content = Meteor.call('getContent', blob.path);
            done(content, content);
          }).result;

          blob.src = img.html_url;
          blob.raw = img.download_url;
          blob.content = '';

        } else { // get the raw file content

          blob.content = Async.runSync(function(done) { // wait on github response
            var content = Meteor.call('getBlob', blob);
            done(content, content);
          }).result;

        }

        Meteor.call('createFile', blob);
      }

    });

    Meteor.call('postAllShareJS');

  },

  ////////////////////////////////////////////////////////
  // top level function, grab files and commit to github
  ////////////////////////////////////////////////////////

  newCommit: function(msg) { // grab sjs contents, commit to github

    // getting all file ids, names, and content
    var user = Meteor.user().profile;
    var bname = user.repoBranch;
    var blobs = Files.find({
      repo: user.repo,
      branch: bname,
    }).map(function getBlob(file){ // update file cache too
      Files.update(file._id, {$set: {cache: file.content}});
      return {
        path: file.title,
        mode: '100644',
        type: 'blob',
        content: file.content
      };
    });

    // get old tree and update it with new shas, post and get that sha
    var branch = Meteor.call('getBranch', bname);
    var oldTree = Meteor.call('getTree', branch.commit.commit.tree.sha);
    var newTree = {base: oldTree.sha, tree: blobs};
    var treeSHA = Meteor.call('postTree', newTree);

    // specify author of this commit
    var commitAuthor = {
      name: user.name,
      email: user.email,
      date: new Date()
    };

    // make the new commit result object
    var commitResult = Meteor.call('postCommit', {
      message: msg, // passed in
      author: commitAuthor,
      parents: [ branch.commit.sha ],
      tree: treeSHA
    });

    // update the ref, point to new commmit
    Meteor.call('postRef', commitResult);

    // get the latest commit from the branch head
    var lastCommit = Meteor.call('getBranch', bname).commit;

    // post into commit db with repo tag
    Meteor.call('addCommit', lastCommit);

    // update the feed with new commit
    Meteor.call('addMessage', 'commited - ' + msg);

  },

  ///////////////////////////
  // helper & testing methods
  ///////////////////////////

  //http://stackoverflow.com/questions/10677491/how-to-get-meteor-call-to-return-value-for-template
  getCollabs: function(repo) { // get a users profile based on their id
    return repo.users.map(function(uid){
      var user = Meteor.users.findOne(uid);
      if (user.profile.repo === repo._id) { // only return users currently working on project
        return user.profile;
      }
    });
  },

  resetAllData: function() { // detroy everything
    Messages.remove({});
    Commits.remove({});
    Screens.remove({});
    Issues.remove({});
    Repos.remove({});
    Tasks.remove({});
    Files.remove({});
    Docs.remove({});
  },

});
<|endoftext|>