<commit_before>/**
 * boot.js -- loads lively in any page that inserts through a script tag
 *
 **/

/*
 #TODO refactor booting/loading/init of lively4
  - currently we have different entry points we should unify
 */

if (window.lively && window.lively4url) {
  console.log("CANCEL BOOT Lively4, because it is already loaded")
} else {

  console.group("BOOT")

  // for finding the baseURL...
  var script = document.currentScript
  var scriptURL = script.src;
  window.lively4url = scriptURL.replace("/src/client/boot.js","")

  var loadContainer = script.getAttribute("data-container") // some simple configuration
  console.log("lively4url: " + lively4url);

  // COPIED HERE BECAUSE resuse through libs does not work yet
  function loadJavaScriptThroughDOM(name, src, force) {
    return new Promise(function (resolve) {
      var scriptNode = document.querySelector(name);
      if (scriptNode) {
        scriptNode.remove();
      }
      var script = document.createElement("script");
      script.id = name;
      script.charset = "utf-8";
      script.type = "text/javascript";
      script.setAttribute("data-lively4-donotpersist","all");
      if (force) {
        src += +"?" + Date.now();
      }
      script.src = src;
      script.onload = function () {
        resolve();
      };
      document.head.appendChild(script);
    });
  }

  Promise.resolve().then( () => {
    return loadJavaScriptThroughDOM("systemjs", lively4url + "/src/external/systemjs/system.src.js");
  }).then(async () => {
    // setup var recorder object
    window._recorder_ = {_module_:{}}

    const moduleOptionsNon = {
      babelOptions: {
        es2015: false,
        stage2: false,
        stage3: false,
        plugins: []
      }
    };

    System.trace = true; // does not work in config

    // config for loading babal plugins
    SystemJS.config({
      baseURL: lively4url + '/', // needed for global refs like "src/client/lively.js", we have to refactor those before disabling this here. #TODO #Discussion
      babelOptions: {
        // stage2: false,
        // stage3: false,
        // es2015: false,
        // stage0: true,
        // stage1: true
        //presets: [
        //    ["es2015", { "loose": true, "modules": false }]
        //],
        plugins: []
      },
      meta: {
        '*.js': moduleOptionsNon
      },
      map: {
        // #Discussion have to use absolute paths here, because it is not clear what the baseURL is
        'plugin-babel': lively4url + '/src/external/babel/plugin-babel2.js',
        'systemjs-plugin-babel': lively4url + '/src/external/babel/plugin-babel.js',
        'systemjs-babel-build': lively4url + '/src/external/babel/systemjs-babel-browser.js',
        'kernel': lively4url + '/src/client/legacy-kernel.js',
        'babel-plugin-doit-result': lively4url + '/src/external/babel-plugin-doit-result.js',
        'babel-plugin-doit-this-ref': lively4url + '/src/external/babel-plugin-doit-this-ref.js',
        'babel-plugin-locals': lively4url + '/src/external/babel-plugin-locals.js',
        'babel-plugin-var-recorder': lively4url + '/src/external/babel-plugin-var-recorder.js',
        'workspace-loader': lively4url + '/src/client/workspace-loader.js'
      },
      trace: true,
      transpiler: 'plugin-babel'
    })

/*    await System.import('babel-plugin-doit-result');
    await System.import('babel-plugin-doit-this-ref');
    await System.import('babel-plugin-locals');
    await System.import('babel-plugin-var-recorder');
  */  //await System.import(lively4url + '/src/client/workspaces.js');
    //await System.import('workspace-loader');

    SystemJS.config({
      meta: {
        // plugins are not transpiled with other plugins, except for SystemJS-internal plugins
        [lively4url + '/src/external/babel-plugin-*.js']: moduleOptionsNon,
        '*.js': {
          babelOptions: {
            es2015: false,
            stage2: false,
            stage3: false,
            plugins: window.__karma__ ? [] : [ // #TODO disable plugins while testing... for now
              'babel-plugin-locals',
              'babel-plugin-var-recorder'
            ]
          }
        },
        'workspace:*': {
          babelOptions: {
            es2015: false,
            stage2: false,
            stage3: false,
            plugins: [
              'babel-plugin-locals',
              'babel-plugin-doit-result',
              'babel-plugin-doit-this-ref',
              'babel-plugin-var-recorder'
            ]
          },
          loader: 'workspace-loader'
        },
      }
    });

    try {
      let { whenLoaded } = await System.import(lively4url + "/src/client/load.js")

      console.group("1/3: Wait for Service Worker...")
      await new Promise(whenLoaded);
      console.groupEnd();

      console.group("2/3: Look for uninitialized instances of Web Compoments");
      await lively.components.loadUnresolved();
      console.groupEnd();

      console.group("3/3: Initialize Document")
      await lively.initializeDocument(document, window.lively4chrome, loadContainer);
      console.groupEnd();

      console.log("Finally loaded!");

      document.dispatchEvent(new Event("livelyloaded"))

      console.groupEnd(); // BOOT
    } catch(err) {
      console.error("Lively Loading failed");
      console.error(err);
      alert("load Lively4 failed:" + err);
    }
  });
}
<commit_msg>AUTO-COMMIT src/client/boot.js<commit_after>/**
 * boot.js -- loads lively in any page that inserts through a script tag
 *
 **/

/*
 #TODO refactor booting/loading/init of lively4
  - currently we have different entry points we should unify
 */

if (window.lively && window.lively4url) {
  console.log("CANCEL BOOT Lively4, because it is already loaded")
} else {

  console.group("BOOT")

  // for finding the baseURL...
  var script = document.currentScript
  var scriptURL = script.src;
  window.lively4url = scriptURL.replace("/src/client/boot.js","")

  var loadContainer = script.getAttribute("data-container") // some simple configuration
  console.log("lively4url: " + lively4url);

  // COPIED HERE BECAUSE resuse through libs does not work yet
  function loadJavaScriptThroughDOM(name, src, force) {
    return new Promise(function (resolve) {
      var scriptNode = document.querySelector(name);
      if (scriptNode) {
        scriptNode.remove();
      }
      var script = document.createElement("script");
      script.id = name;
      script.charset = "utf-8";
      script.type = "text/javascript";
      script.setAttribute("data-lively4-donotpersist","all");
      if (force) {
        src += +"?" + Date.now();
      }
      script.src = src;
      script.onload = function () {
        resolve();
      };
      document.head.appendChild(script);
    });
  }

  Promise.resolve().then( () => {
    return loadJavaScriptThroughDOM("systemjs", lively4url + "/src/external/systemjs/system.src.js");
  }).then(async () => {
    // setup var recorder object
    window._recorder_ = {_module_:{}}

    const moduleOptionsNon = {
      babelOptions: {
        es2015: false,
        stage2: false,
        stage3: false,
        plugins: []
      }
    };

    System.trace = true; // does not work in config

    // config for loading babal plugins
    SystemJS.config({
      baseURL: lively4url + '/', // needed for global refs like "src/client/lively.js", we have to refactor those before disabling this here. #TODO #Discussion
      babelOptions: {
        // stage2: false,
        // stage3: false,
        // es2015: false,
        // stage0: true,
        // stage1: true
        //presets: [
        //    ["es2015", { "loose": true, "modules": false }]
        //],
        plugins: []
      },
      meta: {
        '*.js': moduleOptionsNon
      },
      map: {
        // #Discussion have to use absolute paths here, because it is not clear what the baseURL is
        'plugin-babel': lively4url + '/src/external/babel/plugin-babel2.js',
        'systemjs-plugin-babel': lively4url + '/src/external/babel/plugin-babel.js',
        'systemjs-babel-build': lively4url + '/src/external/babel/systemjs-babel-browser.js',
        'kernel': lively4url + '/src/client/legacy-kernel.js',
        'babel-plugin-doit-result': lively4url + '/src/external/babel-plugin-doit-result.js',
        'babel-plugin-doit-this-ref': lively4url + '/src/external/babel-plugin-doit-this-ref.js',
        'babel-plugin-locals': lively4url + '/src/external/babel-plugin-locals.js',
        'babel-plugin-var-recorder': lively4url + '/src/external/babel-plugin-var-recorder.js',
        'workspace-loader': lively4url + '/src/client/workspace-loader.js'
      },
      trace: true,
      transpiler: 'plugin-babel'
    })

/*    await System.import('babel-plugin-doit-result');
    await System.import('babel-plugin-doit-this-ref');
    await System.import('babel-plugin-locals');
    await System.import('babel-plugin-var-recorder');
  */  //await System.import(lively4url + '/src/client/workspaces.js');
    //await System.import('workspace-loader');

    SystemJS.config({
      meta: {
        // plugins are not transpiled with other plugins, except for SystemJS-internal plugins
        [lively4url + '/src/external/babel-plugin-*.js']: moduleOptionsNon,
        [lively4url + '/src/external/ContextJS/src/*.js']: moduleOptionsNon,
        '*.js': {
          babelOptions: {
            es2015: false,
            stage2: false,
            stage3: false,
            plugins: window.__karma__ ? [] : [ // #TODO disable plugins while testing... for now
              'babel-plugin-locals',
              'babel-plugin-var-recorder'
            ]
          }
        },
        'workspace:*': {
          babelOptions: {
            es2015: false,
            stage2: false,
            stage3: false,
            plugins: [
              'babel-plugin-locals',
              'babel-plugin-doit-result',
              'babel-plugin-doit-this-ref',
              'babel-plugin-var-recorder'
            ]
          },
          loader: 'workspace-loader'
        },
      }
    });

    try {
      let { whenLoaded } = await System.import(lively4url + "/src/client/load.js")

      console.group("1/3: Wait for Service Worker...")
      await new Promise(whenLoaded);
      console.groupEnd();

      console.group("2/3: Look for uninitialized instances of Web Compoments");
      await lively.components.loadUnresolved();
      console.groupEnd();

      console.group("3/3: Initialize Document")
      await lively.initializeDocument(document, window.lively4chrome, loadContainer);
      console.groupEnd();

      console.log("Finally loaded!");

      document.dispatchEvent(new Event("livelyloaded"))

      console.groupEnd(); // BOOT
    } catch(err) {
      console.error("Lively Loading failed");
      console.error(err);
      alert("load Lively4 failed:" + err);
    }
  });
}
<|endoftext|>


