<commit_before>"use strict";

var a, b, c, cb, log; // for DEBUG only
c = setTimeout(function() { // currentFirst will be reloaded when window.focus
  c = 0;
  Settings.postUpdate("updateAll", { name: "reRegisterFrame", work: "rereg" });
}, 50); // According to tests: onInstalled will be executed after 0 ~ 16 ms if needed
setTimeout(function() {
  a = c = null, b = cb, log = console.log.bind(console);
}, 100);
cb = function(b) { a = b; console.log(b); };

if (Settings.get("vimSync") === true) {
  Settings.Sync = {
    debug: window._DEBUG,
    storage: chrome.storage.sync,
    doNotSync: ["settingsVersion", "previousVersion"],
    fetchAsync: function() {
      var _this = this;
      this.storage.get(null, function(items) {
        var key, value, hasOwn;
        if (chrome.runtime.lastError) {
          return chrome.runtime.lastError;
        }
        hasOwn = Object.prototype.hasOwnProperty;
        for (key in items) {
          if (!hasOwn.call(items, key)) continue;
          value = items[key];
          _this.storeAndPropagate(key, value);
        }
      });
    },
    handleStorageUpdate: function(changes, area) {
      var change, key;
      for (key in changes) {
        if (!Object.prototype.hasOwnProperty.call(changes, key)) continue;
        change = changes[key];
        this.storeAndPropagate(key, change != null ? change.newValue : undefined);
      }
    },
    storeAndPropagate: function(key, value) {
      var defaultValue, defaultValueJSON;
      if (!(key in Settings.defaults)) {
        return;
      }
      if (!this.shouldSyncKey(key)) {
        return;
      }
      if (value && key in localStorage && localStorage[key] === value) {
        return;
      }
      defaultValue = Settings.defaults[key];
      defaultValueJSON = JSON.stringify(defaultValue);
      if (value && value !== defaultValueJSON) {
        value = JSON.parse(value);
      } else {
        value = defaultValue;
      }
      Settings.set(key, value);
    },
    set: function(key, value) {
      if (this.shouldSyncKey(key)) {
        var settings = {};
        settings[key] = value;
        this.storage.set(settings);
      }
    },
    clear: function(key) {
      if (this.shouldSyncKey(key)) {
        this.storage.remove(key);
      }
    },
    shouldSyncKey: function(key) {
      return this.doNotSync.index(key) < 0;
    }
  };
  chrome.storage.onChanged.addListener(Settings.Sync.handleStorageUpdate.bind(Settings.Sync));
  Settings.Sync.fetchAsync();
}

if (chrome.browserAction && chrome.browserAction.setIcon) (function() {
  var currentBadge, badgeTimer, updateBadge, time1 = 50, set, shouldShow;
  chrome.browserAction.setBadgeBackgroundColor({color: [82, 156, 206, 255]});
  updateBadge = function(badge) {
    badgeTimer = 0;
    chrome.browserAction.setBadgeText({text: badge});
  };
  g_requestHandlers.setBadge = function(request) {
    var badge = request.badge;
    if (shouldShow && badge != null && badge !== currentBadge) {
      currentBadge = badge;
      if (badgeTimer) {
        clearTimeout(badgeTimer);
      }
      badgeTimer = setTimeout(updateBadge, time1, badge);
    }
  };
  g_requestHandlers.setIcon = function(tabId, type) {
    if (shouldShow) {
      chrome.browserAction.setIcon({
        tabId: tabId,
        path: Settings.icons[type]
      });
    }
  };
  set = function (value) {
    if (value === shouldShow) { return; }
    // TODO: hide icon
    if (shouldShow = value) {
      chrome.browserAction.enable();
    } else {
      chrome.browserAction.disable();
    }
  };
  Settings.updateHooks.showActionIcon = set;
  set(Settings.get("showActionIcon"));
})();

chrome.runtime.onInstalled.addListener(window.b = function(details) {
  var contentScripts, js, css, allFrames, _i, _len, reason = details.reason;
  if (reason === "install") { reason = ""; }
  else if (reason === "update") { reason = details.previousVersion; }
  else { return; }
  if (window.c > 0) {
    clearTimeout(window.c);
  }
  contentScripts = Settings.ContentScripts;
  js = contentScripts.js;
  css = (!reason || window._DEBUG >= 3) ? contentScripts.css : [];
  allFrames = contentScripts.all_frames;
  contentScripts = null;
  for (_i = css.length; 0 <= --_i; ) {
    css[_i] = {file: css[_i], allFrames: allFrames};
  }
  for (_i = js.length; 0 <= --_i; ) {
    js[_i] = {file: js[_i], allFrames: allFrames};
  }
  chrome.tabs.query({
    windowType: "normal",
    status: "complete"
  }, function(tabs) {
    var _i = tabs.length, tabId, _j, _len, callback, url, t = chrome.tabs;
    callback = function() { return chrome.runtime.lastError; };
    for (; 0 <= --_i; ) {
      url = tabs[_i].url;
      if (url.startsWith("chrome") || url.indexOf("://") === -1) continue;
      tabId = tabs[_i].id;
      for (_j = 0, _len = css.length; _j < _len; ++_j)
        t.insertCSS(tabId, css[_j], callback);
      for (_j = 0, _len = js.length; _j < _len; ++_j)
        t.executeScript(tabId, js[_j], callback);
    }
    console.log("%cvim %chas %cinstalled", "color:blue", "color:auto", "color:red", details);
  });

  if (!reason && chrome.notifications && chrome.notifications.create) { return; }

  var func = function(versionA, versionB) {
    var a, b, i, _i, _ref;
    versionA = versionA.split('.');
    versionB = versionB.split('.');
    for (i = 0, _ref = Math.max(versionA.length, versionB.length); i < _ref; ++i) {
      a = parseInt(versionA[i] || 0, 10);
      b = parseInt(versionB[i] || 0, 10);
      if (a < b) {
        return -1;
      } else if (a > b) {
        return 1;
      }
    }
    return 0;
  };
  if (func(Settings.CurrentVersion, reason) === 0) { return; }

  var key = "vimium++_upgradeNotification";
  chrome.notifications.create(key, {
    type: "basic",
    iconUrl: chrome.runtime.getURL("favicon.ico"),
    title: "Vimium++ Upgrade",
    message: "Vimium++ has been upgraded to version " + Settings.CurrentVersion
      + ". Click here for more information.",
    isClickable: true
  }, function() {
    if (chrome.runtime.lastError) {
      return chrome.runtime.lastError;
    }
    chrome.notifications.onClicked.addListener(function(id) {
      if (id !== key) { return; }
      chrome.tabs.create({
        url: "https://github.com/gdh1995/vimium-plus#release-notes"
      }, function(tab) {
        chrome.windows.update(tab.windowId, {focused: true});
      });
      chrome.notifications.clear(key, function() {
        return chrome.runtime.lastError;
      });
    });
  });
});
setTimeout(function() {
  chrome.runtime.onInstalled.removeListener(window.b);
  window.b = null;
}, 50);
<commit_msg>fix a bug that Settings.ContentScripts should have been const<commit_after>"use strict";

var a, b, c, cb, log; // for DEBUG only
c = setTimeout(function() { // currentFirst will be reloaded when window.focus
  c = 0;
  Settings.postUpdate("updateAll", { name: "reRegisterFrame", work: "rereg" });
}, 50); // According to tests: onInstalled will be executed after 0 ~ 16 ms if needed
setTimeout(function() {
  a = c = null, b = cb, log = console.log.bind(console);
}, 100);
cb = function(b) { a = b; console.log(b); };

if (Settings.get("vimSync") === true) {
  Settings.Sync = {
    debug: window._DEBUG,
    storage: chrome.storage.sync,
    doNotSync: ["settingsVersion", "previousVersion"],
    fetchAsync: function() {
      var _this = this;
      this.storage.get(null, function(items) {
        var key, value, hasOwn;
        if (chrome.runtime.lastError) {
          return chrome.runtime.lastError;
        }
        hasOwn = Object.prototype.hasOwnProperty;
        for (key in items) {
          if (!hasOwn.call(items, key)) continue;
          value = items[key];
          _this.storeAndPropagate(key, value);
        }
      });
    },
    handleStorageUpdate: function(changes, area) {
      var change, key;
      for (key in changes) {
        if (!Object.prototype.hasOwnProperty.call(changes, key)) continue;
        change = changes[key];
        this.storeAndPropagate(key, change != null ? change.newValue : undefined);
      }
    },
    storeAndPropagate: function(key, value) {
      var defaultValue, defaultValueJSON;
      if (!(key in Settings.defaults)) {
        return;
      }
      if (!this.shouldSyncKey(key)) {
        return;
      }
      if (value && key in localStorage && localStorage[key] === value) {
        return;
      }
      defaultValue = Settings.defaults[key];
      defaultValueJSON = JSON.stringify(defaultValue);
      if (value && value !== defaultValueJSON) {
        value = JSON.parse(value);
      } else {
        value = defaultValue;
      }
      Settings.set(key, value);
    },
    set: function(key, value) {
      if (this.shouldSyncKey(key)) {
        var settings = {};
        settings[key] = value;
        this.storage.set(settings);
      }
    },
    clear: function(key) {
      if (this.shouldSyncKey(key)) {
        this.storage.remove(key);
      }
    },
    shouldSyncKey: function(key) {
      return this.doNotSync.index(key) < 0;
    }
  };
  chrome.storage.onChanged.addListener(Settings.Sync.handleStorageUpdate.bind(Settings.Sync));
  Settings.Sync.fetchAsync();
}

if (chrome.browserAction && chrome.browserAction.setIcon) (function() {
  var currentBadge, badgeTimer, updateBadge, time1 = 50, set, shouldShow;
  chrome.browserAction.setBadgeBackgroundColor({color: [82, 156, 206, 255]});
  updateBadge = function(badge) {
    badgeTimer = 0;
    chrome.browserAction.setBadgeText({text: badge});
  };
  g_requestHandlers.setBadge = function(request) {
    var badge = request.badge;
    if (shouldShow && badge != null && badge !== currentBadge) {
      currentBadge = badge;
      if (badgeTimer) {
        clearTimeout(badgeTimer);
      }
      badgeTimer = setTimeout(updateBadge, time1, badge);
    }
  };
  g_requestHandlers.setIcon = function(tabId, type) {
    if (shouldShow) {
      chrome.browserAction.setIcon({
        tabId: tabId,
        path: Settings.icons[type]
      });
    }
  };
  set = function (value) {
    if (value === shouldShow) { return; }
    // TODO: hide icon
    if (shouldShow = value) {
      chrome.browserAction.enable();
    } else {
      chrome.browserAction.disable();
    }
  };
  Settings.updateHooks.showActionIcon = set;
  set(Settings.get("showActionIcon"));
})();