<commit_before>module.exports = {
    auth: {
        type: {
            Account: 'account',
            Profile: 'profile',
            Role: 'role'
        },
        collection: {
            Account: 'accounts',
            Role: 'roles'
        }
    },
    inventory: {
        StockIn: 'stock-ins',
        StockOut: 'stock-outs',
        Inventory: 'inventories',
        InventoryMovement: 'inventory-movements',
        TransferInDoc: 'transfer-in-docs',
        TransferOutDoc: 'transfer-out-docs',
        FinishedGoodsDoc: 'finished-goods-docs',
        ExpeditionDoc: 'expedition-docs',
        AdjustmentDoc: 'adjustment-docs',
        StockOpnameDoc: 'stock-opname-docs'
    },
    manufacture: {
        Finishing: 'finishings',
        CostCalculationDocument: 'cost-calculation-documents',
        FabricConsumptionDocument: 'fabric-consumption-documents',
        DesignTrackingBoard: 'design-tracking-boards',
        DesignTrackingStage: 'design-tracking-stages',
        DesignTrackingDesign: 'design-tracking-designs',
        DesignTrackingActivity: 'design-tracking-activities'
    },
    master: {
        Item: 'items',
        FinishedGoods: 'items',
        Material: 'items',
        article: {
            ArticleApproval: 'article-approvals',
            ArticleBrand: 'article-brands',
            ArticleCategory: 'article-categories',
            ArticleColor: 'article-colors',
            ArticleCostCalculationDetail: 'article-cost-calculation-details',
            ArticleCostCalculation: 'article-cost-calculations',
            ArticleCounter: 'article-counters',
            ArticleMaterial: 'article-materials',
            ArticleMotif: 'article-motifs',
            ArticleOrigin: 'article-origins',
            ArticleSeason: 'article-seasons',
            ArticleSize: 'article-sub-sizes',
            ArticleSubCounter: 'article-sub-counters',
            ArticleTheme: 'article-themes',
            ArticleType: 'article-types',
            ArticleVariant: 'article-variants',
            Article: 'articles',
            ArticleSeason: 'article-seasons',
            ArticleProcess: 'article-process',
            ArticleSubProcess: 'article-sub-process',
            ArticleCollection: 'article-collections',
            ArticleSubCollection: 'article-sub-collections',
            ArticleMaterialComposition: 'article-material-compositions',
            ArticleSubMaterialComposition: 'article-sub-material-compositions',
        },
        Module: 'modules',
        Bank: 'banks',
        CardType: 'card-types',
        Store: 'stores',
        Storage: 'storages',
        Supplier: 'suppliers',
        StoreShift: 'store-shifts',
        Division: 'division',
        Unit: 'unit',
        Colour: 'colours',
        collection: {
            Division: 'divisions',
            Unit: 'units',
            Colour: 'colours'
        },
        ExpeditionService: 'expedition-services',
        RangeDiscProduct: 'range-disc-products',
        DesignTrackingReason: 'design-tracking-reasons'
    },
    merchandiser: {
        SPKDoc: 'spk-docs'
    },
    sales: {
        SalesDoc: 'sales-docs',
        SalesReturnDoc: 'sales-return-docs',
        Promo: 'promos'
    },
    purchasing: {
        PurchaseRequest: 'purchase-request',
        PurchaseRequestItem: 'purchase-Request-Item'
    }
};<commit_msg>update map.js<commit_after>module.exports = {
    auth: {
        type: {
            Account: 'account',
            Profile: 'profile',
            Role: 'role'
        },
        collection: {
            Account: 'accounts',
            Role: 'roles'
        }
    },
    inventory: {
        StockIn: 'stock-ins',
        StockOut: 'stock-outs',
        Inventory: 'inventories',
        InventoryMovement: 'inventory-movements',
        TransferInDoc: 'transfer-in-docs',
        TransferOutDoc: 'transfer-out-docs',
        FinishedGoodsDoc: 'finished-goods-docs',
        ExpeditionDoc: 'expedition-docs',
        AdjustmentDoc: 'adjustment-docs',
        StockOpnameDoc: 'stock-opname-docs'
    },
    manufacture: {
        Finishing: 'finishings',
        CostCalculationDocument: 'cost-calculation-documents',
        FabricConsumptionDocument: 'fabric-consumption-documents',
        DesignTrackingBoard: 'design-tracking-boards',
        DesignTrackingStage: 'design-tracking-stages',
        DesignTrackingDesign: 'design-tracking-designs',
        DesignTrackingActivity: 'design-tracking-activities'
    },
    master: {
        Item: 'items',
        FinishedGoods: 'items',
        Material: 'items',
        article: {
            ArticleApproval: 'article-approvals',
            ArticleBrand: 'article-brands',
            ArticleCategory: 'article-categories',
            ArticleColor: 'article-colors',
            ArticleCostCalculationDetail: 'article-cost-calculation-details',
            ArticleCostCalculation: 'article-cost-calculations',
            ArticleCounter: 'article-counters',
            ArticleMaterial: 'article-materials',
            ArticleMotif: 'article-motifs',
            ArticleOrigin: 'article-origins',
            ArticleSeason: 'article-seasons',
            ArticleSize: 'article-sub-sizes',
            ArticleSubCounter: 'article-sub-counters',
            ArticleTheme: 'article-themes',
            ArticleType: 'article-types',
            ArticleVariant: 'article-variants',
            Article: 'articles',
            ArticleSeason: 'article-seasons',
            ArticleProcess: 'article-process',
            ArticleSubProcess: 'article-sub-process',
            ArticleCollection: 'article-collections',
            ArticleSubCollection: 'article-sub-collections',
            ArticleMaterialComposition: 'article-material-compositions',
            ArticleSubMaterialComposition: 'article-sub-material-compositions',
        },
        Module: 'modules',
        Bank: 'banks',
        CardType: 'card-types',
        Store: 'stores',
        Storage: 'storages',
        Supplier: 'suppliers',
        StoreShift: 'store-shifts',
        Division: 'division',
        Unit: 'unit',
        Colour: 'colours',
        collection: {
            Division: 'divisions',
            Unit: 'units',
            Colour: 'colours'
        },
        ExpeditionService: 'expedition-services',
        RangeDiscProduct: 'range-disc-products',
        DesignTrackingReason: 'design-tracking-reasons'
    },
    merchandiser: {
        SPKDoc: 'spk-docs'
    },
    sales: {
        SalesDoc: 'sales-docs',
        SalesReturnDoc: 'sales-return-docs',
        Promo: 'promos'
    },
    purchasing: {
        type: {
            PurchaseRequest: 'purchase-request'
        },
        collection: {
            PurchaseRequest: 'purchase-requests'
        }
    }
};<|endoftext|>


<commit_before>/**
 *
 * @author petr.sloup@klokantech.com (Petr Sloup)
 *
 * Copyright 2013 Klokan Technologies Gmbh (www.klokantech.com)
 */

goog.provide('weapi.Map');

/**
 * @param {!Cesium.ImageryLayer} layer .
 * @constructor
 */
weapi.Map = function(layer) {
  /**
   * @type {!Cesium.ImageryLayer}
   */
  this.layer = layer;

  /**
   * @type {?weapi.App}
   */
  this.app = null;
};

/**
 * @param {number} minLat Minimal latitude in degrees.
 * @param {number} maxLat Maximal latitude in degrees.
 * @param {number} minLon Minimal longitude in degrees.
 * @param {number} maxLon Maximal longitude in degrees.
 */
weapi.Map.prototype.setBoundingBox = function(minLat, maxLat,
                                              minLon, maxLon) {
  var extent = this.layer.imageryProvider['_rectangle'];
  extent.west = goog.math.toRadians(minLon);
  extent.south = goog.math.toRadians(minLat);
  extent.east = goog.math.toRadians(maxLon);
  extent.north = goog.math.toRadians(maxLat);
  if (this.app) this.app.sceneChanged = true;
};

/**
 * @param {number} opacity Opacity.
 */
weapi.Map.prototype.setOpacity = function(opacity) {
  this.layer.alpha = opacity;
  if (this.app) this.app.sceneChanged = true;
};

/**
 * @return {number} Opacity.
 */