chrome.runtime.onInstalled.addListener(window.b = function(details) {
  var contentScripts, func, js, css, reason = details.reason;
  if (reason === "install") { reason = ""; }
  else if (reason === "update") { reason = details.previousVersion; }
  else { return; }
  if (window.c > 0) {
    clearTimeout(window.c);
  }

  contentScripts = chrome.runtime.getManifest().content_scripts[0];
  if (contentScripts.all_frames) {
    func = function(url) { return {file: url, allFrames: true}; };
  } else {
    func = function(url) { return {file: url, allFrames: false}; };
  }
  css = contentScripts.css.map(func);
  js = contentScripts.js.map(func);
  contentScripts = null;
  chrome.tabs.query({
    windowType: "normal",
    status: "complete"
  }, function(tabs) {
    var _i = tabs.length, tabId, _j, _len, callback, url, t = chrome.tabs;
    callback = function() { return chrome.runtime.lastError; };
    for (; 0 <= --_i; ) {
      url = tabs[_i].url;
      if (url.startsWith("chrome") || url.indexOf("://") === -1) continue;
      tabId = tabs[_i].id;
      for (_j = 0, _len = css.length; _j < _len; ++_j)
        t.insertCSS(tabId, css[_j], callback);
      for (_j = 0, _len = js.length; _j < _len; ++_j)
        t.executeScript(tabId, js[_j], callback);
    }
    console.log("%cvim %chas %cinstalled", "color:blue", "color:auto", "color:red", details);
  });

  if (!reason && chrome.notifications && chrome.notifications.create) { return; }

  func = function(versionA, versionB) {
    var a, b, i, _i, _ref;
    versionA = versionA.split('.');
    versionB = versionB.split('.');
    for (i = 0, _ref = Math.max(versionA.length, versionB.length); i < _ref; ++i) {
      a = parseInt(versionA[i] || 0, 10);
      b = parseInt(versionB[i] || 0, 10);
      if (a < b) {
        return -1;
      } else if (a > b) {
        return 1;
      }
    }
    return 0;
  };
  if (func(Settings.CurrentVersion, reason) === 0) { return; }

  var key = "vimium++_upgradeNotification";
  chrome.notifications.create(key, {
    type: "basic",
    iconUrl: chrome.runtime.getURL("favicon.ico"),
    title: "Vimium++ Upgrade",
    message: "Vimium++ has been upgraded to version " + Settings.CurrentVersion
      + ". Click here for more information.",
    isClickable: true
  }, function() {
    if (chrome.runtime.lastError) {
      return chrome.runtime.lastError;
    }
    chrome.notifications.onClicked.addListener(function(id) {
      if (id !== key) { return; }
      chrome.tabs.create({
        url: "https://github.com/gdh1995/vimium-plus#release-notes"
      }, function(tab) {
        chrome.windows.update(tab.windowId, {focused: true});
      });
      chrome.notifications.clear(key, function() {
        return chrome.runtime.lastError;
      });
    });
  });
});
setTimeout(function() {
  chrome.runtime.onInstalled.removeListener(window.b);
  window.b = null;
}, 50);
<|endoftext|>


<commit_before>var Scene = require('./scene.js'),
    util = require('util'),
    EventEmitter = require('events').EventEmitter,
    Gl3dLayout = require('./gl3dlayout'),
    Gl3dAxes = require('./gl3daxes'),
    Scatter3D = require('./scatter3d');

function SceneFrame () {
    'use strict';
    this.ID = 0;

    this.markerSymbols = {
        'circle': '●',
        'circle-open': '○',
        'square': '■',
        'square-open': '□',
        'diamond': '◆',
        'diamond-open': '◇',
        'cross': '+',
        'x': '❌'
    };

    this.modules = [Gl3dAxes, Gl3dLayout, Scatter3D];

}

util.inherits(SceneFrame, EventEmitter);

module.exports = new SceneFrame();

var proto = SceneFrame.prototype;

proto.createScene = function (opts) {
    'use strict';
    opts = opts || {};
    var container = opts.container || document.body;
    var newIframe = document.createElement('iframe');
    var glOptions = opts.glopts || {};
    var _this = this;
    newIframe.width = opts.width || '100%';
    newIframe.height = opts.height || '100%';
    newIframe.style.zIndex = '' + (opts.zIndex || '1000');
    newIframe.frameBorder = '0';
    newIframe.src = 'glcontext.html';

    newIframe.id = opts.id || ('scene-'+ this.ID);
    this.ID++;

    container.appendChild(newIframe);

    newIframe.onload = function () {
        var shell = newIframe.contentWindow.glnow({
            clearColor: [0,0,0,0],
            glOptions: glOptions,
            tickRate: 3
        });
        // Once the shell has initialized create and pass a new scene to the user.
        // set the container of the shell to be the new iframe
        shell.on('gl-init', function () {
            var scene = new Scene(opts, shell);
            scene.container = newIframe;
            scene.markerSymbols = _this.markerSymbols;

            _this.emit('scene-loaded', scene);
        });

    };
};
<commit_msg>feed new surface module into Plotly<commit_after>var Scene = require('./scene.js'),
    util = require('util'),
    EventEmitter = require('events').EventEmitter,
    Gl3dLayout = require('./gl3dlayout'),
    Gl3dAxes = require('./gl3daxes'),
    Scatter3D = require('./scatter3d'),
    Surface = require('./surface');

function SceneFrame () {
    'use strict';
    this.ID = 0;

    this.markerSymbols = {
        'circle': '●',
        'circle-open': '○',
        'square': '■',
        'square-open': '□',
        'diamond': '◆',
        'diamond-open': '◇',
        'cross': '+',
        'x': '❌'
    };

    this.modules = [Gl3dAxes, Gl3dLayout, Scatter3D, Surface];

}

util.inherits(SceneFrame, EventEmitter);

module.exports = new SceneFrame();

var proto = SceneFrame.prototype;

proto.createScene = function (opts) {
    'use strict';
    opts = opts || {};
    var container = opts.container || document.body;
    var newIframe = document.createElement('iframe');
    var glOptions = opts.glopts || {};
    var _this = this;
    newIframe.width = opts.width || '100%';
    newIframe.height = opts.height || '100%';
    newIframe.style.zIndex = '' + (opts.zIndex || '1000');
    newIframe.frameBorder = '0';
    newIframe.src = 'glcontext.html';

    newIframe.id = opts.id || ('scene-'+ this.ID);
    this.ID++;

    container.appendChild(newIframe);

    newIframe.onload = function () {
        var shell = newIframe.contentWindow.glnow({
            clearColor: [0,0,0,0],
            glOptions: glOptions,
            tickRate: 3
        });
        // Once the shell has initialized create and pass a new scene to the user.
        // set the container of the shell to be the new iframe
        shell.on('gl-init', function () {
            var scene = new Scene(opts, shell);
            scene.container = newIframe;
            scene.markerSymbols = _this.markerSymbols;

            _this.emit('scene-loaded', scene);
        });

    };
};
<|endoftext|>


<commit_before>var controller = (function(){

    var data;

    function initialize(){
    }

    function startGame(){
        // scene.loadScene(data);
        var results = loadTestCase([{
          "type": "family",
          "id": 1,
          "gators": [
              {
                "type": "gator",
                "id": 2,
                "colorId": 1
              }, {
               "type": "gator",
               "id": 3,
               "colorId": 2
              }
          ],
          "foodChain": [
            {
              "type": "egg",
              "id": 4,
              "colorId": 1
            }, {
              "type": "egg",
              "id": 5,
              "colorId": 2
            },
            {
              "type": "family",
              "id": 6,
              "gators": [
                {
                  "type": "gator",
                  "id": 7,
                  "colorId": 3
                }, {
                  "type": "gator",
                  "id": 8,
                  "colorId": 4
                }
              ],
              "foodChain": [
                {
                  "type": "egg",
                  "id": 9,
                  "colorId": 4
                }
              ]
            }
          ]
      }], 'and', 2);
        loadAnimation(results, 0);
        startAnimation(results, 0);
    }

    function getDataAsFamily(){
        return {type:"family",gators:[],foodChain:data};
    }

    function swapElements(id1, id2){
        console.log(JSON.stringify(data,4));
        traverseChild(getDataAsFamily(), function(child1, parent1, child1Name){
            if (child1.id == id1){
                traverseChild(getDataAsFamily(), function(child2, parent2, child2Name){
                    if (child2.id == id2){
                        console.log("Found");
                        parent2[child2Name] = child1;
                        parent1[child1Name] = child2;
                        return true;
                    }
                });
                return true;
            }
        });
        scene.loadScene(data);
        console.log(JSON.stringify(data,4));
    }

    function traverseChild(element, callback){
        if (element.type == "family"){
            if (element.gators.some(function(gator, index){
                if (callback(gator, element.gators, index)){
                    return true;
                }
            })) return true;
            if (element.foodChain.some(function(child, index){
                if (callback(child, element.foodChain, index)){
                    return true;
                }
                if (traverseChild(child, callback)){
                    return true;
                }
            })) return true;
        }
    }

    function loadTestCase(foodchain, testcase){
        var results = interpreter.test(foodchain, testcase);
        return results;
    }

    function loadAnimation(results, index, step){
        renderer.clear("#fff");
        scene.initialize();
        if (step){
            scene.loadScene(results[index].steps[step].state);
        }
        else{
            scene.loadScene(results[index].steps[0].state);
        }
    }
    function startAnimation(results, index){
        var i = 0;
        console.log(results[index].steps);
        var repeat = false;
        var interval = setInterval(function(){
            i++;
            console.log(i);
            if (i == results[index].steps.length){
                clearInterval(interval);
                return;
            }
            console.log(results[index].steps[i].events);
            var eaten_obj_id;
            function eats(events){
                console.log("events", events);
                for (var a = 0; a < events.length; a++){
                    console.log("events[a].action", events[a]);
                    if (events[a] && events[a].action && events[a].action == "eat"){
                        eaten_obj_id = events[a].food;
                        console.log("a", a);
                        console.log("eaten_obj_id", eaten_obj_id);

                        return true;
                    }
                }
            }
            if (!repeat && eats(results[index].steps[i].events)){
                console.log("Now", results[index].steps[i].events);
                console.log("Prev", results[index].steps[i-1].events);

                console.log("eats");
                repeat = true;
                renderer.clear("#fff");
                scene.initialize();
                var intermidiate = JSON.parse(JSON.stringify(results[index].steps[i].state));
                intermidiate.splice(1, 0, {
                      "id": eaten_obj_id,
                      "type": "family",
                      "gators":[],
                      "foodChain":[]
                });

                scene.loadScene(intermidiate);
                i = i-1;
            }
            else{
                console.log("no eats");
                repeat = false;
                loadAnimation(results, index, i);

            }
            console.log(results[index].steps[i].state);
        }, 5000);
    }

    return {
        "initialize": initialize,
        "swapElements": swapElements,
        "startGame": startGame,
        "loadAnimation": loadAnimation

    };
})();
<commit_msg>Add first state and blank first gator after eating<commit_after>var controller = (function(){

    var data;

    function initialize(){
    }

    function startGame(){
        // scene.loadScene(data);
        var results = loadTestCase([{
          "type": "family",
          "id": 1,
          "gators": [
              {
                "type": "gator",
                "id": 2,
                "colorId": 1
              }, {
               "type": "gator",
               "id": 3,
               "colorId": 2
              }
          ],
          "foodChain": [
            {
              "type": "egg",
              "id": 4,
              "colorId": 1
            }, {
              "type": "egg",
              "id": 5,
              "colorId": 2
            },
            {
              "type": "family",
              "id": 6,
              "gators": [
                {
                  "type": "gator",
                  "id": 7,
                  "colorId": 3
                }, {
                  "type": "gator",
                  "id": 8,
                  "colorId": 4
                }
              ],
              "foodChain": [
                {
                  "type": "egg",
                  "id": 9,
                  "colorId": 4
                }
              ]
            }
          ]
      }], 'and', 2);
        loadAnimation(results, 0);
        startAnimation(results, 0);
    }

    function getDataAsFamily(){
        return {type:"family",gators:[],foodChain:data};
    }

    function swapElements(id1, id2){
        console.log(JSON.stringify(data,4));
        traverseChild(getDataAsFamily(), function(child1, parent1, child1Name){
            if (child1.id == id1){
                traverseChild(getDataAsFamily(), function(child2, parent2, child2Name){
                    if (child2.id == id2){
                        console.log("Found");
                        parent2[child2Name] = child1;
                        parent1[child1Name] = child2;
                        return true;
                    }
                });
                return true;
            }
        });
        scene.loadScene(data);
        console.log(JSON.stringify(data,4));
    }

    function traverseChild(element, callback){
        if (element.type == "family"){
            if (element.gators.some(function(gator, index){
                if (callback(gator, element.gators, index)){
                    return true;
                }
            })) return true;
            if (element.foodChain.some(function(child, index){
                if (callback(child, element.foodChain, index)){
                    return true;
                }
                if (traverseChild(child, callback)){
                    return true;
                }
            })) return true;
        }
    }

    function loadTestCase(foodchain, testcase){
        var results = interpreter.test(foodchain, testcase);
        return results;
    }

    function loadAnimation(results, index, step){
        renderer.clear("#fff");
        scene.initialize();
        if (step){
            scene.loadScene(results[index].steps[step].state);
        }
        else{
            scene.loadScene(results[index].steps[0].state);
        }
    }
    function startAnimation(results, index){
        var i = 0;
        console.log(results[index].steps);
        var repeat = false;
        var interval = setInterval(function(){
            i++;
            console.log(i);
            if (i == results[index].steps.length){
                clearInterval(interval);
                return;
            }
            console.log(results[index].steps[i].events);
            var eaten_obj_id;
            var eating_obj_id;

            function eats(events){
                console.log("events", events);
                for (var a = 0; a < events.length; a++){
                    console.log("events[a].action", events[a]);
                    if (events[a] && events[a].action && events[a].action == "eat"){
                        eaten_obj_id = events[a].food;
                        console.log("a", a);
                        console.log("eaten_obj_id", eaten_obj_id);
                        eating_obj_id = events[a].gator;
                        return true;
                    }
                }
            }
            if (!repeat && eats(results[index].steps[i].events)){
                console.log("Now", results[index].steps[i].events);
                console.log("Prev", results[index].steps[i-1].events);

                console.log("eats");
                repeat = true;
                renderer.clear("#fff");
                scene.initialize();
                var intermidiate = JSON.parse(JSON.stringify(results[index].steps[i].state));
                intermidiate.splice(1, 0, {
                      "id": eaten_obj_id,
                      "type": "family",
                      "gators":[],
                      "foodChain":[]
                });
                if (intermidiate[0].gators.length > 0){
                    intermidiate[0].gators.splice(0,0, {"id": eaten_obj_id, "colorId":0, "type":"blank"});
                }

                scene.loadScene(intermidiate);
                i = i-1;
            }
            else{
                console.log("no eats");
                repeat = false;
                loadAnimation(results, index, i);

            }
            console.log(results[index].steps[i].state);
        }, 1500);
    }

    return {
        "initialize": initialize,
        "swapElements": swapElements,
        "startGame": startGame,
        "loadAnimation": loadAnimation

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


<commit_before>import should from 'should/as-function';
import Syncano from '../../src/syncano';
import {ValidationError} from '../../src/errors';
import {suffix, credentials, createCleaner} from './utils';

describe.only('ScriptTrace', function() {
  this.timeout(15000);

  const cleaner = createCleaner();
  let connection = null;
  let Model = null;
  let Instance = null;

  const instanceName = suffix.get('ScriptTrace');
  const data = {
    instanceName,
  };

  before(function() {
    connection = Syncano(credentials.getCredentials());
    Instance = connection.Instance;
    Model = connection.ScriptTrace;

    return Instance.please().create({name: instanceName}).then(() => {
      return connection.Script.please().create({
        instanceName: instanceName,
        label: instanceName,
        runtime_name: 'python_library_v4.2',
        source: 'print "x"'
      });
    }).then((script) => {
      data.scriptId = script.id;
      return script.run();
    }).then((trace) => {
      console.log('trace', trace);
      data.id = trace.id;
    });
  });

  after(function() {
    return Instance.please().delete({name: instanceName});
  });

  afterEach(function() {
    return cleaner.clean();
  });

  it('should be validated', function() {
    should(Model().save()).be.rejectedWith(ValidationError);
  });

  it('should require "instanceName"', function() {
    should(Model().save()).be.rejectedWith(/instanceName/);
  });

  it('should require "scriptId"', function() {
    should(Model({instanceName}).save()).be.rejectedWith(/scriptId/);
  });

  describe('#please()', function() {

    it('should be able to list objects', function() {
      return Model.please().list(data).then((objects) => {
        should(objects).be.an.Array();
      });
    });

    it('should be able to get an object', function() {
      return Model.please().get(data).then((object) => {
        should(object).be.a.Object();
        should(object).have.property('id').which.is.Number();
        should(object).have.property('status').which.is.String();
        should(object).have.property('instanceName').which.is.String().equal(instanceName);
        should(object).have.property('scriptId').which.is.Number().equal(data.scriptId);
        should(object).have.property('executed_at');
        should(object).have.property('duration');
        should(object).have.property('links').which.is.Object();
        should(object).have.property('result').which.is.Object();
      });
    });

    it('should be able to get first object (SUCCESS)', function() {
      return Model.please().first(data).then((object) => {
        should(object).be.an.Object();
      });
    });

    it('should be able to change page size', function() {
      return Model.please(data).pageSize(1).then((objects) => {
        should(objects).be.an.Array().with.length(1);
      });
    });

    it('should be able to get raw data', function() {
      return Model.please().list(data).raw().then((response) => {
        should(response).be.a.Object();
        should(response).have.property('objects').which.is.Array();
        should(response).have.property('next').which.is.null();
        should(response).have.property('prev').which.is.null();
      });
    });

  });

});
<commit_msg>[LIB-704] only<commit_after>import should from 'should/as-function';
import Syncano from '../../src/syncano';
import {ValidationError} from '../../src/errors';
import {suffix, credentials, createCleaner} from './utils';

describe('ScriptTrace', function() {
  this.timeout(15000);

  const cleaner = createCleaner();
  let connection = null;
  let Model = null;
  let Instance = null;

  const instanceName = suffix.get('ScriptTrace');
  const data = {
    instanceName,
  };

  before(function() {
    connection = Syncano(credentials.getCredentials());
    Instance = connection.Instance;
    Model = connection.ScriptTrace;

    return Instance.please().create({name: instanceName}).then(() => {
      return connection.Script.please().create({
        instanceName: instanceName,
        label: instanceName,
        runtime_name: 'python_library_v4.2',
        source: 'print "x"'
      });
    }).then((script) => {
      data.scriptId = script.id;
      return script.run();
    }).then((trace) => {
      console.log('trace', trace);
      data.id = trace.id;
    });
  });

  after(function() {
    return Instance.please().delete({name: instanceName});
  });

  afterEach(function() {
    return cleaner.clean();
  });

  it('should be validated', function() {
    should(Model().save()).be.rejectedWith(ValidationError);
  });

  it('should require "instanceName"', function() {
    should(Model().save()).be.rejectedWith(/instanceName/);
  });

  it('should require "scriptId"', function() {
    should(Model({instanceName}).save()).be.rejectedWith(/scriptId/);
  });

  describe('#please()', function() {

    it('should be able to list objects', function() {
      return Model.please().list(data).then((objects) => {
        should(objects).be.an.Array();
      });
    });

    it('should be able to get an object', function() {
      return Model.please().get(data).then((object) => {
        should(object).be.a.Object();
        should(object).have.property('id').which.is.Number();
        should(object).have.property('status').which.is.String();
        should(object).have.property('instanceName').which.is.String().equal(instanceName);
        should(object).have.property('scriptId').which.is.Number().equal(data.scriptId);
        should(object).have.property('executed_at');
        should(object).have.property('duration');
        should(object).have.property('links').which.is.Object();
        should(object).have.property('result').which.is.Object();
      });
    });

    it('should be able to get first object (SUCCESS)', function() {
      return Model.please().first(data).then((object) => {
        should(object).be.an.Object();
      });
    });

    it('should be able to change page size', function() {
      return Model.please(data).pageSize(1).then((objects) => {
        should(objects).be.an.Array().with.length(1);
      });
    });

    it('should be able to get raw data', function() {
      return Model.please().list(data).raw().then((response) => {
        should(response).be.a.Object();
        should(response).have.property('objects').which.is.Array();
        should(response).have.property('next').which.is.null();
        should(response).have.property('prev').which.is.null();
      });
    });

  });

});
<|endoftext|>


<commit_before>//console.log("running preTest");
mocha.setup("bdd");
<commit_msg>improve comments<commit_after>//makes the bdd interface available (describe, it before, after, etc
mocha.setup("bdd");
<|endoftext|>


<commit_before>var _ = require('lodash')
var moment = require('moment')
var excelWriter = require('./excel-writer')
var fs = require('fs')
var enums = require('../shared/enums')

var dateFormat = 'DD.MM.YYYY'

exports.createProviderRegistration = function createProviderRegistration(accountRows) {
  return createBilling(accountRows, _.curry(providerBillingHeader)(null), providerRowDescription, _.curry(providerBillingFooter)(accountRows))
}

exports.createYearlyProviderRegistration = function createYearlyProviderRegistration(year, accountRows) {
  return createBilling(accountRows, _.curry(providerBillingHeader)(moment().year()), providerRowDescription, _.curry(providerBillingFooter)(accountRows))
}

function providerBillingHeader(year, invoice) {
  return 'Valvontamaksu, vuosi ' + (year ? year : invoice.registrationDate.getFullYear()) + '. ' + invoice.invoiceText
}

function providerRowDescription(invoice) {
  return [moment(invoice.registrationDate).format(dateFormat), enums.providingType[invoice.providingType], invoice.name].join(', ')
}

function providerBillingFooter(accountRows, invoice) {
  var account = findAccountHavingInvoice(accountRows, invoice)
  return 'Lasku yhteensä ' + price(account.rows) + ' EUR'
}

exports.createClassificationRegistration = function createClassificationRegistration(dateRange, accountRows) {
  function billingHeader(invoice) {
    var period = dateRange.begin + ' - ' + dateRange.end
    return 'KOONTILASKUTUS ' + period + '. ' + invoice.invoiceText
  }
  function rowDescription(invoice) {
    return [
      invoice.programSequenceId,
      moment(invoice.registrationDate).format(dateFormat),
      invoice.name
    ].join(' ')
  }
  function billingFooter(invoice) {
    function summaryText(type, rows) {
      var typeString = { classification: 'luokiteltu', reclassification: 'uudelleenluokiteltu', registration: 'rekisteröity' }
      var plural = rows.length > 1 ? ' kuvaohjelmaa ' : ' kuvaohjelma '
      return rows.length + plural + typeString[type] + ', yhteensä ' + price(rows) + ' EUR.'
    }
    var account = findAccountHavingInvoice(accountRows, invoice)
    var rowsPerType = _.groupBy(account.rows, 'type')
    return _.pairs(rowsPerType).map(function (item) {
      return summaryText(item[0], item[1])
    }).join(' ')
  }
  return createBilling(accountRows, billingHeader, rowDescription, billingFooter)
}

function findAccountHavingInvoice(accountRows, invoice) {
  return _.find(accountRows, function (account) { return _.indexOf(account.rows, invoice) != -1 })
}

function price(arr) {
  return _(arr).pluck('price').reduce(function (a, b) {
    return a + b
  }, 0) / 100
}

function createBilling(accounts, billingDescription, rowDescription, billingFooter) {
  var billingData = _.flatten(_.map(accounts, function (ac) {
    var account = ac.account
    var rows = ac.rows
    var first = _.first(rows)
    var invoiceText = account.billing ? (account.billing.invoiceText || '').replace(/\n/g, ' ') : ''
    return _.map(rows, function (row) {
      return _.extend(row, { first: row === first ? 1 : 0, accountName: account.name, accountContactName: account.contactName, invoiceText: invoiceText, euroPrice: row.price / 100 })
    })
  }))
  var columns = [
    { name: 'T', value: invoiceValue('first'), repeatable: true, width: 2 },
    { name: 'Tilauslaji', value: constantValue('Z001') },
    { name: 'Myyntiorg', value: constantValue('6008') },
    { name: 'Jakelutie', value: constantValue('02') },
    { name: 'Sektori', value: constantValue('01') },
    { name: 'Myyntitsto', value: constantValue('') },
    { name: 'Viitelasku', value: constantValue('') },
    { name: 'Asiakasnro', value: constantValue('??') },
    { name: 'Nimi', value: invoiceValue('accountName'), width: 30 },
    { name: 'Lähiosoite', value: constantValue('') },
    { name: 'PostiNo', value: constantValue('') },
    { name: 'Toimipaikka', value: constantValue('') },
    { name: 'Laskutusasiakasnro', value: constantValue('') },
    { name: 'Laskutusasiakas nimi', value: constantValue('') },
    { name: 'Maksaja-asiakasnro', value: constantValue('') },
    { name: 'Maksaja-asiakas nimi', value: constantValue('') },
    { name: 'Toimitusasiakasnro', value: constantValue('') },
    { name: 'Palvelun luontipvm', value: constantValue('') },
    { name: 'Laskuttaja', value: constantValue('??') },
    { name: 'Asiaviite', value: invoiceValue('accountContactName'), width: 15 },
    { name: 'Tilausnumero', value: constantValue('') },
    { name: 'Otsikkoteksti: Otsikkomuistio 1 (tekstilaji 0002) tulostuu ennen rivejä. Huom. Kirjoita teksti katkeamattomasti, ei rivivaihtoja eikä alt+enter painikkeita', value: billingDescription, width: 20 },
    { name: 'Otsikkoteksti: Maksuperusteteksti (tekstilaji Z000) tulostuu laskun loppuun. Huom. Kirjoita teksti katkeamattomasti, ei rivivaihtoja eikä alt+enter painikkeita', value: billingFooter, width: 20 },
    { name: 'Nimike', value: constantValue('??'), repeatable: true },
    { name: 'Määrä', value: constantValue(1), repeatable: true },
    { name: 'Määräyksikkö', value: constantValue('kpl'), repeatable: true },
    { name: 'Yksikköhinta', value: invoiceValue('euroPrice'), repeatable: true },
    { name: 'Brutto/Netto', value: constantValue('N'), repeatable: true },
    { name: 'Tulosyksikkö', value: constantValue('') },
    { name: 'Nimikkeen nimitysteksti (laskutusaihe)', value: constantValue(''), width: 20 },
    { name: 'Riviteksti: Rivimuistio (tekstilaji 0002), tulostuu laskulle rivin jälkeen', value: rowDescription, width:30, repeatable: true },
    { name: 'PRR-osa', value: constantValue('') },
    { name: 'TaKP-tili', value: constantValue('') },
    { name: 'Suorite', value: constantValue('') },
    { name: 'Toiminto', value: constantValue('') },
    { name: 'Alue /  kunta', value: constantValue('') },
    { name: 'Seurantakohde 1', value: constantValue('') },
    { name: 'Seurantakohde 2', value: constantValue('') },
    { name: 'Laskuliitteet', value: constantValue('') }
  ]

  function invoiceValue(key) {
    function valueFromInvoice(key, invoice) {
      return invoice[key]
    }
    return _.curry(valueFromInvoice)(key)
  }

  function constantValue(c) {
    function constant(c, invoice) {
      return c
    }
    return _.curry(constant)(c)
  }

  var xlsData = [ _.map(columns, function (column) { return column.name }) ]
  _.each(billingData, function (invoice) {
    xlsData.push(_.map(columns, function (column) {
      return invoice.first || column.repeatable ? column.value(invoice) : ''
    }))
  })
  var tmpXlsxFile = 'kieku_' + Math.random() + '.xlsx'
  var columnWidths = _.map(columns, function (column) { return { wch: (column.width || 10) }})
  excelWriter.write(tmpXlsxFile, xlsData, columnWidths)
  var fileData = fs.readFileSync(tmpXlsxFile)
  fs.unlinkSync(tmpXlsxFile)
  return fileData
}
<commit_msg>Add address (only if customer number is not known)<commit_after>var _ = require('lodash')
var moment = require('moment')
var excelWriter = require('./excel-writer')
var fs = require('fs')
var enums = require('../shared/enums')

var dateFormat = 'DD.MM.YYYY'

exports.createProviderRegistration = function createProviderRegistration(accountRows) {
  return createBilling(accountRows, _.curry(providerBillingHeader)(null), providerRowDescription, _.curry(providerBillingFooter)(accountRows))
}

exports.createYearlyProviderRegistration = function createYearlyProviderRegistration(year, accountRows) {
  return createBilling(accountRows, _.curry(providerBillingHeader)(moment().year()), providerRowDescription, _.curry(providerBillingFooter)(accountRows))
}

function providerBillingHeader(year, invoice) {
  return 'Valvontamaksu, vuosi ' + (year ? year : invoice.registrationDate.getFullYear()) + '. ' + invoice.invoiceText
}

function providerRowDescription(invoice) {
  return [moment(invoice.registrationDate).format(dateFormat), enums.providingType[invoice.providingType], invoice.name].join(', ')
}

function providerBillingFooter(accountRows, invoice) {
  var account = findAccountHavingInvoice(accountRows, invoice)
  return 'Lasku yhteensä ' + price(account.rows) + ' EUR'
}

exports.createClassificationRegistration = function createClassificationRegistration(dateRange, accountRows) {
  function billingHeader(invoice) {
    var period = dateRange.begin + ' - ' + dateRange.end
    return 'KOONTILASKUTUS ' + period + '. ' + invoice.invoiceText
  }
  function rowDescription(invoice) {
    return [
      invoice.programSequenceId,
      moment(invoice.registrationDate).format(dateFormat),
      invoice.name
    ].join(' ')
  }
  function billingFooter(invoice) {
    function summaryText(type, rows) {
      var typeString = { classification: 'luokiteltu', reclassification: 'uudelleenluokiteltu', registration: 'rekisteröity' }
      var plural = rows.length > 1 ? ' kuvaohjelmaa ' : ' kuvaohjelma '
      return rows.length + plural + typeString[type] + ', yhteensä ' + price(rows) + ' EUR.'
    }
    var account = findAccountHavingInvoice(accountRows, invoice)
    var rowsPerType = _.groupBy(account.rows, 'type')
    return _.pairs(rowsPerType).map(function (item) {
      return summaryText(item[0], item[1])
    }).join(' ')
  }
  return createBilling(accountRows, billingHeader, rowDescription, billingFooter)
}

function findAccountHavingInvoice(accountRows, invoice) {
  return _.find(accountRows, function (account) { return _.indexOf(account.rows, invoice) != -1 })
}

function price(arr) {
  return _(arr).pluck('price').reduce(function (a, b) {
    return a + b
  }, 0) / 100
}

function createBilling(accounts, billingDescription, rowDescription, billingFooter) {
  var billingData = _.flatten(_.map(accounts, function (ac) {
    var account = ac.account
    var rows = ac.rows
    var first = _.first(rows)
    var invoiceText = account.billing ? (account.billing.invoiceText || '').replace(/\n/g, ' ') : ''
    var accountAddress = customerNumber ? {} : account.billingPreference == 'address' ? account.billing.address : account.address
    return _.map(rows, function (row) {
      return _.extend(row, {
        first: row === first ? 1 : 0,
        accountName: account.name,
        accountContactName: account.contactName,
        accountStreetAddress: accountAddress.street,
        accountZipCode: accountAddress.zip,
        accountCity: accountAddress.city,
        invoiceText: invoiceText,
        euroPrice: row.price / 100
      })
    })
  }))
  var columns = [
    { name: 'T', value: invoiceValue('first'), repeatable: true, width: 2 },
    { name: 'Tilauslaji', value: constantValue('Z001') },
    { name: 'Myyntiorg', value: constantValue('6008') },
    { name: 'Jakelutie', value: constantValue('02') },
    { name: 'Sektori', value: constantValue('01') },
    { name: 'Myyntitsto', value: constantValue('') },
    { name: 'Viitelasku', value: constantValue('') },
    { name: 'Asiakasnro', value: constantValue('??') },
    { name: 'Nimi', value: invoiceValue('accountName'), width: 30 },
    { name: 'Lähiosoite', value: invoiceValue('accountStreetAddress') },
    { name: 'PostiNo', value: invoiceValue('accountZipCode') },
    { name: 'Toimipaikka', value: invoiceValue('accountCity') },
    { name: 'Laskutusasiakasnro', value: constantValue('') },
    { name: 'Laskutusasiakas nimi', value: constantValue('') },
    { name: 'Maksaja-asiakasnro', value: constantValue('') },
    { name: 'Maksaja-asiakas nimi', value: constantValue('') },
    { name: 'Toimitusasiakasnro', value: constantValue('') },
    { name: 'Palvelun luontipvm', value: constantValue('') },
    { name: 'Laskuttaja', value: constantValue('??') },
    { name: 'Asiaviite', value: invoiceValue('accountContactName'), width: 15 },
    { name: 'Tilausnumero', value: constantValue('') },
    { name: 'Otsikkoteksti: Otsikkomuistio 1 (tekstilaji 0002) tulostuu ennen rivejä. Huom. Kirjoita teksti katkeamattomasti, ei rivivaihtoja eikä alt+enter painikkeita', value: billingDescription, width: 20 },
    { name: 'Otsikkoteksti: Maksuperusteteksti (tekstilaji Z000) tulostuu laskun loppuun. Huom. Kirjoita teksti katkeamattomasti, ei rivivaihtoja eikä alt+enter painikkeita', value: billingFooter, width: 20 },
    { name: 'Nimike', value: constantValue('??'), repeatable: true },
    { name: 'Määrä', value: constantValue(1), repeatable: true },
    { name: 'Määräyksikkö', value: constantValue('kpl'), repeatable: true },
    { name: 'Yksikköhinta', value: invoiceValue('euroPrice'), repeatable: true },
    { name: 'Brutto/Netto', value: constantValue('N'), repeatable: true },
    { name: 'Tulosyksikkö', value: constantValue('') },
    { name: 'Nimikkeen nimitysteksti (laskutusaihe)', value: constantValue(''), width: 20 },
    { name: 'Riviteksti: Rivimuistio (tekstilaji 0002), tulostuu laskulle rivin jälkeen', value: rowDescription, width:30, repeatable: true },
    { name: 'PRR-osa', value: constantValue('') },
    { name: 'TaKP-tili', value: constantValue('') },
    { name: 'Suorite', value: constantValue('') },
    { name: 'Toiminto', value: constantValue('') },
    { name: 'Alue /  kunta', value: constantValue('') },
    { name: 'Seurantakohde 1', value: constantValue('') },
    { name: 'Seurantakohde 2', value: constantValue('') },
    { name: 'Laskuliitteet', value: constantValue('') }
  ]

  function invoiceValue(key) {
    function valueFromInvoice(key, invoice) {
      return invoice[key]
    }
    return _.curry(valueFromInvoice)(key)
  }

  function constantValue(c) {
    function constant(c, invoice) {
      return c
    }
    return _.curry(constant)(c)
  }

  var xlsData = [ _.map(columns, function (column) { return column.name }) ]
  _.each(billingData, function (invoice) {
    xlsData.push(_.map(columns, function (column) {
      return invoice.first || column.repeatable ? column.value(invoice) : ''
    }))
  })
  var tmpXlsxFile = 'kieku_' + Math.random() + '.xlsx'
  var columnWidths = _.map(columns, function (column) { return { wch: (column.width || 10) }})
  excelWriter.write(tmpXlsxFile, xlsData, columnWidths)
  var fileData = fs.readFileSync(tmpXlsxFile)
  fs.unlinkSync(tmpXlsxFile)
  return fileData
}
<|endoftext|>


<commit_before>"use strict";

if (Settings.get("vimSync") === true) {
  Settings.Sync = {
    storage: chrome.storage.sync,
    doNotSync: ["settingsVersion", "previousVersion"],
    fetchAsync: function() {
      var _this = this;
      this.storage.get(null, function(items) {
        var key, value, hasOwn;
        if (chrome.runtime.lastError) {
          return chrome.runtime.lastError;
        }
        hasOwn = Object.prototype.hasOwnProperty;
        for (key in items) {
          if (!hasOwn.call(items, key)) continue;
          value = items[key];
          _this.storeAndPropagate(key, value);
        }
      });
    },
    handleStorageUpdate: function(changes, area) {
      var change, key;
      for (key in changes) {
        if (!Object.prototype.hasOwnProperty.call(changes, key)) continue;
        change = changes[key];
        this.storeAndPropagate(key, change != null ? change.newValue : undefined);
      }
    },
    storeAndPropagate: function(key, value) {
      var defaultValue, defaultValueJSON;
      if (!(key in Settings.defaults)) {
        return;
      }
      if (!this.shouldSyncKey(key)) {
        return;
      }
      if (value && key in localStorage && localStorage[key] === value) {
        return;
      }
      defaultValue = Settings.defaults[key];
      defaultValueJSON = JSON.stringify(defaultValue);
      if (value && value !== defaultValueJSON) {
        value = JSON.parse(value);
      } else {
        value = defaultValue;
      }
      Settings.set(key, value);
    },
    set: function(key, value) {
      if (this.shouldSyncKey(key)) {
        var settings = {};
        settings[key] = value;
        this.storage.set(settings);
      }
    },
    clear: function(key) {
      if (this.shouldSyncKey(key)) {
        this.storage.remove(key);
      }
    },
    shouldSyncKey: function(key) {
      return this.doNotSync.index(key) < 0;
    }
  };
  chrome.storage.onChanged.addListener(Settings.Sync.handleStorageUpdate.bind(Settings.Sync));
  Settings.Sync.fetchAsync();
  Settings.Sync.__proto__ = null;
}

if (chrome.browserAction && chrome.browserAction.setIcon) (function() {
  g_requestHandlers.setIcon = function(tabId, type, pass) {
    chrome.browserAction.setIcon({
      tabId: tabId,
      path: Settings.icons[type ||
        (pass === null ? "enabled" : pass ? "partial" : "disabled")]
    });
  };
  Settings.updateHooks.showActionIcon = (function (value) {
    this(value);
    if (value) {
      chrome.browserAction.setTitle({
        title: "Vimium++"
      });
      chrome.browserAction.enable();
    } else {
      chrome.browserAction.disable();
      chrome.browserAction.setTitle({
        title: "Vimium++\nThis icon is not in use"
      });
    }
  }).bind(Settings.updateHooks.showActionIcon);
  Settings.postUpdate("showActionIcon");
})();

// According to tests: onInstalled will be executed after 0 ~ 16 ms if needed
chrome.runtime.onInstalled.addListener(window.b = function(details) {
  var contentScripts, func, js, css, reason = details.reason;
  if (reason === "install") { reason = ""; }
  else if (reason === "update") { reason = details.previousVersion; }
  else { return; }
  if (Settings.CONST.Timer > 0) {
    clearTimeout(Settings.CONST.Timer);
    Settings.CONST.Timer = 0;
  }

  contentScripts = chrome.runtime.getManifest().content_scripts[0];
  if (contentScripts.all_frames) {
    func = function(url) { return {file: url, allFrames: true}; };
  } else {
    func = function(url) { return {file: url, allFrames: false}; };
  }
  css = contentScripts.css.map(func);
  js = contentScripts.js.map(func);
  contentScripts = null;
  chrome.tabs.query({
    status: "complete"
  }, function(tabs) {
    var _i = tabs.length, tabId, _j, _len, callback, url, t = chrome.tabs;
    callback = function() { return chrome.runtime.lastError; };
    for (; 0 <= --_i; ) {
      url = tabs[_i].url;
      if (url.startsWith("chrome") || url.indexOf("://") === -1) continue;
      tabId = tabs[_i].id;
      for (_j = 0, _len = css.length; _j < _len; ++_j)
        t.insertCSS(tabId, css[_j], callback);
      for (_j = 0, _len = js.length; _j < _len; ++_j)
        t.executeScript(tabId, js[_j], callback);
    }
    console.log("%cVimium++ %chas %cinstalled", "color:blue", "color:auto", "color:red", details);
  });

  if (!reason && chrome.notifications && chrome.notifications.create) { return; }

  func = function(versionA, versionB) {
    var a, b, i, _i, _ref;
    versionA = versionA.split('.');
    versionB = versionB.split('.');
    for (i = 0, _ref = Math.max(versionA.length, versionB.length); i < _ref; ++i) {
      a = parseInt(versionA[i] || 0, 10);
      b = parseInt(versionB[i] || 0, 10);
      if (a < b) {
        return -1;
      } else if (a > b) {
        return 1;
      }
    }
    return 0;
  };
  if (func(Settings.CONST.CurrentVersion, reason) <= 0) { return; }

  reason = "vimium++_upgradeNotification";
  chrome.notifications.create(reason, {
    type: "basic",
    iconUrl: chrome.runtime.getURL("icons/icon128.png"),
    title: "Vimium++ Upgrade",
    message: "Vimium++ has been upgraded to version " + Settings.CONST.CurrentVersion
      + ". Click here for more information.",
    isClickable: true
  }, function() {
    if (chrome.runtime.lastError) {
      return chrome.runtime.lastError;
    }
    chrome.notifications.onClicked.addListener(function(id) {
      if (id !== reason) { return; }
      g_requestHandlers.focusOrLaunch({
        url: "https://github.com/gdh1995/vimium-plus#release-notes"
      });
      chrome.notifications.clear(reason, function() {
        return chrome.runtime.lastError;
      });
    });
  });
});
setTimeout(function() {
  chrome.runtime.onInstalled.removeListener(window.b);
  window.b = null;
}, 50);

var a, b, c, cb, log; // #if DEBUG
cb = function(b) { a = b; console.log(b); };
setTimeout(function() {
  a = c = null, b = cb, log = console.log.bind(console);
}, 100); // #endif
<commit_msg>a better notification<commit_after>"use strict";

if (Settings.get("vimSync") === true) {
  Settings.Sync = {
    storage: chrome.storage.sync,
    doNotSync: ["settingsVersion", "previousVersion"],
    fetchAsync: function() {
      var _this = this;
      this.storage.get(null, function(items) {
        var key, value, hasOwn;
        if (chrome.runtime.lastError) {
          return chrome.runtime.lastError;
        }
        hasOwn = Object.prototype.hasOwnProperty;
        for (key in items) {
          if (!hasOwn.call(items, key)) continue;
          value = items[key];
          _this.storeAndPropagate(key, value);
        }
      });
    },
    handleStorageUpdate: function(changes, area) {
      var change, key;
      for (key in changes) {
        if (!Object.prototype.hasOwnProperty.call(changes, key)) continue;
        change = changes[key];
        this.storeAndPropagate(key, change != null ? change.newValue : undefined);
      }
    },
    storeAndPropagate: function(key, value) {
      var defaultValue, defaultValueJSON;
      if (!(key in Settings.defaults)) {
        return;
      }
      if (!this.shouldSyncKey(key)) {
        return;
      }
      if (value && key in localStorage && localStorage[key] === value) {
        return;
      }
      defaultValue = Settings.defaults[key];
      defaultValueJSON = JSON.stringify(defaultValue);
      if (value && value !== defaultValueJSON) {
        value = JSON.parse(value);
      } else {
        value = defaultValue;
      }
      Settings.set(key, value);
    },
    set: function(key, value) {
      if (this.shouldSyncKey(key)) {
        var settings = {};
        settings[key] = value;
        this.storage.set(settings);
      }
    },
    clear: function(key) {
      if (this.shouldSyncKey(key)) {
        this.storage.remove(key);
      }
    },
    shouldSyncKey: function(key) {
      return this.doNotSync.index(key) < 0;
    }
  };
  chrome.storage.onChanged.addListener(Settings.Sync.handleStorageUpdate.bind(Settings.Sync));
  Settings.Sync.fetchAsync();
  Settings.Sync.__proto__ = null;
}

if (chrome.browserAction && chrome.browserAction.setIcon) (function() {
  g_requestHandlers.setIcon = function(tabId, type, pass) {
    chrome.browserAction.setIcon({
      tabId: tabId,
      path: Settings.icons[type ||
        (pass === null ? "enabled" : pass ? "partial" : "disabled")]
    });
  };
  Settings.updateHooks.showActionIcon = (function (value) {
    this(value);
    if (value) {
      chrome.browserAction.setTitle({
        title: "Vimium++"
      });
      chrome.browserAction.enable();
    } else {
      chrome.browserAction.disable();
      chrome.browserAction.setTitle({
        title: "Vimium++\nThis icon is not in use"
      });
    }
  }).bind(Settings.updateHooks.showActionIcon);
  Settings.postUpdate("showActionIcon");
})();

// According to tests: onInstalled will be executed after 0 ~ 16 ms if needed
chrome.runtime.onInstalled.addListener(window.b = function(details) {
  var contentScripts, func, js, css, reason = details.reason;
  if (reason === "install") { reason = ""; }
  else if (reason === "update") { reason = details.previousVersion; }
  else { return; }
  if (Settings.CONST.Timer > 0) {
    clearTimeout(Settings.CONST.Timer);
    Settings.CONST.Timer = 0;
  }

  contentScripts = chrome.runtime.getManifest().content_scripts[0];
  if (contentScripts.all_frames) {
    func = function(url) { return {file: url, allFrames: true}; };
  } else {
    func = function(url) { return {file: url, allFrames: false}; };
  }
  css = contentScripts.css.map(func);
  js = contentScripts.js.map(func);
  contentScripts = null;
  chrome.tabs.query({
    status: "complete"
  }, function(tabs) {
    var _i = tabs.length, tabId, _j, _len, callback, url, t = chrome.tabs;
    callback = function() { return chrome.runtime.lastError; };
    for (; 0 <= --_i; ) {
      url = tabs[_i].url;
      if (url.startsWith("chrome") || url.indexOf("://") === -1) continue;
      tabId = tabs[_i].id;
      for (_j = 0, _len = css.length; _j < _len; ++_j)
        t.insertCSS(tabId, css[_j], callback);
      for (_j = 0, _len = js.length; _j < _len; ++_j)
        t.executeScript(tabId, js[_j], callback);
    }
    console.log("%cVimium++ %chas %cinstalled", "color:blue", "color:auto", "color:red", details);
  });

  if (!reason && chrome.notifications && chrome.notifications.create) { return; }

  func = function(versionA, versionB) {
    var a, b, i, _i, _ref;
    versionA = versionA.split('.');
    versionB = versionB.split('.');
    for (i = 0, _ref = Math.max(versionA.length, versionB.length); i < _ref; ++i) {
      a = parseInt(versionA[i] || 0, 10);
      b = parseInt(versionB[i] || 0, 10);
      if (a < b) {
        return -1;
      } else if (a > b) {
        return 1;
      }
    }
    return 0;
  };
  if (func(Settings.CONST.CurrentVersion, reason) <= 0) { return; }

  reason = "vimium++_upgradeNotification";
  chrome.notifications.create(reason, {
    type: "basic",
    iconUrl: chrome.runtime.getURL("icons/icon128.png"),
    title: "Vimium++ Upgrade",
    message: "Vimium++ has been upgraded to version " + Settings.CONST.CurrentVersion
      + ". Click here for more information.",
    isClickable: true
  }, function(notificationId) {
    if (chrome.runtime.lastError) {
      return chrome.runtime.lastError;
    }
    reason = notificationId || reason;
    chrome.notifications.onClicked.addListener(function(id) {
      if (id !== reason) { return; }
      g_requestHandlers.focusOrLaunch({
        url: "https://github.com/gdh1995/vimium-plus#release-notes"
      });
    });
  });
});
setTimeout(function() {
  chrome.runtime.onInstalled.removeListener(window.b);
  window.b = null;
}, 50);

var a, b, c, cb, log; // #if DEBUG
cb = function(b) { a = b; console.log(b); };
setTimeout(function() {
  a = c = null, b = cb, log = console.log.bind(console);
}, 100); // #endif
<|endoftext|>


<commit_before>// https://github.com/gulpjs/gulp/tree/master/docs
// https://github.com/gulpjs/gulp/blob/master/docs/API.md
var gulp = require('gulp');

// https://www.npmjs.com/package/gulp-webpack/
var webpack = require("gulp-webpack");
// https://github.com/webpack/webpack-with-common-libs/blob/master/webpack.config.js
var webpackConfig = require('./webpack.config.js');

// https://www.npmjs.com/package/gulp-watch/
var watch = require('gulp-watch');

// https://github.com/terinjokes/gulp-uglify
var uglify = require('gulp-uglify');

// https://github.com/gulpjs/gulp/blob/master/docs/recipes/server-with-livereload-and-css-injection.md
var browserSync = require('browser-sync');
var reload = browserSync.reload;

// https://github.com/stevelacy/gulp-stylus
var stylus = require('gulp-stylus');

// https://github.com/floridoo/gulp-sourcemaps
var sourcemaps = require('gulp-sourcemaps');

// http://nibstyl.us/docs/
// https://github.com/tj/nib/blob/master/lib/nib/reset.styl
var nib = require('nib');

// 团队不同成员可以添加各自的任务，此处供项目初始化demo使用。
gulp.task('js', function() {
    gulp.src('src/app.js')
        .pipe(webpack(webpackConfig))
        // .pipe(uglify())
        .pipe(gulp.dest('./dist'))
});

gulp.task('css', function() {
    gulp.src('./src/**/*.styl')
        .pipe(sourcemaps.init())
        .pipe(stylus())
        .pipe(sourcemaps.write('.'))
        .pipe(gulp.dest('./src'));
});

// watch files for changes and reload
gulp.task('watch', ['js', 'css'], function() {
    browserSync({
        server: {
            baseDir: './'
        }
    });

    gulp.watch([
        'src/**/*.js',
        'src/**/*.styl'
    ], ['js', 'css', reload]);
});<commit_msg>update<commit_after>// https://github.com/gulpjs/gulp/tree/master/docs
// https://github.com/gulpjs/gulp/blob/master/docs/API.md
var gulp = require('gulp');

// https://www.npmjs.com/package/gulp-webpack/
var webpack = require("gulp-webpack");
// https://github.com/webpack/webpack-with-common-libs/blob/master/webpack.config.js
var webpackConfig = require('./webpack.config.js');

// https://www.npmjs.com/package/gulp-watch/
var watch = require('gulp-watch');

// https://github.com/terinjokes/gulp-uglify
var uglify = require('gulp-uglify');

// https://github.com/gulpjs/gulp/blob/master/docs/recipes/server-with-livereload-and-css-injection.md
var browserSync = require('browser-sync');
var reload = browserSync.reload;

// https://github.com/stevelacy/gulp-stylus
var stylus = require('gulp-stylus');

// https://github.com/floridoo/gulp-sourcemaps
var sourcemaps = require('gulp-sourcemaps');

// http://nibstyl.us/docs/
// https://github.com/tj/nib/blob/master/lib/nib/reset.styl
var nib = require('nib');

// TODO https://www.npmjs.com/package/gulp-concat/
// TODO https://www.npmjs.com/package/gulp-css-globbing/
// TODO https://www.npmjs.com/package/gulp-cssimport/

// 团队不同成员可以添加各自的任务，此处供项目初始化demo使用。
gulp.task('js', function() {
    gulp.src('src/app.js')
        .pipe(webpack(webpackConfig))
        // .pipe(uglify())
        .pipe(gulp.dest('./dist'))
});

gulp.task('css', function() {
    gulp.src('./src/**/*.styl')
        .pipe(sourcemaps.init())
        .pipe(stylus())
        .pipe(sourcemaps.write('.'))
        .pipe(gulp.dest('./src'));
});

// watch files for changes and reload
gulp.task('watch', ['js', 'css'], function() {
    browserSync({
        server: {
            baseDir: './'
        }
    });

    gulp.watch([
        'src/**/*.js',
        'src/**/*.styl'
    ], ['js', 'css', reload]);
});<|endoftext|>


<commit_before>/**
 * Copyright 2018 The AMP HTML Authors. All Rights Reserved.
 *
 * 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.
 */

import {RequestBank} from '../../testing/test-helper';
import {maybeSwitchToCompiledJs} from '../../testing/iframe';
import {parseQueryString} from '../../src/url';
import {toggleExperiment} from '../../src/experiments';
import {xhrServiceForTesting} from '../../src/service/xhr-impl';

describe('AMPHTML ad on AMP Page', () => {
  describes.integration(
    'ATF',
    {
      amp: true,
      extensions: ['amp-ad'],
      body: `
  <amp-ad
      width="300" height="250"
      id="i-amphtml-demo-id"
      type="fake"
      a4a-conversion="true"
      checksig=""
      disable3pfallback="true"
      src="//ads.localhost:9876/amp4test/a4a/${RequestBank.getBrowserId()}">
  </amp-ad>
      `,
    },
    () => {
      it('should layout amp-img, amp-pixel, amp-analytics', () => {
        // Open http://ads.localhost:9876/amp4test/a4a/12345 to see ad content
        return testAmpComponents();
      });
    }
  );

  describes.integration(
    'BTF',
    {
      amp: true,
      extensions: ['amp-ad'],
      frameStyle: 'height: 100vh',
      body: `

  <amp-ad
      width="300" height="250"
      id="i-amphtml-demo-id"
      type="fake"
      a4a-conversion="true"
      checksig=""
      disable3pfallback="true"
      src="//ads.localhost:9876/amp4test/a4a/${RequestBank.getBrowserId()}">
  </amp-ad>
      `,
    },
    env => {
      it('should layout amp-img, amp-pixel, amp-analytics', () => {
        // Open http://ads.localhost:9876/amp4test/a4a/12345 to see ad content
        return testAmpComponentsBTF(env.win);
      });
    }
  );
});

describe('AMPHTML ad on non-AMP page (inabox)', () => {
  describes.integration(
    'ATF',
    {
      amp: false,
      body: `

      `,
    },
    env => {
      it('should layout amp-img, amp-pixel, amp-analytics', () => {
        // See amp4test.js for creative content
        return testAmpComponents();
      });

      afterEach(() => {
        unregisterIframe(env.win.document.getElementById('inabox'));
      });
    }
  );

  describes.integration(
    'BTF',
    {
      amp: false,
      frameStyle: 'height: 100vh',
      body: `

      `,
    },
    env => {
      it('should layout amp-img, amp-pixel, amp-analytics', () => {
        // See amp4test.js for creative content
        return testAmpComponentsBTF(env.win);
      });

      afterEach(() => {
        unregisterIframe(env.win.document.getElementById('inabox'));
      });
    }
  );

  describes.integration(
    'ATF within friendly frame or safe frame',
    {
      amp: false,
      body: `

      `,
    },
    env => {
      let adContent;
      let iframe;
      before(() => {
        // Gets the same ad as the other tests.
        return fetchAdContent().then(text => {
          adContent = text;
        });
      });

      beforeEach(() => {
        iframe = document.createElement('iframe');
        Array.prototype.push.apply(env.win.top.ampInaboxIframes, [iframe]);
      });

      afterEach(() => {
        unregisterIframe(iframe);
        env.win.document.body.removeChild(iframe);
      });

      it(
        'should layout amp-img, amp-pixel, ' +
          'amp-analytics within friendly frame',
        () => {
          writeFriendlyFrame(env.win.document, iframe, adContent);
          return testAmpComponents();
        }
      );

      it(
        'should layout amp-img, amp-pixel, ' +
          'amp-analytics within safe frame',
        () => {
          writeSafeFrame(env.win.document, iframe, adContent);
          return testAmpComponents();
        }
      );
    }
  );

  // TODO(zombifier): The BTF test fails on Safari (#21311).
  // TODO(powerivq): Flaky on Firefox and Edge too. unskip. (#24657)
  describes.integration(
    'BTF within friendly frame or safe frame',
    {
      amp: false,
      body: `

      `,
    },
    env => {
      let adContent;
      let iframe;
      before(() => {
        return fetchAdContent().then(text => {
          adContent = text;
        });
      });

      beforeEach(() => {
        env.iframe.style.height = '100vh';
        iframe = document.createElement('iframe');
        Array.prototype.push.apply(env.win.top.ampInaboxIframes, [iframe]);
      });

      afterEach(() => {
        unregisterIframe(iframe);
        env.win.document.body.removeChild(iframe);
      });

      it.skip(
        'should layout amp-img, amp-pixel, ' +
          'amp-analytics within friendly frame',
        () => {
          writeFriendlyFrame(env.win.document, iframe, adContent);
          return testAmpComponentsBTF(env.win);
        }
      );

      it.skip(
        'should layout amp-img, amp-pixel, ' +
          'amp-analytics within safe frame',
        () => {
          writeSafeFrame(env.win.document, iframe, adContent);
          return testAmpComponentsBTF(env.win);
        }
      );
    }
  );
});

describe('A more real AMPHTML image ad', () => {
  const {testServerPort} = window.ampTestRuntimeConfig;

  // The image ad as seen in examples/inabox.gpt.html,
  // with visibility pings being placeholders that's substituted with calls to
  // the request bank.
  const adBody = maybeSwitchToCompiledJs(
    // eslint-disable-next-line no-undef
    __html__['test/fixtures/amp-cupcake-ad.html']
  )
    .replace(/__TEST_SERVER_PORT__/g, testServerPort)
    .replace(/__VIEW_URL__/g, RequestBank.getUrl('view')) // get all instances
    .replace('__VISIBLE_URL__', RequestBank.getUrl('visible'))
    .replace('__ACTIVE_VIEW_URL__', RequestBank.getUrl('activeview'));

  function testVisibilityPings(visibleDelay, activeViewDelay) {
    let viewTime = 0;
    let visibleTime = 0;
    let activeViewTime = 0;
    const viewPromise = RequestBank.withdraw('view').then(
      () => (viewTime = Date.now())
    );
    const visiblePromise = RequestBank.withdraw('visible').then(
      () => (visibleTime = Date.now())
    );
    const activeViewPromise = RequestBank.withdraw('activeview').then(
      () => (activeViewTime = Date.now())
    );
    return Promise.all([viewPromise, visiblePromise, activeViewPromise]).then(
      () => {
        // Add a 200ms "buffer" to account for possible browser jankiness
        expect(visibleTime - viewTime).to.be.above(visibleDelay - 200);
        expect(activeViewTime - viewTime).to.be.above(activeViewDelay - 200);
      }
    );
  }

  describes.integration(
    'ATF within friendly frame or safe frame',
    {
      amp: false,
      body: `

        `,
    },
    env => {
      let iframe;
      let doc;
      beforeEach(() => {
        doc = env.win.document;
        iframe = document.createElement('iframe');
        // we add the iframe here because it's dynamically created, so the
        // bootstrap script would have missed it.
        Array.prototype.push.apply(env.win.top.ampInaboxIframes, [iframe]);
      });

      it('should properly render ad in a friendly iframe with viewability pings', () => {
        writeFriendlyFrame(doc, iframe, adBody);
        return testVisibilityPings(0, 1000);
      });

      it('should properly render ad in a safe frame with viewability pings', () => {
        writeSafeFrame(doc, iframe, adBody);
        return testVisibilityPings(0, 1000);
      });

      afterEach(() => {
        unregisterIframe(iframe);
        doc.body.removeChild(iframe);
      });
    }
  );

  // Testing that analytics components use IntersectionObserver properly.
  describes.integration(
    'No Host Script within friendly frame or safe frame',
    {
      amp: false,
      body: '',
    },
    env => {
      let iframe;
      let doc;
      beforeEach(() => {
        doc = env.win.document;
        iframe = document.createElement('iframe');
      });

      it('should properly render ad in a friendly iframe with viewability pings', () => {
        toggleExperiment(env.win, 'inabox-viewport-friendly', true);
        writeFriendlyFrame(doc, iframe, adBody);
        return testVisibilityPings(0, 1000);
      });

      it.configure()
        .ifChrome()
        .run(
          'should properly render ad in a safe frame with viewability pings',
          () => {
            writeSafeFrame(doc, iframe, adBody);
            return testVisibilityPings(0, 1000);
          }
        );

      afterEach(() => {
        doc.body.removeChild(iframe);
      });
    }
  );

  // TODO(zombifier): unskip the tests.
  describes.integration(
    'BTF within friendly frame or safe frame',
    {
      amp: false,
      body: `

        `,
    },
    env => {
      let iframe;
      let doc;
      beforeEach(() => {
        env.iframe.style.height = '100vh';
        doc = env.win.document;
        iframe = document.createElement('iframe');
        Array.prototype.push.apply(env.win.top.ampInaboxIframes, [iframe]);
        setTimeout(() => {
          env.win.scrollTo(0, 1000);
          window.top.scrollTo(window.top.scrollX, window.top.scrollY - 1);
        }, 2000);
      });

      it.skip('should properly render ad in a friendly iframe with viewability pings', () => {
        writeFriendlyFrame(doc, iframe, adBody);
        return testVisibilityPings(2000, 3000);
      });

      it.skip('should properly render ad in a safe frame with viewability pings', () => {
        writeSafeFrame(doc, iframe, adBody);
        return testVisibilityPings(2000, 3000);
      });

      afterEach(() => {
        unregisterIframe(iframe);
        doc.body.removeChild(iframe);
      });
    }
  );
});

function testAmpComponents() {
  const imgPromise = RequestBank.withdraw('image').then(req => {
    expect(req.url).to.equal('/');
  });
  const pixelPromise = RequestBank.withdraw('pixel').then(req => {
    expect(req.url).to.equal('/foo?cid=');
  });
  const analyticsPromise = RequestBank.withdraw('analytics').then(req => {
    expect(req.url).to.match(/^\/bar\?/);
    const queries = parseQueryString(req.url.substr('/bar'.length));
    expect(queries['cid']).to.equal('');
    expect(queries['sourceUrl']).be.ok;
    // Cookie is sent via http response header when requesting
    // localhost:9876/amp4test/a4a/
    // COOKIE macro is not allowed in inabox and resolves to empty
    expect(queries['cookie']).to.equal('');
  });
  return Promise.all([imgPromise, pixelPromise, analyticsPromise]);
}

function testAmpComponentsBTF(win) {
  // The iframe starts BTF. "visible" trigger should be after scroll.
  // We will record scrolling time for comparison.
  let scrollTime = Infinity;
  const imgPromise = RequestBank.withdraw('image').then(req => {
    expect(Date.now()).to.be.below(scrollTime);
    expect(req.url).to.equal('/');
  });
  const pixelPromise = RequestBank.withdraw('pixel').then(req => {
    expect(Date.now()).to.be.below(scrollTime);
    expect(req.url).to.equal('/foo?cid=');
  });
  const analyticsPromise = RequestBank.withdraw('analytics').then(req => {
    expect(req.url).to.match(/^\/bar\?/);
    const queries = parseQueryString(req.url.substr('/bar'.length));
    expect(queries['cid']).to.equal('');
    expect(Date.now()).to.be.above(scrollTime);
    expect(parseInt(queries['timestamp'], 10)).to.be.above(scrollTime);
  });
  setTimeout(() => {
    scrollTime = Date.now();
    win.scrollTo(0, 1000);
    // Scroll the top frame by 1 pixel manually because the host script lives
    // there so it will only fire the position changed event if the top window
    // itself is scrolled.
    window.top.scrollTo(window.top.scrollX, window.top.scrollY - 1);
  }, 2000);
  return Promise.all([imgPromise, pixelPromise, analyticsPromise]);
}

/**
 * Returns a promise that fetches the content of the AMP ad at the amp4test url.
 * This somewhat simulates rendering an ad by getting its content from an ad
 * server.
 */
function fetchAdContent() {
  const url = '//localhost:9876/amp4test/a4a/' + RequestBank.getBrowserId();
  return xhrServiceForTesting(window)
    .fetchText(url, {
      method: 'GET',
      ampCors: false,
      credentials: 'omit',
    })
    .then(res => res.text());
}

/**
 * Write the HTML page into the provided iframe then add it to the document.
 */
function writeFriendlyFrame(doc, iframe, adContent) {
  doc.body.appendChild(iframe);
  iframe.contentDocument.write(adContent);
  iframe.contentDocument.close();
}

/**
 * Write the HTML page into the provided iframe, turn it into a safe frame
 * then add it to the document.
 */
function writeSafeFrame(doc, iframe, adContent) {
  iframe.name = `1-0-31;${adContent.length};${adContent}{"uid": "test"}`;
  iframe.src =
    '//iframe.localhost:9876/test/fixtures/served/iframe-safeframe.html';
  doc.body.appendChild(iframe);
}

/**
 * Unregister the specified iframe from the host script at the top-level window.
 * Use this command to reset between tests so the host script stops observing
 * iframes that has been removed when their tests ended.
 */
function unregisterIframe(frame) {
  try {
    const hostWin = window.top;
    if (hostWin.AMP && hostWin.AMP.inaboxUnregisterIframe) {
      hostWin['AMP'].inaboxUnregisterIframe(frame);
    }
  } catch (e) {
    // ignore errors like: 'Can't execute code from a freed script' in Edge
  }
}
<commit_msg>✅ Skip flaky BTF integration test (#25369)<commit_after>/**
 * Copyright 2018 The AMP HTML Authors. All Rights Reserved.
 *
 * 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.
 */

import {RequestBank} from '../../testing/test-helper';
import {maybeSwitchToCompiledJs} from '../../testing/iframe';
import {parseQueryString} from '../../src/url';
import {toggleExperiment} from '../../src/experiments';
import {xhrServiceForTesting} from '../../src/service/xhr-impl';

describe('AMPHTML ad on AMP Page', () => {
  describes.integration(
    'ATF',
    {
      amp: true,
      extensions: ['amp-ad'],
      body: `
  <amp-ad
      width="300" height="250"
      id="i-amphtml-demo-id"
      type="fake"
      a4a-conversion="true"
      checksig=""
      disable3pfallback="true"
      src="//ads.localhost:9876/amp4test/a4a/${RequestBank.getBrowserId()}">
  </amp-ad>
      `,
    },
    () => {
      it('should layout amp-img, amp-pixel, amp-analytics', () => {
        // Open http://ads.localhost:9876/amp4test/a4a/12345 to see ad content
        return testAmpComponents();
      });
    }
  );

  describes.integration(
    'BTF',
    {
      amp: true,
      extensions: ['amp-ad'],
      frameStyle: 'height: 100vh',
      body: `

  <amp-ad
      width="300" height="250"
      id="i-amphtml-demo-id"
      type="fake"
      a4a-conversion="true"
      checksig=""
      disable3pfallback="true"
      src="//ads.localhost:9876/amp4test/a4a/${RequestBank.getBrowserId()}">
  </amp-ad>
      `,
    },
    env => {
      // TODO(#24657): Flaky on Travis.
      it.skip('should layout amp-img, amp-pixel, amp-analytics', () => {
        // Open http://ads.localhost:9876/amp4test/a4a/12345 to see ad content
        return testAmpComponentsBTF(env.win);
      });
    }
  );
});

describe('AMPHTML ad on non-AMP page (inabox)', () => {
  describes.integration(
    'ATF',
    {
      amp: false,
      body: `

      `,
    },
    env => {
      it('should layout amp-img, amp-pixel, amp-analytics', () => {
        // See amp4test.js for creative content
        return testAmpComponents();
      });

      afterEach(() => {
        unregisterIframe(env.win.document.getElementById('inabox'));
      });
    }
  );

  describes.integration(
    'BTF',
    {
      amp: false,
      frameStyle: 'height: 100vh',
      body: `

      `,
    },
    env => {
      it('should layout amp-img, amp-pixel, amp-analytics', () => {
        // See amp4test.js for creative content
        return testAmpComponentsBTF(env.win);
      });

      afterEach(() => {
        unregisterIframe(env.win.document.getElementById('inabox'));
      });
    }
  );

  describes.integration(
    'ATF within friendly frame or safe frame',
    {
      amp: false,
      body: `

      `,
    },
    env => {
      let adContent;
      let iframe;
      before(() => {
        // Gets the same ad as the other tests.
        return fetchAdContent().then(text => {
          adContent = text;
        });
      });

      beforeEach(() => {
        iframe = document.createElement('iframe');
        Array.prototype.push.apply(env.win.top.ampInaboxIframes, [iframe]);
      });

      afterEach(() => {
        unregisterIframe(iframe);
        env.win.document.body.removeChild(iframe);
      });

      it(
        'should layout amp-img, amp-pixel, ' +
          'amp-analytics within friendly frame',
        () => {
          writeFriendlyFrame(env.win.document, iframe, adContent);
          return testAmpComponents();
        }
      );

      it(
        'should layout amp-img, amp-pixel, ' +
          'amp-analytics within safe frame',
        () => {
          writeSafeFrame(env.win.document, iframe, adContent);
          return testAmpComponents();
        }
      );
    }
  );

  // TODO(zombifier): The BTF test fails on Safari (#21311).
  // TODO(powerivq): Flaky on Firefox and Edge too. unskip. (#24657)
  describes.integration(
    'BTF within friendly frame or safe frame',
    {
      amp: false,
      body: `

      `,
    },
    env => {
      let adContent;
      let iframe;
      before(() => {
        return fetchAdContent().then(text => {
          adContent = text;
        });
      });

      beforeEach(() => {
        env.iframe.style.height = '100vh';
        iframe = document.createElement('iframe');
        Array.prototype.push.apply(env.win.top.ampInaboxIframes, [iframe]);
      });

      afterEach(() => {
        unregisterIframe(iframe);
        env.win.document.body.removeChild(iframe);
      });

      it.skip(
        'should layout amp-img, amp-pixel, ' +
          'amp-analytics within friendly frame',
        () => {
          writeFriendlyFrame(env.win.document, iframe, adContent);
          return testAmpComponentsBTF(env.win);
        }
      );

      it.skip(
        'should layout amp-img, amp-pixel, ' +
          'amp-analytics within safe frame',
        () => {
          writeSafeFrame(env.win.document, iframe, adContent);
          return testAmpComponentsBTF(env.win);
        }
      );
    }
  );
});

describe('A more real AMPHTML image ad', () => {
  const {testServerPort} = window.ampTestRuntimeConfig;

  // The image ad as seen in examples/inabox.gpt.html,
  // with visibility pings being placeholders that's substituted with calls to
  // the request bank.
  const adBody = maybeSwitchToCompiledJs(
    // eslint-disable-next-line no-undef
    __html__['test/fixtures/amp-cupcake-ad.html']
  )
    .replace(/__TEST_SERVER_PORT__/g, testServerPort)
    .replace(/__VIEW_URL__/g, RequestBank.getUrl('view')) // get all instances
    .replace('__VISIBLE_URL__', RequestBank.getUrl('visible'))
    .replace('__ACTIVE_VIEW_URL__', RequestBank.getUrl('activeview'));

  function testVisibilityPings(visibleDelay, activeViewDelay) {
    let viewTime = 0;
    let visibleTime = 0;
    let activeViewTime = 0;
    const viewPromise = RequestBank.withdraw('view').then(
      () => (viewTime = Date.now())
    );
    const visiblePromise = RequestBank.withdraw('visible').then(
      () => (visibleTime = Date.now())
    );
    const activeViewPromise = RequestBank.withdraw('activeview').then(
      () => (activeViewTime = Date.now())
    );
    return Promise.all([viewPromise, visiblePromise, activeViewPromise]).then(
      () => {
        // Add a 200ms "buffer" to account for possible browser jankiness
        expect(visibleTime - viewTime).to.be.above(visibleDelay - 200);
        expect(activeViewTime - viewTime).to.be.above(activeViewDelay - 200);
      }
    );
  }

  describes.integration(
    'ATF within friendly frame or safe frame',
    {
      amp: false,
      body: `

        `,
    },
    env => {
      let iframe;
      let doc;
      beforeEach(() => {
        doc = env.win.document;
        iframe = document.createElement('iframe');
        // we add the iframe here because it's dynamically created, so the
        // bootstrap script would have missed it.
        Array.prototype.push.apply(env.win.top.ampInaboxIframes, [iframe]);
      });

      it('should properly render ad in a friendly iframe with viewability pings', () => {
        writeFriendlyFrame(doc, iframe, adBody);
        return testVisibilityPings(0, 1000);
      });

      it('should properly render ad in a safe frame with viewability pings', () => {
        writeSafeFrame(doc, iframe, adBody);
        return testVisibilityPings(0, 1000);
      });

      afterEach(() => {
        unregisterIframe(iframe);
        doc.body.removeChild(iframe);
      });
    }
  );

  // Testing that analytics components use IntersectionObserver properly.
  describes.integration(
    'No Host Script within friendly frame or safe frame',
    {
      amp: false,
      body: '',
    },
    env => {
      let iframe;
      let doc;
      beforeEach(() => {
        doc = env.win.document;
        iframe = document.createElement('iframe');
      });

      it('should properly render ad in a friendly iframe with viewability pings', () => {
        toggleExperiment(env.win, 'inabox-viewport-friendly', true);
        writeFriendlyFrame(doc, iframe, adBody);
        return testVisibilityPings(0, 1000);
      });

      it.configure()
        .ifChrome()
        .run(
          'should properly render ad in a safe frame with viewability pings',
          () => {
            writeSafeFrame(doc, iframe, adBody);
            return testVisibilityPings(0, 1000);
          }
        );

      afterEach(() => {
        doc.body.removeChild(iframe);
      });
    }
  );

  // TODO(zombifier): unskip the tests.
  describes.integration(
    'BTF within friendly frame or safe frame',
    {
      amp: false,
      body: `

        `,
    },
    env => {
      let iframe;
      let doc;
      beforeEach(() => {
        env.iframe.style.height = '100vh';
        doc = env.win.document;
        iframe = document.createElement('iframe');
        Array.prototype.push.apply(env.win.top.ampInaboxIframes, [iframe]);
        setTimeout(() => {
          env.win.scrollTo(0, 1000);
          window.top.scrollTo(window.top.scrollX, window.top.scrollY - 1);
        }, 2000);
      });

      it.skip('should properly render ad in a friendly iframe with viewability pings', () => {
        writeFriendlyFrame(doc, iframe, adBody);
        return testVisibilityPings(2000, 3000);
      });

      it.skip('should properly render ad in a safe frame with viewability pings', () => {
        writeSafeFrame(doc, iframe, adBody);
        return testVisibilityPings(2000, 3000);
      });

      afterEach(() => {
        unregisterIframe(iframe);
        doc.body.removeChild(iframe);
      });
    }
  );
});

function testAmpComponents() {
  const imgPromise = RequestBank.withdraw('image').then(req => {
    expect(req.url).to.equal('/');
  });
  const pixelPromise = RequestBank.withdraw('pixel').then(req => {
    expect(req.url).to.equal('/foo?cid=');
  });
  const analyticsPromise = RequestBank.withdraw('analytics').then(req => {
    expect(req.url).to.match(/^\/bar\?/);
    const queries = parseQueryString(req.url.substr('/bar'.length));
    expect(queries['cid']).to.equal('');
    expect(queries['sourceUrl']).be.ok;
    // Cookie is sent via http response header when requesting
    // localhost:9876/amp4test/a4a/
    // COOKIE macro is not allowed in inabox and resolves to empty
    expect(queries['cookie']).to.equal('');
  });
  return Promise.all([imgPromise, pixelPromise, analyticsPromise]);
}

function testAmpComponentsBTF(win) {
  // The iframe starts BTF. "visible" trigger should be after scroll.
  // We will record scrolling time for comparison.
  let scrollTime = Infinity;
  const imgPromise = RequestBank.withdraw('image').then(req => {
    expect(Date.now()).to.be.below(scrollTime);
    expect(req.url).to.equal('/');
  });
  const pixelPromise = RequestBank.withdraw('pixel').then(req => {
    expect(Date.now()).to.be.below(scrollTime);
    expect(req.url).to.equal('/foo?cid=');
  });
  const analyticsPromise = RequestBank.withdraw('analytics').then(req => {
    expect(req.url).to.match(/^\/bar\?/);
    const queries = parseQueryString(req.url.substr('/bar'.length));
    expect(queries['cid']).to.equal('');
    expect(Date.now()).to.be.above(scrollTime);
    expect(parseInt(queries['timestamp'], 10)).to.be.above(scrollTime);
  });
  setTimeout(() => {
    scrollTime = Date.now();
    win.scrollTo(0, 1000);
    // Scroll the top frame by 1 pixel manually because the host script lives
    // there so it will only fire the position changed event if the top window
    // itself is scrolled.
    window.top.scrollTo(window.top.scrollX, window.top.scrollY - 1);
  }, 2000);
  return Promise.all([imgPromise, pixelPromise, analyticsPromise]);
}

/**
 * Returns a promise that fetches the content of the AMP ad at the amp4test url.
 * This somewhat simulates rendering an ad by getting its content from an ad
 * server.
 */
function fetchAdContent() {
  const url = '//localhost:9876/amp4test/a4a/' + RequestBank.getBrowserId();
  return xhrServiceForTesting(window)
    .fetchText(url, {
      method: 'GET',
      ampCors: false,
      credentials: 'omit',
    })
    .then(res => res.text());
}

/**
 * Write the HTML page into the provided iframe then add it to the document.
 */
function writeFriendlyFrame(doc, iframe, adContent) {
  doc.body.appendChild(iframe);
  iframe.contentDocument.write(adContent);
  iframe.contentDocument.close();
}

/**
 * Write the HTML page into the provided iframe, turn it into a safe frame
 * then add it to the document.
 */
function writeSafeFrame(doc, iframe, adContent) {
  iframe.name = `1-0-31;${adContent.length};${adContent}{"uid": "test"}`;
  iframe.src =
    '//iframe.localhost:9876/test/fixtures/served/iframe-safeframe.html';
  doc.body.appendChild(iframe);
}

/**
 * Unregister the specified iframe from the host script at the top-level window.
 * Use this command to reset between tests so the host script stops observing
 * iframes that has been removed when their tests ended.
 */
function unregisterIframe(frame) {
  try {
    const hostWin = window.top;
    if (hostWin.AMP && hostWin.AMP.inaboxUnregisterIframe) {
      hostWin['AMP'].inaboxUnregisterIframe(frame);
    }
  } catch (e) {
    // ignore errors like: 'Can't execute code from a freed script' in Edge
  }
}
<|endoftext|>


<commit_before>import angular from 'angular';
import $ from 'jquery';
import 'pivottable';
import 'pivottable/dist/pivot.css';

import editorTemplate from './pivottable-editor.html';
import './pivot.less';

function pivotTableRenderer() {
  return {
    restrict: 'E',
    scope: {
      queryResult: '=',
      visualization: '=',
    },
    template: '',
    replace: false,
    link($scope, element) {
      function removeControls() {
        const hideControls =
          $scope.visualization.options.controls &&
          $scope.visualization.options.controls.enabled;

        document.querySelectorAll('.pvtAxisContainer, .pvtRenderer, .pvtVals').forEach((control) => {
          if (hideControls) {
            control.style.display = 'none';
          } else {
            control.style.display = '';
          }
        });
      }

      function updatePivot() {
        $scope.$watch('queryResult && queryResult.getData()', (data) => {
          if (!data) {
            return;
          }

          if ($scope.queryResult.getData() !== null) {
            // We need to give the pivot table its own copy of the data, because it changes
            // it which interferes with other visualizations.
            data = angular.copy($scope.queryResult.getData());
            const options = {
              renderers: $.pivotUtilities.renderers,
              onRefresh(config) {
                const configCopy = Object.assign({}, config);
                // delete some values which are functions
                delete configCopy.aggregators;
                delete configCopy.renderers;
                delete configCopy.onRefresh;
                // delete some bulky default values
                delete configCopy.rendererOptions;
                delete configCopy.localeStrings;

                if ($scope.visualization) {
                  $scope.visualization.options = configCopy;
                }
              },
            };

            if ($scope.visualization) {
              Object.assign(options, $scope.visualization.options);
            }

            $(element).pivotUI(data, options, true);
            removeControls();
          }
        });
      }

      $scope.$watch('queryResult && queryResult.getData()', updatePivot);
      $scope.$watch('visualization.options.controls.enabled', removeControls);
    },
  };
}

function pivotTableEditor() {
  return {
    restrict: 'E',
    template: editorTemplate,
  };
}

export default function init(ngModule) {
  ngModule.directive('pivotTableRenderer', pivotTableRenderer);
  ngModule.directive('pivotTableEditor', pivotTableEditor);

  ngModule.config((VisualizationProvider) => {
    const editTemplate = '<pivot-table-editor></pivot-table-editor>';
    const defaultOptions = {
      defaultRows: 10,
      defaultColumns: 3,
      minColumns: 2,
    };

    VisualizationProvider.registerVisualization({
      type: 'PIVOT',
      name: 'Pivot Table',
      renderTemplate: '<pivot-table-renderer visualization="visualization" query-result="queryResult"></pivot-table-renderer>',
      editorTemplate: editTemplate,
      defaultOptions,
    });
  });
}
<commit_msg>Fix pivot hide control in dashboards<commit_after>import angular from 'angular';
import $ from 'jquery';
import 'pivottable';
import 'pivottable/dist/pivot.css';

import editorTemplate from './pivottable-editor.html';
import './pivot.less';

function pivotTableRenderer() {
  return {
    restrict: 'E',
    scope: {
      queryResult: '=',
      visualization: '=',
    },
    template: '',
    replace: false,
    link($scope, element) {
      function removeControls() {
        const hideControls =
          $scope.visualization.options.controls &&
          $scope.visualization.options.controls.enabled;

        element[0].querySelectorAll('.pvtAxisContainer, .pvtRenderer, .pvtVals').forEach((control) => {
          if (hideControls) {
            control.style.display = 'none';
          } else {
            control.style.display = '';
          }
        });
      }

      function updatePivot() {
        $scope.$watch('queryResult && queryResult.getData()', (data) => {
          if (!data) {
            return;
          }

          if ($scope.queryResult.getData() !== null) {
            // We need to give the pivot table its own copy of the data, because it changes
            // it which interferes with other visualizations.
            data = angular.copy($scope.queryResult.getData());
            const options = {
              renderers: $.pivotUtilities.renderers,
              onRefresh(config) {
                const configCopy = Object.assign({}, config);
                // delete some values which are functions
                delete configCopy.aggregators;
                delete configCopy.renderers;
                delete configCopy.onRefresh;
                // delete some bulky default values
                delete configCopy.rendererOptions;
                delete configCopy.localeStrings;

                if ($scope.visualization) {
                  $scope.visualization.options = configCopy;
                }
              },
            };

            if ($scope.visualization) {
              Object.assign(options, $scope.visualization.options);
            }

            $(element).pivotUI(data, options, true);
            removeControls();
          }
        });
      }

      $scope.$watch('queryResult && queryResult.getData()', updatePivot);
      $scope.$watch('visualization.options.controls.enabled', removeControls);
    },
  };
}

function pivotTableEditor() {
  return {
    restrict: 'E',
    template: editorTemplate,
  };
}

export default function init(ngModule) {
  ngModule.directive('pivotTableRenderer', pivotTableRenderer);
  ngModule.directive('pivotTableEditor', pivotTableEditor);

  ngModule.config((VisualizationProvider) => {
    const editTemplate = '<pivot-table-editor></pivot-table-editor>';
    const defaultOptions = {
      defaultRows: 10,
      defaultColumns: 3,
      minColumns: 2,
    };

    VisualizationProvider.registerVisualization({
      type: 'PIVOT',
      name: 'Pivot Table',
      renderTemplate: '<pivot-table-renderer visualization="visualization" query-result="queryResult"></pivot-table-renderer>',
      editorTemplate: editTemplate,
      defaultOptions,
    });
  });
}
<|endoftext|>