weapi.Map.prototype.getOpacity = function() {
  return this.layer.alpha;
};
<commit_msg>Clamping of the map bbox (should fix issue #3)<commit_after>/**
 *
 * @author petr.sloup@klokantech.com (Petr Sloup)
 *
 * Copyright 2013 Klokan Technologies Gmbh (www.klokantech.com)
 */

goog.provide('weapi.Map');

/**
 * @param {!Cesium.ImageryLayer} layer .
 * @constructor
 */
weapi.Map = function(layer) {
  /**
   * @type {!Cesium.ImageryLayer}
   */
  this.layer = layer;

  /**
   * @type {?weapi.App}
   */
  this.app = null;
};

/**
 * @param {number} minLat Minimal latitude in degrees.
 * @param {number} maxLat Maximal latitude in degrees.
 * @param {number} minLon Minimal longitude in degrees.
 * @param {number} maxLon Maximal longitude in degrees.
 */
weapi.Map.prototype.setBoundingBox = function(minLat, maxLat,
                                              minLon, maxLon) {
  var extent = this.layer.imageryProvider['_rectangle'];
  extent.west = goog.math.toRadians(minLon);
  extent.south = goog.math.toRadians(goog.math.clamp(minLat, -85.051, 85.051));
  extent.east = goog.math.toRadians(maxLon);
  extent.north = goog.math.toRadians(goog.math.clamp(maxLat, -85.051, 85.051));
  if (this.app) this.app.sceneChanged = true;
};

/**
 * @param {number} opacity Opacity.
 */
weapi.Map.prototype.setOpacity = function(opacity) {
  this.layer.alpha = opacity;
  if (this.app) this.app.sceneChanged = true;
};

/**
 * @return {number} Opacity.
 */
weapi.Map.prototype.getOpacity = function() {
  return this.layer.alpha;
};
<|endoftext|>


<commit_before>(function($,$TD){
  //
  // Variable: Current timeout ID for update checking.
  //
  var updateCheckTimeoutID;

  //
  // Constant: Update exe file name.
  //
  const updateFileName = $TD.brandName+".Update.exe";

  //
  // Function: Creates the update notification element. Removes the old one if already exists.
  //
  var createUpdateNotificationElement = function(version, download){
    var ele = $("#tweetdck-update");
    var existed = ele.length > 0;

    if (existed > 0){
      ele.remove();
    }

    var html = [
      "
",
      "
"+$TD.brandName+" Update
",
      "
Version "+version+" is now available.
",
      "
",
      "<button class='btn btn-positive tdu-btn-download'>Download</button>",
      "<button class='btn btn-negative tdu-btn-dismiss'>Dismiss</button>",
      "
",
      "
"
    ];

    $(document.body).append(html.join(""));

    ele = $("#tweetdck-update");

    var buttonDiv = ele.children("div.tdu-buttons").first();

    ele.css({
      color: "#fff",
      backgroundColor: "rgb(32,94,138)",
      position: "absolute",
      left: "4px",
      bottom: "4px",
      width: "192px",
      height: "86px",
      display: existed ? "block" : "none",
      borderRadius: "2px",
      zIndex: 9999
    });

    ele.children("p.tdu-title").first().css({
      fontSize: "17px",
      fontWeight: "bold",
      textAlign: "center",
      letterSpacing: "0.2px",
      margin: "4px auto 2px"
    });

    ele.children("p.tdu-info").first().css({
      fontSize: "12px",
      textAlign: "center",
      margin: "2px auto 6px"
    });

    buttonDiv.css({
      textAlign: "center"
    });

    buttonDiv.children().css({
      margin: "0 4px",
      minHeight: "25px",
      boxShadow: "1px 1px 1px rgba(17,17,17,0.5)"
    });

    buttonDiv.find("span").css({
      verticalAlign: "baseline"
    });

    ele.find("span.tdu-data-tag").first().css({
      cursor: "pointer",
      textDecoration: "underline"
    });

    buttonDiv.children(".tdu-btn-download").click(function(){
      ele.remove();
      $TD.onUpdateAccepted(version,download);
    });

    buttonDiv.children(".tdu-btn-dismiss").click(function(){
      $TD.onUpdateDismissed(version);
      ele.slideUp(function(){ ele.remove(); });
    });

    if (!existed){
      ele.slideDown();
    }

    return ele;
  };

  //
  // Function: Runs an update check and updates all DOM elements appropriately.
  //
  var runUpdateCheck = function(){
    clearTimeout(updateCheckTimeoutID);
    updateCheckTimeoutID = setTimeout(runUpdateCheck,1000*60*60); // 1 hour

    if (!$TD.updateCheckEnabled)return;

    $.getJSON("https://api.github.com/repos/chylex/"+$TD.brandName+"/releases/latest",function(response){
      var tagName = response.tag_name;

      if (tagName !== $TD.versionTag && tagName !== $TD.dismissedVersionTag && response.assets.length > 0){
        var obj = response.assets.find(asset => asset.name === updateFileName) || assets[0];
        createUpdateNotificationElement(tagName,obj.browser_download_url);
      }
    });
  };

  //
  // Block: Setup global functions.
  //
  window.TDGF_runUpdateCheck = runUpdateCheck;
  runUpdateCheck();
})($,$TD);
<commit_msg>Push unsaved change that was supposed to be in previous commit<commit_after>(function($,$TD){
  //
  // Variable: Current timeout ID for update checking.
  //
  var updateCheckTimeoutID;

  //
  // Constant: Update exe file name.
  //
  const updateFileName = $TD.brandName+".Update.exe";

  //
  // Function: Creates the update notification element. Removes the old one if already exists.
  //
  var createUpdateNotificationElement = function(version, download){
    var ele = $("#tweetdck-update");
    var existed = ele.length > 0;

    if (existed > 0){
      ele.remove();
    }

    var html = [
      "
",
      "
"+$TD.brandName+" Update
",
      "
Version "+version+" is now available.
",
      "
",
      "<button class='btn btn-positive tdu-btn-download'>Download</button>",
      "<button class='btn btn-negative tdu-btn-dismiss'>Dismiss</button>",
      "
",
      "
"
    ];

    $(document.body).append(html.join(""));

    ele = $("#tweetdck-update");

    var buttonDiv = ele.children("div.tdu-buttons").first();

    ele.css({
      color: "#fff",
      backgroundColor: "rgb(32,94,138)",
      position: "absolute",
      left: "4px",
      bottom: "4px",
      width: "192px",
      height: "86px",
      display: existed ? "block" : "none",
      borderRadius: "2px",
      zIndex: 9999
    });

    ele.children("p.tdu-title").first().css({
      fontSize: "17px",
      fontWeight: "bold",
      textAlign: "center",
      letterSpacing: "0.2px",
      margin: "4px auto 2px"
    });

    ele.children("p.tdu-info").first().css({
      fontSize: "12px",
      textAlign: "center",
      margin: "2px auto 6px"
    });

    buttonDiv.css({
      textAlign: "center"
    });

    buttonDiv.children().css({
      margin: "0 4px",
      minHeight: "25px",
      boxShadow: "1px 1px 1px rgba(17,17,17,0.5)"
    });

    buttonDiv.find("span").css({
      verticalAlign: "baseline"
    });

    ele.find("span.tdu-data-tag").first().css({
      cursor: "pointer",
      textDecoration: "underline"
    });

    buttonDiv.children(".tdu-btn-download").click(function(){
      ele.remove();
      $TD.onUpdateAccepted(version,download);
    });

    buttonDiv.children(".tdu-btn-dismiss").click(function(){
      $TD.onUpdateDismissed(version);
      ele.slideUp(function(){ ele.remove(); });
    });

    if (!existed){
      ele.slideDown();
    }

    return ele;
  };

  //
  // Function: Runs an update check and updates all DOM elements appropriately.
  //
  var runUpdateCheck = function(){
    clearTimeout(updateCheckTimeoutID);
    updateCheckTimeoutID = setTimeout(runUpdateCheck,1000*60*60); // 1 hour

    if (!$TD.updateCheckEnabled)return;

    $.getJSON("https://api.github.com/repos/chylex/"+$TD.brandName+"/releases/latest",function(response){
      var tagName = response.tag_name;

      if (tagName !== $TD.versionTag && tagName !== $TD.dismissedVersionTag && response.assets.length > 0){
        var obj = response.assets.find(asset => asset.name === updateFileName) || response.assets[0];
        createUpdateNotificationElement(tagName,obj.browser_download_url);
      }
    });
  };

  //
  // Block: Setup global functions.
  //
  window.TDGF_runUpdateCheck = runUpdateCheck;
  runUpdateCheck();
})($,$TD);
<|endoftext|>


<commit_before>import avocado from 'avocado-type-checker/index';

export default function(d3) {
  return function(id) {
    var av = avocado;
    var autozoom = true;
    var transitionDuration = 500; // ms
    var pi = Math.PI;
    var tau = 2 * pi;
    var radius = d3.scalePow()
        .domain([1, 10])
        .range([3, 15]);

    var strokeWidth = 0.5;

    var wrapper = d3.select(id);
    var width = wrapper.node().clientWidth;
    var height = wrapper.node().clientHeight;
    var size = [width, height];

    var tilesConfig = {
      url: 'https://maps.wikimedia.org/osm-intl/',
      extension: '.png',
      switchXY: false,
      factor: 256
    };

    var projection = d3.geoMercator()
        .scale(1 / tau)
        .translate([0, 0]);

    var path = d3.geoPath()
        .projection(projection);

    var tile = d3.tile()
        .size(size);

    var zoom = d3.zoom()
        .scaleExtent([1 << 9, 1 << 14])
        .on('zoom', zoomed);

    var svg = wrapper.select('svg');

    if (svg.empty()) {
      svg = wrapper.append('svg')
          .classed('infovyz-map', true)
          .attr('width', width)
          .attr('height', height);
    }

    var raster = svg.append('g').classed('tiles', true);

    var vector = svg.append('g').classed('overlay', true);

    var previousTransform;
    function zoomed() {
      var transform = previousTransform = d3.event.transform;

      var tiles = tile
          .scale(transform.k)
          .translate([transform.x, transform.y])
          ();

      vector
        .attr('transform', transform);

      vector.selectAll('circle')
        .attr('transform', function(d) { return 'translate(' + projection(d.geometry.coordinates) + ')'; })
        .attr('r', function(d) {
          return radius(d.properties.value || 1) / transform.k;
        })
        .attr('stroke-width', strokeWidth / transform.k);

      var image = raster
        .attr('transform', stringify(tiles.scale, tiles.translate))
        .selectAll('image')
        .data(tiles, function(d) { return d; });

      image
        .exit().remove();

      image.enter().append('image')
          .attr('xlink:href', function(d) { return tilesConfig.url + d[2] + '/' + d[0] + '/' + d[1] + tilesConfig.extension; })
          .attr('x', function(d) { return d[0] * tilesConfig.factor; })
          .attr('y', function(d) { return d[1] * tilesConfig.factor; })
          .attr('width', tilesConfig.factor)
          .attr('height', tilesConfig.factor);
    }

    function stringify(scale, translate) {
      var k = scale / tilesConfig.factor;
      var r = scale % 1 ? Number : Math.round;
      return 'translate(' + r(translate[0] * scale) + ',' + r(translate[1] * scale) + ') scale(' + k + ')';
    }

    var latitude = function(i) {
      return av(i, function (d){
        var l = av.float(d)();
        if (l < -90 || l > 90) {
          throw new Error('latitude must be a number between -90 and 90.');
        }
        return d;
      });
    };

    var longitude = function(i) {
      return av(i, function (d){
        var l = av.float(d)();
        if (l < -180 || l > 180) {
          throw new Error('longitude must be a number between -180 and 180.');
        }
        return d;
      });
    };

    var Location = function(i) {
      return av.map(i, {
        id: av.string,
        // commented for now because examples use data without values
        // and avocado doesn't support optional attributes yet.
        // value: av.int,
        longitude: longitude,
        latitude: latitude
      });
    };

    var transformData = function(d) {
      return {
        type: 'FeatureCollection',
        features: d.map(function(el) {
          Location(el);
          return {
            type: 'Feature',
            id: el.id,
            properties: el,
            geometry: {type: 'Point', coordinates: [+el.longitude, +el.latitude]}
          };
        })
      };
    };

    var map = function(d) {
      var newWidth = wrapper.node().clientWidth;
      var newHeight = wrapper.node().clientHeight;
      var _transitionDuration = transitionDuration;

      if (width !== newWidth || height !== newHeight) {
        width = newWidth;
        height = newHeight;
        _transitionDuration = 0;
        svg
          .attr('width', width)
          .attr('height', height);
      }

      if (typeof vector.datum() === 'undefined') {
        _transitionDuration = 0;
      }
      var data = (typeof d !== 'undefined') ? transformData(d) : vector.datum();

      // Compute the projected center.
      var bounds  = path.bounds(data);
      var bx = bounds[1][0] - bounds[0][0];
      var by = bounds[1][1] - bounds[0][1];
      var tx = (bounds[0][0] + bounds[1][0]) / 2;
      var ty = (bounds[0][1] + bounds[1][1]) / 2;
      var scale = 0.8 * Math.min(width / bx, height / by);

      svg.call(zoom);

      if (!autozoom && typeof vector.datum() === 'undefined') {
        svg
          .call(
            zoom.transform,
            d3.zoomIdentity
              .translate(width / 2, height / 2)
              .scale(1 << 10)
              .translate(0, 0)
          );
      }

      if (autozoom) {
        svg
          .transition()
          .duration(_transitionDuration)
          .call(
            zoom.transform,
            d3.zoomIdentity
              .translate(width / 2, height / 2)
              .scale(scale)
              .translate(-tx, -ty)
          );
      }

      vector.datum(data);

      var features = (typeof data !== 'undefined') ? data.features : [];

      radius.domain(d3.extent(features.map(function(f) {
        return f.properties.value || 1;
      })));

      var bubble = vector
        .selectAll('circle')
        .data(features, function(d) { return d.id; });

      bubble
        .exit()
        .transition()
        .attr('r', 0)
        .attr('stroke-width', 0)
        .remove();

      scale = (previousTransform) ? previousTransform.k : scale;
      bubble.enter().append('circle')
        .attr('transform', function(d) { return 'translate(' + projection(d.geometry.coordinates) + ')'; })
        .attr('r', 0)
        .attr('stroke-width', 0)
        .transition()
        .attr('r', function(d) {
          return radius(d.properties.value || 1) / scale;
        })
        .attr('stroke-width', strokeWidth / scale);

      return map;
    };

    map.panTo = function(d) {
      if (typeof d === 'undefined') {
        throw new Error('map.panTo() called without location.');
      }

      if (typeof d.longitude === 'undefined' || typeof d.latitude === 'undefined') {
        throw new Error('map.panTo() called without valid location.');
      }

      var t = projection([d.longitude, d.latitude]);
      var z = d.zoom || 10;
      svg
        .transition()
        .duration(transitionDuration)
        .call(
          zoom.transform,
          d3.zoomIdentity
            .translate(width / 2, height / 2)
            .scale(1 << z)
            .translate(-t[0], -t[1])
        );
    };

    map.autozoom = function(d) {
      if (arguments.length === 0) return autozoom;
      autozoom = d;
      return map;
    };

    map.transitionDuration = function(d) {
      if (arguments.length === 0) return transitionDuration;
      transitionDuration = d;
      return map;
    };

    return map;
  };
}
<commit_msg>Fixes an issue with updating the value range for new locations.<commit_after>import avocado from 'avocado-type-checker/index';

export default function(d3) {
  return function(id) {
    var av = avocado;
    var autozoom = true;
    var transitionDuration = 500; // ms
    var pi = Math.PI;
    var tau = 2 * pi;
    var radius = d3.scalePow()
        .domain([1, 10])
        .range([3, 15]);

    var strokeWidth = 0.5;

    var wrapper = d3.select(id);
    var width = wrapper.node().clientWidth;
    var height = wrapper.node().clientHeight;
    var size = [width, height];

    var tilesConfig = {
      url: 'https://maps.wikimedia.org/osm-intl/',
      extension: '.png',
      switchXY: false,
      factor: 256
    };

    var projection = d3.geoMercator()
        .scale(1 / tau)
        .translate([0, 0]);

    var path = d3.geoPath()
        .projection(projection);

    var tile = d3.tile()
        .size(size);

    var zoom = d3.zoom()
        .scaleExtent([1 << 9, 1 << 14])
        .on('zoom', zoomed);

    var svg = wrapper.select('svg');

    if (svg.empty()) {
      svg = wrapper.append('svg')
          .classed('infovyz-map', true)
          .attr('width', width)
          .attr('height', height);
    }

    var raster = svg.append('g').classed('tiles', true);

    var vector = svg.append('g').classed('overlay', true);

    var previousTransform;
    function zoomed() {
      var transform = previousTransform = d3.event.transform;

      var tiles = tile
          .scale(transform.k)
          .translate([transform.x, transform.y])
          ();

      vector
        .attr('transform', transform);

      vector.selectAll('circle.active')
        .attr('transform', function(d) { return 'translate(' + projection(d.geometry.coordinates) + ')'; })
        .attr('r', function(d) {
          return radius(d.properties.value || 1) / transform.k;
        })
        .attr('stroke-width', strokeWidth / transform.k);

      var image = raster
        .attr('transform', stringify(tiles.scale, tiles.translate))
        .selectAll('image')
        .data(tiles, function(d) { return d; });

      image
        .exit().remove();

      image.enter().append('image')
          .attr('xlink:href', function(d) { return tilesConfig.url + d[2] + '/' + d[0] + '/' + d[1] + tilesConfig.extension; })
          .attr('x', function(d) { return d[0] * tilesConfig.factor; })
          .attr('y', function(d) { return d[1] * tilesConfig.factor; })
          .attr('width', tilesConfig.factor)
          .attr('height', tilesConfig.factor);
    }

    function stringify(scale, translate) {
      var k = scale / tilesConfig.factor;
      var r = scale % 1 ? Number : Math.round;
      return 'translate(' + r(translate[0] * scale) + ',' + r(translate[1] * scale) + ') scale(' + k + ')';
    }

    var latitude = function(i) {
      return av(i, function (d){
        var l = av.float(d)();
        if (l < -90 || l > 90) {
          throw new Error('latitude must be a number between -90 and 90.');
        }
        return d;
      });
    };

    var longitude = function(i) {
      return av(i, function (d){
        var l = av.float(d)();
        if (l < -180 || l > 180) {
          throw new Error('longitude must be a number between -180 and 180.');
        }
        return d;
      });
    };

    var Location = function(i) {
      return av.map(i, {
        id: av.string,
        // commented for now because examples use data without values
        // and avocado doesn't support optional attributes yet.
        // value: av.int,
        longitude: longitude,
        latitude: latitude
      });
    };

    var transformData = function(d) {
      return {
        type: 'FeatureCollection',
        features: d.map(function(el) {
          Location(el);
          return {
            type: 'Feature',
            id: el.id,
            properties: el,
            geometry: {type: 'Point', coordinates: [+el.longitude, +el.latitude]}
          };
        })
      };
    };

    var map = function(d) {
      var newWidth = wrapper.node().clientWidth;
      var newHeight = wrapper.node().clientHeight;
      var _transitionDuration = transitionDuration;

      if (width !== newWidth || height !== newHeight) {
        width = newWidth;
        height = newHeight;
        _transitionDuration = 0;
        svg
          .attr('width', width)
          .attr('height', height);
      }

      if (typeof vector.datum() === 'undefined') {
        _transitionDuration = 0;
      }
      var data = (typeof d !== 'undefined') ? transformData(d) : vector.datum();

      // Compute the projected center.
      var bounds  = path.bounds(data);
      var bx = bounds[1][0] - bounds[0][0];
      var by = bounds[1][1] - bounds[0][1];
      var tx = (bounds[0][0] + bounds[1][0]) / 2;
      var ty = (bounds[0][1] + bounds[1][1]) / 2;
      var scale = 0.8 * Math.min(width / bx, height / by);

      svg.call(zoom);

      if (!autozoom && typeof vector.datum() === 'undefined') {
        svg
          .call(
            zoom.transform,
            d3.zoomIdentity
              .translate(width / 2, height / 2)
              .scale(1 << 10)
              .translate(0, 0)
          );
      }

      if (autozoom) {
        svg
          .transition()
          .duration(_transitionDuration)
          .call(
            zoom.transform,
            d3.zoomIdentity
              .translate(width / 2, height / 2)
              .scale(scale)
              .translate(-tx, -ty)
          );
      }

      vector.datum(data);

      var features = (typeof data !== 'undefined') ? data.features : [];

      var valueExtent = d3.extent(features.map(function(f) {
        return f.properties.value || 1;
      }));

      radius = d3.scalePow()
        .domain(valueExtent)
        .range([3, 15]);

      var bubble = vector
        .selectAll('circle')
        .data(features, function(d) { return d.id; });

      bubble
        .exit()
        .classed('active', false)
        .transition()
        .attr('r', 0)
        .attr('stroke-width', 0)
        .remove();

      scale = (previousTransform) ? previousTransform.k : scale;
      bubble.enter().append('circle')
        .classed('active', true)
        .attr('transform', function(d) { return 'translate(' + projection(d.geometry.coordinates) + ')'; })
        .attr('r', 0)
        .attr('stroke-width', 0)
        .transition()
        .attr('r', function(d) {
          return radius(d.properties.value || 1) / scale;
        })
        .attr('stroke-width', strokeWidth / scale);

      return map;
    };

    map.panTo = function(d) {
      if (typeof d === 'undefined') {
        throw new Error('map.panTo() called without location.');
      }

      if (typeof d.longitude === 'undefined' || typeof d.latitude === 'undefined') {
        throw new Error('map.panTo() called without valid location.');
      }

      var t = projection([d.longitude, d.latitude]);
      var z = d.zoom || 10;
      svg
        .transition()
        .duration(transitionDuration)
        .call(
          zoom.transform,
          d3.zoomIdentity
            .translate(width / 2, height / 2)
            .scale(1 << z)
            .translate(-t[0], -t[1])
        );
    };

    map.autozoom = function(d) {
      if (arguments.length === 0) return autozoom;
      autozoom = d;
      return map;
    };

    map.transitionDuration = function(d) {
      if (arguments.length === 0) return transitionDuration;
      transitionDuration = d;
      return map;
    };

    return map;
  };
}
<|endoftext|>


<commit_before>const functions = require('firebase-functions');
const admin = require('firebase-admin');

admin.initializeApp(functions.config().firebase);

exports.createPlayer = functions.auth.user().onCreate(event => {
  return admin.database()
    .ref('/players')
    .child(event.data.uid)
    .set({
      email: event.data.email,
      displayName: event.data.displayName,
      photoURL: event.data.photoURL,
      rating: 750
    })
});

exports.createMatch = functions.database.ref('/users/{userUid}/matches/{matchUid}')
  .onWrite(event => {
    if (event.auth.admin || event.data.previous.exists() || !event.data.exists())
      return;

    let match = event.data.val();
    if(match.final || match.player1Uid === match.player2Uid)
      return;

    match.createdDate = new Date().toISOString();
    match.final = false;

    return Promise.all([
      admin.database()
        .ref(`/users/${match.player1Uid}/matches`)
        .child(event.params.matchUid)
        .set(match),
      admin.database()
        .ref(`/users/${match.player2Uid}/matches`)
        .child(event.params.matchUid)
        .set(match)
    ]);

  });

exports.rejectMatch = functions.database.ref('/users/{userUid}/matches/{matchUid}')
  .onWrite(event => {
    if (event.data.exists())
      return;

    let deletedMatch = event.data.previous.val();

    return Promise.all([
      admin.database()
        .ref(`/users/${deletedMatch.player1Uid}/matches`)
        .child(event.params.matchUid)
        .set(null),
      admin.database()
        .ref(`/users/${deletedMatch.player2Uid}/matches`)
        .child(event.params.matchUid)
        .set(null)
    ]);
  });

let syncScores = (event, propertyName) => {
  if (event.auth.admin || !event.data.exists())
    return;

  let score = event.data.val();
  if(event.data.previous.exists() && score === event.data.previous.val())
    return;

  return admin.database().ref(`/users/${event.params.userUid}/matches/${event.params.matchUid}`)
    .once('value')
    .then((snap) => {
      let match = snap.val();
      let playerToUpdate = event.params.userUid === match.player1Uid ? match.player2Uid : match.player1Uid;
      return  Promise.all([
        admin.database()
          .ref(`/users/${playerToUpdate}/matches/${event.params.matchUid}`)
          .child(propertyName)
          .set(score),
        admin.database()
          .ref(`/users/${playerToUpdate}/matches/${event.params.matchUid}/player1Accepted`)
          .set(false),
        admin.database()
          .ref(`/users/${playerToUpdate}/matches/${event.params.matchUid}/player2Accepted`)
          .set(false),
      ]);
    });
}
exports.syncPlayer1Score = functions.database.ref('/users/{userUid}/matches/{matchUid}/player1Score')
  .onWrite((e) => syncScores(e, 'player1Score'));
exports.syncPlayer2Score = functions.database.ref('/users/{userUid}/matches/{matchUid}/player2Score')
  .onWrite((e) => syncScores(e, 'player2Score'));

let syncAcceptances = (event, propertyName) => {
  if (event.auth.admin || !event.data.exists())
    return;

  let accepted = event.data.val();
  if(event.data.previous.exists() && accepted === event.data.previous.val())
    return;

  return admin.database().ref(`/users/${event.params.userUid}/matches/${event.params.matchUid}`)
    .once('value')
    .then((snap) => {
      let match = snap.val();

      if(match.player1Accepted && match.player2Accepted) {
        match.final = true;
        match.finalizedDate = new Date().toISOString();
        delete match.player1Accepted;
        delete match.player2Accepted;
      }
      let playerToUpdate = event.params.userUid === match.player1Uid ? match.player2Uid : match.player1Uid;

      return Promise.all([
        match.final ? admin.database().ref(`/matches/${event.params.matchUid}`).set(match) : Promise.resolve(),
        match.final ? admin.database().ref(`/users/${match.player1Uid}/matches/${event.params.matchUid}/final`).set(true) : Promise.resolve(),
        match.final ? admin.database().ref(`/users/${match.player2Uid}/matches/${event.params.matchUid}/final`).set(true) : Promise.resolve(),
        admin.database()
          .ref(`/users/${playerToUpdate}/matches/${event.params.matchUid}`)
          .child(propertyName)
          .set(accepted)
      ]);
    });
};
exports.syncPlayer1Accepted = functions.database.ref('/users/{userUid}/matches/{matchUid}/player1Accepted')
  .onWrite((e) => syncAcceptances(e, 'player1Accepted'));
exports.syncPlayer2Accepted = functions.database.ref('/users/{userUid}/matches/{matchUid}/player2Accepted')
  .onWrite((e) => syncAcceptances(e, 'player2Accepted'));

exports.updateRating = functions.database.ref('/matches/{matchUid}')
  .onWrite(event => {
    if (event.data.previous.exists() || !event.data.exists())
      return;

    let match = event.data.val();
    return Promise.all([
      admin.database().ref(`/players/${match.player1Uid}`).once('value'),
      admin.database().ref(`/players/${match.player2Uid}`).once('value')
    ]).then((playerRefs)=>{
      let kFactor = 32;
      let updateRating = (expected, actual, current) => Math.round(current+ kFactor*(actual-expected));
      let getExpected = (a, b) => 1/(1+Math.pow(10,((b-a)/400)));

      let player1Rating = playerRefs[0].val().rating;
      let player2Rating = playerRefs[1].val().rating;
      let player1Score = Number(match.player1Score);
      let player2Score = Number(match.player2Score);
      let player1Expected = getExpected(player1Rating, player2Rating);
      let player2Expected = getExpected(player2Rating, player1Rating);

      let player1NewRating = updateRating(
        player1Expected,
        player1Score > player2Score ? 1 : player1Score < player2Score ? 0 : 0.5,
        player1Rating
      );
      let player2NewRating = updateRating(
        player2Expected,
        player2Score > player1Score ? 1 : player2Score < player1Score ? 0 : 0.5,
        player2Rating
      );

      return Promise.all([
        admin.database().ref(`/players/${match.player1Uid}/rating`)
          .set(player1NewRating),
        admin.database().ref(`/players/${match.player2Uid}/rating`)
          .set(player2NewRating)
      ]);
    });
  });
<commit_msg>Reseting acceptances in both players to avoid locked out matches<commit_after>const functions = require('firebase-functions');
const admin = require('firebase-admin');

admin.initializeApp(functions.config().firebase);

exports.createPlayer = functions.auth.user().onCreate(event => {
  return admin.database()
    .ref('/players')
    .child(event.data.uid)
    .set({
      email: event.data.email,
      displayName: event.data.displayName,
      photoURL: event.data.photoURL,
      rating: 750
    })
});

exports.createMatch = functions.database.ref('/users/{userUid}/matches/{matchUid}')
  .onWrite(event => {
    if (event.auth.admin || event.data.previous.exists() || !event.data.exists())
      return;

    let match = event.data.val();
    if(match.final || match.player1Uid === match.player2Uid)
      return;

    match.createdDate = new Date().toISOString();
    match.final = false;

    return Promise.all([
      admin.database()
        .ref(`/users/${match.player1Uid}/matches`)
        .child(event.params.matchUid)
        .set(match),
      admin.database()
        .ref(`/users/${match.player2Uid}/matches`)
        .child(event.params.matchUid)
        .set(match)
    ]);

  });

exports.rejectMatch = functions.database.ref('/users/{userUid}/matches/{matchUid}')
  .onWrite(event => {
    if (event.data.exists())
      return;

    let deletedMatch = event.data.previous.val();

    return Promise.all([
      admin.database()
        .ref(`/users/${deletedMatch.player1Uid}/matches`)
        .child(event.params.matchUid)
        .set(null),
      admin.database()
        .ref(`/users/${deletedMatch.player2Uid}/matches`)
        .child(event.params.matchUid)
        .set(null)
    ]);
  });

let syncScores = (event, propertyName) => {
  if (event.auth.admin || !event.data.exists())
    return;

  let score = event.data.val();
  if(event.data.previous.exists() && score === event.data.previous.val())
    return;

  return admin.database().ref(`/users/${event.params.userUid}/matches/${event.params.matchUid}`)
    .once('value')
    .then((snap) => {
      let match = snap.val();
      let playerToUpdate = event.params.userUid === match.player1Uid ? match.player2Uid : match.player1Uid;
      return  Promise.all([
        admin.database()
          .ref(`/users/${playerToUpdate}/matches/${event.params.matchUid}`)
          .child(propertyName)
          .set(score),
        admin.database()
          .ref(`/users/${match.player1Uid}/matches/${event.params.matchUid}/player1Accepted`)
          .set(false),
        admin.database()
          .ref(`/users/${match.player1Uid}/matches/${event.params.matchUid}/player2Accepted`)
          .set(false),
        admin.database()
          .ref(`/users/${match.player2Uid}/matches/${event.params.matchUid}/player1Accepted`)
          .set(false),
        admin.database()
          .ref(`/users/${match.player2Uid}/matches/${event.params.matchUid}/player2Accepted`)
          .set(false),
      ]);
    });
}
exports.syncPlayer1Score = functions.database.ref('/users/{userUid}/matches/{matchUid}/player1Score')
  .onWrite((e) => syncScores(e, 'player1Score'));
exports.syncPlayer2Score = functions.database.ref('/users/{userUid}/matches/{matchUid}/player2Score')
  .onWrite((e) => syncScores(e, 'player2Score'));

let syncAcceptances = (event, propertyName) => {
  if (event.auth.admin || !event.data.exists())
    return;

  let accepted = event.data.val();
  if(event.data.previous.exists() && accepted === event.data.previous.val())
    return;

  return admin.database().ref(`/users/${event.params.userUid}/matches/${event.params.matchUid}`)
    .once('value')
    .then((snap) => {
      let match = snap.val();

      if(match.player1Accepted && match.player2Accepted) {
        match.final = true;
        match.finalizedDate = new Date().toISOString();
        delete match.player1Accepted;
        delete match.player2Accepted;
      }
      let playerToUpdate = event.params.userUid === match.player1Uid ? match.player2Uid : match.player1Uid;

      return Promise.all([
        match.final ? admin.database().ref(`/matches/${event.params.matchUid}`).set(match) : Promise.resolve(),
        match.final ? admin.database().ref(`/users/${match.player1Uid}/matches/${event.params.matchUid}/final`).set(true) : Promise.resolve(),
        match.final ? admin.database().ref(`/users/${match.player2Uid}/matches/${event.params.matchUid}/final`).set(true) : Promise.resolve(),
        admin.database()
          .ref(`/users/${playerToUpdate}/matches/${event.params.matchUid}`)
          .child(propertyName)
          .set(accepted)
      ]);
    });
};
exports.syncPlayer1Accepted = functions.database.ref('/users/{userUid}/matches/{matchUid}/player1Accepted')
  .onWrite((e) => syncAcceptances(e, 'player1Accepted'));
exports.syncPlayer2Accepted = functions.database.ref('/users/{userUid}/matches/{matchUid}/player2Accepted')
  .onWrite((e) => syncAcceptances(e, 'player2Accepted'));

exports.updateRating = functions.database.ref('/matches/{matchUid}')
  .onWrite(event => {
    if (event.data.previous.exists() || !event.data.exists())
      return;

    let match = event.data.val();
    return Promise.all([
      admin.database().ref(`/players/${match.player1Uid}`).once('value'),
      admin.database().ref(`/players/${match.player2Uid}`).once('value')
    ]).then((playerRefs)=>{
      let kFactor = 32;
      let updateRating = (expected, actual, current) => Math.round(current+ kFactor*(actual-expected));
      let getExpected = (a, b) => 1/(1+Math.pow(10,((b-a)/400)));

      let player1Rating = playerRefs[0].val().rating;
      let player2Rating = playerRefs[1].val().rating;
      let player1Score = Number(match.player1Score);
      let player2Score = Number(match.player2Score);
      let player1Expected = getExpected(player1Rating, player2Rating);
      let player2Expected = getExpected(player2Rating, player1Rating);

      let player1NewRating = updateRating(
        player1Expected,
        player1Score > player2Score ? 1 : player1Score < player2Score ? 0 : 0.5,
        player1Rating
      );
      let player2NewRating = updateRating(
        player2Expected,
        player2Score > player1Score ? 1 : player2Score < player1Score ? 0 : 0.5,
        player2Rating
      );

      return Promise.all([
        admin.database().ref(`/players/${match.player1Uid}/rating`)
          .set(player1NewRating),
        admin.database().ref(`/players/${match.player2Uid}/rating`)
          .set(player2NewRating)
      ]);
    });
  });
<|endoftext|>


<commit_before>// Copyright 2018, Google, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

'use strict';

const { DialogflowApp } = require('actions-on-google');
const functions = require('firebase-functions');
const shiritori = require('./shiritori');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

const dict = k => {
  return admin.database()
      .ref('food').child(k)
      .once('value')
      .then(snap => snap.val());
};

function welcomeHandler (app) {
  app.data.used = ['しりとり'];
  app.ask('しりとり');
}

function gameHandler (app) {
  shiritori.loaded.then(() => {
    const input = app.getRawInput();
    shiritori.interact(dict, input, app.data.used, {
      lose () {
        app.tell('ざんねん。');
      },
      win (word, kana) {
        if (word) {
          app.tell(`${word} [${kana}]`);
        } else {
          app.tell('すごい！');
        }
      },
      next (word, kana) {
        app.data.used.unshift(input);
        app.data.used.unshift(kana);
        app.ask({
          speech: word,
          displayText: `${word} [${kana}]`
        });
      }
    });
  });
}

function defaultHandler (app) {
  app.tell('しりとりのゲーム');
}

const actionMap = new Map();
actionMap.set('input.welcome', welcomeHandler);
actionMap.set('input.unknown', gameHandler);
actionMap.set('default', defaultHandler);

exports.shiritori = functions.https.onRequest((request, response) => {
  const app = new DialogflowApp({request, response});
  console.log('Request headers: ' + JSON.stringify(request.headers));
  console.log('Request body: ' + JSON.stringify(request.body));
  app.handleRequest(actionMap);
});
exports.shiritori.actionMap = actionMap;
<commit_msg>shiritori/functions: refactor conversation<commit_after>// Copyright 2018, Google, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

'use strict';

const { DialogflowApp } = require('actions-on-google');
const functions = require('firebase-functions');
const shiritori = require('./shiritori');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

const dict = k => {
  return admin.database()
      .ref('food').child(k)
      .once('value')
      .then(snap => snap.val());
};

function welcomeHandler (app) {
  app.ask('どうぞ、始めて下さい');
}

function gameHandler (app) {
  shiritori.loaded.then(() => {
    const input = app.getRawInput();
    shiritori.interact(dict, input, app.data.used, {
      lose () {
        app.tell('ざんねん。あなたの負けです。');
      },
      win (word, kana) {
        if (word) {
          app.tell(`${word} [${kana}]`);
        } else {
          app.tell('すごい！あなたの勝ちです。');
        }
      },
      next (word, kana) {
        app.data.used.unshift(input);
        app.data.used.unshift(kana);
        app.ask({
          speech: word,
          displayText: `${word} [${kana}]`
        });
      }
    });
  });
}

function defaultHandler (app) {
  app.tell('しりとりのゲーム');
}

const actionMap = new Map();
actionMap.set('input.welcome', welcomeHandler);
actionMap.set('input.unknown', gameHandler);
actionMap.set('default', defaultHandler);

exports.shiritori = functions.https.onRequest((request, response) => {
  const app = new DialogflowApp({request, response});
  app.data.used = app.data.used || [];
  console.log('Request headers: ' + JSON.stringify(request.headers));
  console.log('Request body: ' + JSON.stringify(request.body));
  app.handleRequest(actionMap);
});
exports.shiritori.actionMap = actionMap;
<|endoftext|>


<commit_before>require('api.js')

const functions = require('firebase-functions')
const admin = require('firebase-admin')
admin.initializeApp(functions.config().firebase)

exports.createUser = functions.auth.user().onCreate(event => {
  return admin.database()
    .ref('/users')
    .child(event.data.uid)
    .set({
      email: event.data.email,
      displayName: event.data.displayName,
      photoURL: event.data.photoURL
      // rating: 750,
      // rated: false,
      // active: true
    })
})
exports.draftMatch = functions.database.ref('/orgs/{org}/users/{userUid}/draft-matches/{matchUid}')
  .onCreate(event => {
    if (event.auth.admin) { return }
    let orgRef = admin.database().ref(`/orgs/${event.params.org}`)
    let playersPromise = orgRef.ref(`/players`).once('value')
    let matchesPromise = orgRef.ref(`/users/${event.params.userUid}/matches/`).once('value')
    let openMatchesPromise = orgRef.ref('/open-match-details').once('value')
    let userOpenMatchesPromise = orgRef.ref(`/users/${event.params.userUid}/open-matches/`).once('value')

    return Promise.all([playersPromise, matchesPromise, openMatchesPromise, userOpenMatchesPromise])
      .then((snaps) => {
        let players = {}
        let playerRating = 0

        if (snaps[3].numChildren() >= 5) {
          return orgRef
            .ref(`/users/${event.params.userUid}/draft-matches/${event.params.matchUid}`)
            .set(null)
        }

        snaps[0].forEach((playerSnapshot) => {
          let player = playerSnapshot.val()
          player.matchesCount = 0
          player.openMatchesCount = 0
          player.uid = playerSnapshot.key
          players[playerSnapshot.key] = player
          if (playerSnapshot.key === event.params.userUid) { playerRating = player.rating }
        })
        snaps[1].forEach((matchSnapshot) => {
          let match = matchSnapshot.val()
          if (match.player1Uid === event.params.userUid) {
            players[match.player2Uid].matchesCount++
          } else {
            players[match.player1Uid].matchesCount++
          }
        })
        snaps[2].forEach((openMatchSnapshot) => {
          let openMatch = openMatchSnapshot.val()
          if (openMatch.player1Uid === event.params.userUid) {
            players[openMatch.player2Uid].hasOpenMatch = true
          } else if (openMatch.player2Uid === event.params.userUid) {
            players[openMatch.player1Uid].hasOpenMatch = true
          }
          players[openMatch.player1Uid].openMatchesCount++
          players[openMatch.player2Uid].openMatchesCount++
        })

        let oponents = Object.keys(players)
          .map(key => players[key])
          .filter((p) => p.active && p.rated && p.uid !== event.params.userUid)
          .filter((p) => !p.hasOpenMatch && p.openMatchesCount < 5)
          .filter((p) => Math.abs(p.rating - playerRating) <= 300)
          .sort((a, b) => a.matchesCount - b.matchesCount ||
            Math.abs(a.rating - playerRating) - Math.abs(b.rating - playerRating))

        if (oponents.length === 0) {
          return orgRef
            .ref(`/users/${event.params.userUid}/draft-match-details/${event.params.matchUid}/noOponents`)
            .set(true)
        }

        let details = {
          player1Uid: event.params.userUid,
          player1Probability: calcProbability(playerRating, oponents[0].rating),
          player2Uid: oponents[0].uid,
          player2Probability: calcProbability(oponents[0].rating, playerRating),
          timestamp: admin.database.ServerValue.TIMESTAMP
        }

        return orgRef
          .ref(`/users/${event.params.userUid}/draft-match-details/${event.params.matchUid}`)
          .set(details)
      })
  })
exports.acceptDraftMatch = functions.database.ref('/orgs/{org}/users/{userUid}/draft-matches/{matchUid}/accept')
  .onCreate(event => {
    if (event.auth.admin) { return }
    let orgRef = admin.database().ref(`/orgs/${event.params.org}`)

    return orgRef
      .ref(`/users/${event.params.userUid}/draft-match-details/${event.params.matchUid}`)
      .once('value')
      .then((snap) => {
        let draftDetails = snap.val()
        let match = {
          player1Uid: draftDetails.player1Uid,
          player2Uid: draftDetails.player2Uid,
          createdDate: new Date().toISOString()
        }
        let details = Object.assign({
          approvals: {player1: '', player2: ''},
          scores: [{player1: 0, player2: 0}]
        }, match)

        return Promise.all([
          orgRef
            .ref(`/users/${match.player1Uid}/open-matches/${event.params.matchUid}`)
            .set(match),
          orgRef
            .ref(`/users/${match.player2Uid}/open-matches/${event.params.matchUid}`)
            .set(match),
          orgRef
            .ref(`/open-match-details/${event.params.matchUid}`)
            .set(details),
          orgRef
            .ref(`/players/${match.player1Uid}/active`)
            .set(true),
          orgRef
            .ref(`/users/${match.player1Uid}/draft-matches`)
            .set(null),
          orgRef
            .ref(`/users/${match.player1Uid}/draft-match-details`)
            .set(null)
        ])
      })
  })
exports.openMatchDetailsCreated = functions.database.ref('/orgs/{org}/open-match-details/{matchUid}')
  .onCreate(event => {
    let match = event.data.val()
    let orgRef = admin.database().ref(`/orgs/${event.params.org}`)

    return Promise.all([
      orgRef.ref(`/players/${match.player1Uid}`).once('value'),
      orgRef.ref(`/messaging/${match.player2Uid}`).once('value')
    ]).then((snaps) => {
      let player1 = snaps[0].val()
      let player2Messaging = snaps[1].val()
      if (!player2Messaging || !player2Messaging.token) return

      let notificationPayload = {
        notification: {
          title: 'Match Created!',
          body: `${player1.displayName} created ranked match with you. Go and win!`
        }
      }

      return admin.messaging().sendToDevice([player2Messaging.token], notificationPayload)
    })
  })
exports.approvalsChanged = functions.database.ref('/orgs/{org}/open-match-details/{matchUid}/approvals')
  .onUpdate((event) => {
    if (event.auth.admin) { return }
    let orgRef = admin.database().ref(`/orgs/${event.params.org}`)

    let approvals = event.data.val()
    if (!approvals.player1 || !approvals.player2) { return }

    return orgRef.ref(`/open-match-details/${event.params.matchUid}`)
      .once('value')
      .then((snap) => {
        let details = snap.val()
        let closingApproval = details.scores.reduce((approval, score) => {
          return approval + `|${score.player1}|${score.player2}`
        }, '')
        if (closingApproval !== details.approvals.player1 ||
          closingApproval !== details.approvals.player2) {
          return
        }

        let player1Games = details.scores.filter((s) => Number(s.player1) > Number(s.player2)).length
        let player2Games = details.scores.length - player1Games

        let finalMatch = {
          finalizedDate: new Date().toISOString(),
          createdDate: details.createdDate,
          player1Uid: details.player1Uid,
          player2Uid: details.player2Uid,
          scores: details.scores,
          player1Games: player1Games,
          player2Games: player2Games,
          winnerUid: player1Games > player2Games ? details.player1Uid : details.player2Uid
        }
        return Promise.all([
          orgRef.ref(`/matches/${event.params.matchUid}`).set(finalMatch),
          orgRef.ref(`/users/${details.player1Uid}/matches/${event.params.matchUid}`).set(finalMatch),
          orgRef.ref(`/users/${details.player2Uid}/matches/${event.params.matchUid}`).set(finalMatch),
          orgRef.ref(`/users/${details.player1Uid}/open-matches/${event.params.matchUid}`).set(null),
          orgRef.ref(`/users/${details.player2Uid}/open-matches/${event.params.matchUid}`).set(null),
          orgRef.ref(`/open-match-details/${event.params.matchUid}`).set(null)
        ])
      })
  })
exports.updateRating = functions.database.ref('/orgs/{org}/matches/{matchUid}')
  .onCreate(event => {
    let orgRef = admin.database().ref(`/orgs/${event.params.org}`)
    let match = event.data.val()
    return Promise.all([
      orgRef.ref(`/players/${match.player1Uid}`).once('value'),
      orgRef.ref(`/players/${match.player2Uid}`).once('value')
    ]).then((playerRefs) => {
      let p1Rating = playerRefs[0].val().rating
      let p2Rating = playerRefs[1].val().rating
      let p1Score = match.scores.reduce((total, i) => total + Number(i.player1), 0)
      let p2Score = match.scores.reduce((total, i) => total + Number(i.player2), 0)
      let p1Prob = calcProbability(p1Rating, p2Rating)
      let p2Prob = calcProbability(p2Rating, p1Rating)
      let kFactor = 50 * calcKFactorMultiplier(p1Score, p2Score, p1Rating, p2Rating)
      let p1Result = match.player1Games > match.player2Games ? 1 : match.player1Games < match.player2Games ? 0 : 0.5
      let p2Result = 1 - p1Result
      let player1NewRating = calcNewRating(p1Prob, p1Result, p1Rating, kFactor)
      let player2NewRating = calcNewRating(p2Prob, p2Result, p2Rating, kFactor)
      let ratings = {
        player1Prev: p1Rating,
        player2Prev: p2Rating,
        player1New: player1NewRating,
        player2New: player2NewRating
      }

      return Promise.all([
        orgRef.ref(`/players/${match.player1Uid}/rating`)
          .set(player1NewRating),
        orgRef.ref(`/players/${match.player2Uid}/rating`)
          .set(player2NewRating),
        orgRef.ref(`/matches/${event.params.matchUid}/ratings`)
          .set(ratings),
        orgRef.ref(`/users/${match.player1Uid}/matches/${event.params.matchUid}/ratings`)
          .set(ratings),
        orgRef.ref(`/users/${match.player2Uid}/matches/${event.params.matchUid}/ratings`)
          .set(ratings),
        orgRef.ref(`/players/${match.player1Uid}/rated`)
          .set(true),
        orgRef.ref(`/players/${match.player2Uid}/rated`)
          .set(true)
      ])
    })
  })

let calcKFactorMultiplier = (score1, score2, rating1, rating2) => {
  let diff = Math.log(Math.abs(score1 - score2) + 1)
  let ratingDiff = score1 > score2 ? rating1 - rating2 : rating2 - rating1
  return diff * (2.2 / (ratingDiff * 0.001 + 2.2))
}
let calcNewRating = (expected, actual, current, kFactor) => {
  return Math.round(current + kFactor * (actual - expected))
}
let calcProbability = (rating1, rating2) => {
  return 1 / (1 + Math.pow(10, ((rating2 - rating1) / 400)))
}
<commit_msg>fixed require<commit_after>require('./api')

const functions = require('firebase-functions')
const admin = require('firebase-admin')
admin.initializeApp(functions.config().firebase)

exports.createUser = functions.auth.user().onCreate(event => {
  return admin.database()
    .ref('/users')
    .child(event.data.uid)
    .set({
      email: event.data.email,
      displayName: event.data.displayName,
      photoURL: event.data.photoURL
      // rating: 750,
      // rated: false,
      // active: true
    })
})
exports.draftMatch = functions.database.ref('/orgs/{org}/users/{userUid}/draft-matches/{matchUid}')
  .onCreate(event => {
    if (event.auth.admin) { return }
    let orgRef = admin.database().ref(`/orgs/${event.params.org}`)
    let playersPromise = orgRef.ref(`/players`).once('value')
    let matchesPromise = orgRef.ref(`/users/${event.params.userUid}/matches/`).once('value')
    let openMatchesPromise = orgRef.ref('/open-match-details').once('value')
    let userOpenMatchesPromise = orgRef.ref(`/users/${event.params.userUid}/open-matches/`).once('value')

    return Promise.all([playersPromise, matchesPromise, openMatchesPromise, userOpenMatchesPromise])
      .then((snaps) => {
        let players = {}
        let playerRating = 0

        if (snaps[3].numChildren() >= 5) {
          return orgRef
            .ref(`/users/${event.params.userUid}/draft-matches/${event.params.matchUid}`)
            .set(null)
        }

        snaps[0].forEach((playerSnapshot) => {
          let player = playerSnapshot.val()
          player.matchesCount = 0
          player.openMatchesCount = 0
          player.uid = playerSnapshot.key
          players[playerSnapshot.key] = player
          if (playerSnapshot.key === event.params.userUid) { playerRating = player.rating }
        })
        snaps[1].forEach((matchSnapshot) => {
          let match = matchSnapshot.val()
          if (match.player1Uid === event.params.userUid) {
            players[match.player2Uid].matchesCount++
          } else {
            players[match.player1Uid].matchesCount++
          }
        })
        snaps[2].forEach((openMatchSnapshot) => {
          let openMatch = openMatchSnapshot.val()
          if (openMatch.player1Uid === event.params.userUid) {
            players[openMatch.player2Uid].hasOpenMatch = true
          } else if (openMatch.player2Uid === event.params.userUid) {
            players[openMatch.player1Uid].hasOpenMatch = true
          }
          players[openMatch.player1Uid].openMatchesCount++
          players[openMatch.player2Uid].openMatchesCount++
        })

        let oponents = Object.keys(players)
          .map(key => players[key])
          .filter((p) => p.active && p.rated && p.uid !== event.params.userUid)
          .filter((p) => !p.hasOpenMatch && p.openMatchesCount < 5)
          .filter((p) => Math.abs(p.rating - playerRating) <= 300)
          .sort((a, b) => a.matchesCount - b.matchesCount ||
            Math.abs(a.rating - playerRating) - Math.abs(b.rating - playerRating))

        if (oponents.length === 0) {
          return orgRef
            .ref(`/users/${event.params.userUid}/draft-match-details/${event.params.matchUid}/noOponents`)
            .set(true)
        }

        let details = {
          player1Uid: event.params.userUid,
          player1Probability: calcProbability(playerRating, oponents[0].rating),
          player2Uid: oponents[0].uid,
          player2Probability: calcProbability(oponents[0].rating, playerRating),
          timestamp: admin.database.ServerValue.TIMESTAMP
        }

        return orgRef
          .ref(`/users/${event.params.userUid}/draft-match-details/${event.params.matchUid}`)
          .set(details)
      })
  })
exports.acceptDraftMatch = functions.database.ref('/orgs/{org}/users/{userUid}/draft-matches/{matchUid}/accept')
  .onCreate(event => {
    if (event.auth.admin) { return }
    let orgRef = admin.database().ref(`/orgs/${event.params.org}`)

    return orgRef
      .ref(`/users/${event.params.userUid}/draft-match-details/${event.params.matchUid}`)
      .once('value')
      .then((snap) => {
        let draftDetails = snap.val()
        let match = {
          player1Uid: draftDetails.player1Uid,
          player2Uid: draftDetails.player2Uid,
          createdDate: new Date().toISOString()
        }
        let details = Object.assign({
          approvals: {player1: '', player2: ''},
          scores: [{player1: 0, player2: 0}]
        }, match)

        return Promise.all([
          orgRef
            .ref(`/users/${match.player1Uid}/open-matches/${event.params.matchUid}`)
            .set(match),
          orgRef
            .ref(`/users/${match.player2Uid}/open-matches/${event.params.matchUid}`)
            .set(match),
          orgRef
            .ref(`/open-match-details/${event.params.matchUid}`)
            .set(details),
          orgRef
            .ref(`/players/${match.player1Uid}/active`)
            .set(true),
          orgRef
            .ref(`/users/${match.player1Uid}/draft-matches`)
            .set(null),
          orgRef
            .ref(`/users/${match.player1Uid}/draft-match-details`)
            .set(null)
        ])
      })
  })
exports.openMatchDetailsCreated = functions.database.ref('/orgs/{org}/open-match-details/{matchUid}')
  .onCreate(event => {
    let match = event.data.val()
    let orgRef = admin.database().ref(`/orgs/${event.params.org}`)

    return Promise.all([
      orgRef.ref(`/players/${match.player1Uid}`).once('value'),
      orgRef.ref(`/messaging/${match.player2Uid}`).once('value')
    ]).then((snaps) => {
      let player1 = snaps[0].val()
      let player2Messaging = snaps[1].val()
      if (!player2Messaging || !player2Messaging.token) return

      let notificationPayload = {
        notification: {
          title: 'Match Created!',
          body: `${player1.displayName} created ranked match with you. Go and win!`
        }
      }

      return admin.messaging().sendToDevice([player2Messaging.token], notificationPayload)
    })
  })
exports.approvalsChanged = functions.database.ref('/orgs/{org}/open-match-details/{matchUid}/approvals')
  .onUpdate((event) => {
    if (event.auth.admin) { return }
    let orgRef = admin.database().ref(`/orgs/${event.params.org}`)

    let approvals = event.data.val()
    if (!approvals.player1 || !approvals.player2) { return }

    return orgRef.ref(`/open-match-details/${event.params.matchUid}`)
      .once('value')
      .then((snap) => {
        let details = snap.val()
        let closingApproval = details.scores.reduce((approval, score) => {
          return approval + `|${score.player1}|${score.player2}`
        }, '')
        if (closingApproval !== details.approvals.player1 ||
          closingApproval !== details.approvals.player2) {
          return
        }

        let player1Games = details.scores.filter((s) => Number(s.player1) > Number(s.player2)).length
        let player2Games = details.scores.length - player1Games

        let finalMatch = {
          finalizedDate: new Date().toISOString(),
          createdDate: details.createdDate,
          player1Uid: details.player1Uid,
          player2Uid: details.player2Uid,
          scores: details.scores,
          player1Games: player1Games,
          player2Games: player2Games,
          winnerUid: player1Games > player2Games ? details.player1Uid : details.player2Uid
        }
        return Promise.all([
          orgRef.ref(`/matches/${event.params.matchUid}`).set(finalMatch),
          orgRef.ref(`/users/${details.player1Uid}/matches/${event.params.matchUid}`).set(finalMatch),
          orgRef.ref(`/users/${details.player2Uid}/matches/${event.params.matchUid}`).set(finalMatch),
          orgRef.ref(`/users/${details.player1Uid}/open-matches/${event.params.matchUid}`).set(null),
          orgRef.ref(`/users/${details.player2Uid}/open-matches/${event.params.matchUid}`).set(null),
          orgRef.ref(`/open-match-details/${event.params.matchUid}`).set(null)
        ])
      })
  })
exports.updateRating = functions.database.ref('/orgs/{org}/matches/{matchUid}')
  .onCreate(event => {
    let orgRef = admin.database().ref(`/orgs/${event.params.org}`)
    let match = event.data.val()
    return Promise.all([
      orgRef.ref(`/players/${match.player1Uid}`).once('value'),
      orgRef.ref(`/players/${match.player2Uid}`).once('value')
    ]).then((playerRefs) => {
      let p1Rating = playerRefs[0].val().rating
      let p2Rating = playerRefs[1].val().rating
      let p1Score = match.scores.reduce((total, i) => total + Number(i.player1), 0)
      let p2Score = match.scores.reduce((total, i) => total + Number(i.player2), 0)
      let p1Prob = calcProbability(p1Rating, p2Rating)
      let p2Prob = calcProbability(p2Rating, p1Rating)
      let kFactor = 50 * calcKFactorMultiplier(p1Score, p2Score, p1Rating, p2Rating)
      let p1Result = match.player1Games > match.player2Games ? 1 : match.player1Games < match.player2Games ? 0 : 0.5
      let p2Result = 1 - p1Result
      let player1NewRating = calcNewRating(p1Prob, p1Result, p1Rating, kFactor)
      let player2NewRating = calcNewRating(p2Prob, p2Result, p2Rating, kFactor)
      let ratings = {
        player1Prev: p1Rating,
        player2Prev: p2Rating,
        player1New: player1NewRating,
        player2New: player2NewRating
      }

      return Promise.all([
        orgRef.ref(`/players/${match.player1Uid}/rating`)
          .set(player1NewRating),
        orgRef.ref(`/players/${match.player2Uid}/rating`)
          .set(player2NewRating),
        orgRef.ref(`/matches/${event.params.matchUid}/ratings`)
          .set(ratings),
        orgRef.ref(`/users/${match.player1Uid}/matches/${event.params.matchUid}/ratings`)
          .set(ratings),
        orgRef.ref(`/users/${match.player2Uid}/matches/${event.params.matchUid}/ratings`)
          .set(ratings),
        orgRef.ref(`/players/${match.player1Uid}/rated`)
          .set(true),
        orgRef.ref(`/players/${match.player2Uid}/rated`)
          .set(true)
      ])
    })
  })

let calcKFactorMultiplier = (score1, score2, rating1, rating2) => {
  let diff = Math.log(Math.abs(score1 - score2) + 1)
  let ratingDiff = score1 > score2 ? rating1 - rating2 : rating2 - rating1
  return diff * (2.2 / (ratingDiff * 0.001 + 2.2))
}
let calcNewRating = (expected, actual, current, kFactor) => {
  return Math.round(current + kFactor * (actual - expected))
}
let calcProbability = (rating1, rating2) => {
  return 1 / (1 + Math.pow(10, ((rating2 - rating1) / 400)))
}
<|endoftext|>


<commit_before>'use strict';

const functions = require('firebase-functions');
const admin = require('firebase-admin');

admin.initializeApp(functions.config().firebase);

exports.search = functions.https.onRequest(function search(req, res) {
  const searchTerm = req.query.q;
  admin.database().ref('/books').once('value').then(function data(snapshot) {
    const valAsArray = convertToArray(snapshot.val());
    const result = filterByName(searchTerm, valAsArray);
    res.json({ result: result });
  }).catch(function onError(error) {
    res.json(error);
  });
});

function convertToArray(val) {
  const array = [];
  for (const key in val) {
    array.push(Object.assign({ uid: key }, val[key]));
  }

  return array;
}

function filterByName(term = '', values = []) {
  const search = createSearchExp(term);
  return values.filter(n => search.test(n.title));
}

function createSearchExp(val = '') {
  const exp = val.replace(/ /g, '.*');
  return new RegExp(`.*${exp}.*`, 'gi')
}
<commit_msg>feat(search): accept limit param to limit the results quantity<commit_after>'use strict';

const functions = require('firebase-functions');
const admin = require('firebase-admin');

admin.initializeApp(functions.config().firebase);

exports.search = functions.https.onRequest(function search(req, res) {
  const { q, limit } = req.query;
  if (!q) res.status(400).json("Search term parameter is not optional");
  if (!limit) limit = 12;

  admin.database().ref('/books').limitToFirst(limit).once('value').then(function data(snapshot) {
    const result = filterByName(searchTerm, toArray(snapshot.val()));
    res.json({ term: q, result: result });
  }).catch(function onError(error) {
    res.json(error);
  });
});

function toArray(val) {
  const array = [];
  for (const key in val) {
    array.push(Object.assign({ uid: key }, val[key]));
  }

  return array;
}

function filterByName(term = '', values = []) {
  const exp = searchExpression(term);
  return values.filter(n => exp.test(n.title));
}

function searchExpression(val = '') {
  const exp = val.replace(/ /g, '.*');
  return new RegExp(`.*${exp}.*`, 'gi')
}
<|endoftext|>


<commit_before>const functions = require('firebase-functions');

// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
// exports.helloWorld = functions.https.onRequest((request, response) => {
//  response.send("Hello from Firebase!");
// });
<commit_msg>start adding functions<commit_after>'use strict';

// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');

// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

exports.computeCrimePercentages = functions.database.ref('crimedata/{pushId}')
  .onWrite(event => {
    console.log('Recalculating crime percentages for new message', event.params.pushId);
  });

<|endoftext|>


<commit_before>"use strict"

const functions = require('firebase-functions')
const admin = require("firebase-admin")

// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
exports.helloWorld = functions.https.onRequest((request, response) => {
    response.send("Hello from Firebase!");
});

const second = 1000
const minute = 60 * second

exports.testPush = functions.https.onRequest((request, response) => {
    // admin.database().ref('/messages').push({original: original}).then(snapshot => {
    //     // Redirect with 303 SEE OTHER to the URL of the pushed object in the Firebase console.
    //     res.redirect(303, snapshot.ref);
    // });
    return admin.messaging()
         .sendToDevice(
             "fs_Fhp9WAGc:APA91bH9WAT9nfgn1XJhrkZtxojLOlX3o6fdA6oJ0U2ZMPaOFrDzfzy7VVuwOpUeT2YGrHf2eN63arSVwkDpZeXywP5bpgYT-ntJfyf1bcwiErmF72Uh2Bi__nlO61L0oOXxSDEsLRWs",
             {data: {id: "7aIPoEclCGfR6lPUXb71hGXdoETwthsaETqSK98Bne2qyw2uWJcTgKDj03lpPCDt"}},
             {timeToLive: (10 * minute), priority:"high"}
         )
        .then(()=>response.send("push sent"))
});
<commit_msg>setting firebase app config<commit_after>"use strict"

const functions = require('firebase-functions')
const admin = require("firebase-admin")

admin.initializeApp(functions.config().firebase);

// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
exports.helloWorld = functions.https.onRequest((request, response) => {
    response.send("Hello from Firebase!");
});

const second = 1000
const minute = 60 * second

exports.testPush = functions.https.onRequest((request, response) => {
    // admin.database().ref('/messages').push({original: original}).then(snapshot => {
    //     // Redirect with 303 SEE OTHER to the URL of the pushed object in the Firebase console.
    //     res.redirect(303, snapshot.ref);
    // });
    return admin.messaging()
         .sendToDevice(
             "fs_Fhp9WAGc:APA91bH9WAT9nfgn1XJhrkZtxojLOlX3o6fdA6oJ0U2ZMPaOFrDzfzy7VVuwOpUeT2YGrHf2eN63arSVwkDpZeXywP5bpgYT-ntJfyf1bcwiErmF72Uh2Bi__nlO61L0oOXxSDEsLRWs",
             {data: {id: "7aIPoEclCGfR6lPUXb71hGXdoETwthsaETqSK98Bne2qyw2uWJcTgKDj03lpPCDt"}},
             {timeToLive: (10 * minute), priority:"high"}
         )
        .then(()=>response.send("push sent"))
});
<|endoftext|>


<commit_before>/**
 * @fileoverview Closure compiler externs for the Polymer library.
 *
 * @externs
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt. The complete set of authors may be
 * found at http://polymer.github.io/AUTHORS.txt. The complete set of
 * contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt. Code
 * distributed by Google as part of the polymer project is also subject to an
 * additional IP rights grant found at http://polymer.github.io/PATENTS.txt.
 */

/**
 * @param {string} name The name of the declared Polymer element.
 * @param {PolymerElement} prototype The prototype of the element.
 */
var Polymer = function(name, prototype) {};

/** @constructor @extends {HTMLElement} */
var PolymerElement = function() {
  /** @type {Object.<string, HTMLElement>} */
  this.$;
};

// Canonical docs for these lifecycle callbacks are here:
// http://www.polymer-project.org/docs/polymer/polymer.html#lifecyclemethods

/** On create callback. */
PolymerElement.prototype.created = function() {};
/** On ready callback. */
PolymerElement.prototype.ready = function() {};
/** On attached to the DOM callback. */
PolymerElement.prototype.attached = function() {};
/** On DOM ready callback. */
PolymerElement.prototype.domReady = function() {};
/** On detached from the DOM callback. */
PolymerElement.prototype.detached = function() {};

/**
 * Fire a callback when the light DOM children of an element changes.
 * `callback` is called at most once, and should re-register with onMutation
 * if it cares about further changes to the light DOM.
 *
 * @param {HTMLElement} domElement The element to observe for changes. Often
 *     the polymerElement itself.
 * @param {Function} callback The function to call when changes happen.
 */
PolymerElement.prototype.onMutation = function(domElement, callback) {};

/**
 * Call the callback after a given timeout.
 *
 * @param {Function} callback The function to call after the delay. Called with
 *     `this` bound to the polymerElement.
 * @param {Array=} opt_args Arguments to pass to callback.
 * @param {number=} opt_timeoutMillis Minimum delay in milliseconds before
 *     calling the callback.
 */
PolymerElement.prototype.async = function(
      callback, opt_args, opt_timeoutMillis) {};

/**
 * Call the callback after a timeout. Calling job again with the same name
 * resets the timer but will not result in additional calls to callback.
 *
 * @param {string} name
 * @param {Function} callback
 * @param {number} timeoutMillis The minimum delay in milliseconds before
 *     calling the callback.
 */
PolymerElement.prototype.job = function(name, callback, timeoutMillis) {};

/**
 * Fire an event.
 *
 * @param {string} type An event name.
 * @param {*=} detail
 * @param {Node=} onNode Target node.
 * @param {boolean=} bubbles Set false to prevent bubbling, defaults to true.
 * @param {boolean=} cancelable Set false to prevent cancellation, defaults to
 *     true.
 * @return {Object} event
 */
PolymerElement.prototype.fire =
    function(type, detail, onNode, bubbles, cancelable) {};
<commit_msg>externs: attributeChanged, Element -> Node, !null <commit_after>/**
 * @fileoverview Closure compiler externs for the Polymer library.
 *
 * @externs
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt. The complete set of authors may be
 * found at http://polymer.github.io/AUTHORS.txt. The complete set of
 * contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt. Code
 * distributed by Google as part of the polymer project is also subject to an
 * additional IP rights grant found at http://polymer.github.io/PATENTS.txt.
 */

/**
 * @param {string} name The name of the declared Polymer element.
 * @param {PolymerElement} prototype The prototype of the element.
 */
var Polymer = function(name, prototype) {};

/** @constructor @extends {HTMLElement} */
var PolymerElement = function() {
  /** @type {Object.<string, !HTMLElement>} */
  this.$;
};

// Canonical docs for these lifecycle callbacks are here:
// http://www.polymer-project.org/docs/polymer/polymer.html#lifecyclemethods

/** On create callback. */
PolymerElement.prototype.created = function() {};
/** On ready callback. */
PolymerElement.prototype.ready = function() {};
/** On attached to the DOM callback. */
PolymerElement.prototype.attached = function() {};
/** On DOM ready callback. */
PolymerElement.prototype.domReady = function() {};
/** On detached from the DOM callback. */
PolymerElement.prototype.detached = function() {};

/**
 * Callback fired when an attribute on the element has been added, changed, or removed.
 *
 * @param {string} name The name of the attribute that changed.
 * @param {*} oldValue The previous value of the attribute, or null if it was added.
 * @param {*} newValue The new value of the attribute, or null if it was removed.
 * @param {string} namespace The namespace of the attribute.
 */
PolymerElement.prototype.attributeChanged = function(name, oldValue, newValue, namespace) {};

/**
 * Fire a callback when the light DOM children of an element changes.
 * `callback` is called at most once, and should re-register with onMutation
 * if it cares about further changes to the light DOM.
 *
 * @param {!Node} domNode The node to observe for changes. Often
 *     the polymerElement itself.
 * @param {Function} callback The function to call when changes happen.
 */
PolymerElement.prototype.onMutation = function(domNode, callback) {};

/**
 * Call the callback after a given timeout.
 *
 * @param {Function} callback The function to call after the delay. Called with
 *     `this` bound to the polymerElement.
 * @param {Array=} opt_args Arguments to pass to callback.
 * @param {number=} opt_timeoutMillis Minimum delay in milliseconds before
 *     calling the callback.
 */
PolymerElement.prototype.async = function(
      callback, opt_args, opt_timeoutMillis) {};

/**
 * Call the callback after a timeout. Calling job again with the same name
 * resets the timer but will not result in additional calls to callback.
 *
 * @param {string} name
 * @param {Function} callback
 * @param {number} timeoutMillis The minimum delay in milliseconds before
 *     calling the callback.
 */
PolymerElement.prototype.job = function(name, callback, timeoutMillis) {};

/**
 * Fire an event.
 *
 * @param {string} type An event name.
 * @param {*=} detail
 * @param {Node=} onNode Target node.
 * @param {boolean=} bubbles Set false to prevent bubbling, defaults to true.
 * @param {boolean=} cancelable Set false to prevent cancellation, defaults to
 *     true.
 * @return {Object} event
 */
PolymerElement.prototype.fire =
    function(type, detail, onNode, bubbles, cancelable) {};
<|endoftext|>


<commit_before>import React, { PropTypes } from 'react'
import InputWrapper from './InputWrapper'

const Input = ({ type, name, value, min, onChange }) => (

    {name}
    <input type={type} defaultValue={value} onChange={onChange} min={min || null} />

)

Input.propTypes = {
  type: PropTypes.string,
  name: PropTypes.string,
  min: PropTypes.string,
  onChange: PropTypes.func
}

export default InputWrapper(Input)
<commit_msg>add name to input<commit_after>import React, { PropTypes } from 'react'
import InputWrapper from './InputWrapper'

const Input = ({ type, name, value, min, onChange }) => (

    {name}
    <input type={type} name={name} defaultValue={value} onChange={onChange} min={min || null} />

)

Input.propTypes = {
  type: PropTypes.string,
  name: PropTypes.string,
  min: PropTypes.string,
  onChange: PropTypes.func
}

export default InputWrapper(Input)
<|endoftext|>


<commit_before>// Copyright (c) Rotorz Limited. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root.

/** @module webreed/lib/interfaces/Mode */

/**
 * Interface for a mode that reads and writes resource files.
 *
 * @interface module:webreed/lib/interfaces/Mode
 */

  /**
   * Reads resource data from a file.
   *
   * @method module:webreed/lib/interfaces/Mode#readFile
   *
   * @param {string} path
   *   Path to the resource file.
   * @param {module:webreed/lib/ResourceType} [resourceType]
   *   Configuration of the type of source resource.
   *
   * @returns {Promise.<object>}
   *   A promise to return an `object` representing the data and its frontmatter.
   */

  /**
   * Writes resource to a file.
   *
   * @method module:webreed/lib/interfaces/Mode#writeFile
   *
   * @param {string} path
   *   Path to the output file.
   * @param {module:webreed/lib/Resource|object} resource
   *   The resource that is to be written.
   * @param {module:webreed/lib/ResourceType} [resourceType]
   *   Configuration of the type of source resource.
   *
   * @returns {Promise}
   *   A promise to notify upon completion; or to throw an error.
   */
<commit_msg>Argument 'resourceType' is now required for resource mode methods.<commit_after>// Copyright (c) Rotorz Limited. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root.

/** @module webreed/lib/interfaces/Mode */

/**
 * Interface for a mode that reads and writes resource files.
 *
 * @interface module:webreed/lib/interfaces/Mode
 */

  /**
   * Reads resource data from a file.
   *
   * @method module:webreed/lib/interfaces/Mode#readFile
   *
   * @param {string} path
   *   Path to the resource file.
   * @param {module:webreed/lib/ResourceType} resourceType
   *   Represents the type of resource that is being read.
   *
   * @returns {Promise.<object>}
   *   A promise to return an `object` representing the data and its frontmatter.
   */

  /**
   * Writes resource to a file.
   *
   * @method module:webreed/lib/interfaces/Mode#writeFile
   *
   * @param {string} path
   *   Path to the output file.
   * @param {module:webreed/lib/Resource|object} resource
   *   The resource that is to be written.
   * @param {module:webreed/lib/ResourceType} resourceType
   *   Represents the type of resource that is being written.
   *
   * @returns {Promise}
   *   A promise to notify upon completion; or to throw an error.
   */
<|endoftext|>