<commit_before>Handlebars.registerHelper('toCapitalCase', function(str) {
  return str.charAt(0).toUpperCase() + str.slice(1);
});

function initUserLanguage() {
  var language = amplify.store("language");

  if (language){
    Session.set("language", language);
  }

  setUserLanguage(getUserLanguage());
}

function getUserLanguage() {
  var language = Session.get("language");

  if (language){
    return language;
  } else {
    return "en";
  }
};

function setUserLanguage(language) {
  TAPi18n.setLanguage(language).done(function () {
    Session.set("language", language);
    amplify.store("language", language);
  });
}

function getLanguageDirection() {
  var language = getUserLanguage()
  var rtlLanguages = ['he', 'ar'];

  if ($.inArray(language, rtlLanguages) !== -1) {
    return 'rtl';
  } else {
    return 'ltr';
  }
}

function getLanguageList() {
  var languages = TAPi18n.getLanguages();
  var languageList = _.map(languages, function(value, key) {
    var selected = "";

    if (key == getUserLanguage()){
      selected = "selected";
    }

    // Gujarati isn't handled automatically by tap-i18n,
    // so we need to set the language name manually
    if (value.name == "gu"){
        value.name = "ગુજરાતી";
    }

    return {
      code: key,
      selected: selected,
      languageDetails: value
    };
  });

  if (languageList.length <= 1){
    return null;
  }

  return languageList;
}

function getCurrentGame(){
  var gameID = Session.get("gameID");

  if (gameID) {
    return Games.findOne(gameID);
  }
}

function getAccessLink(){
  var game = getCurrentGame();

  if (!game){
    return;
  }

  return Meteor.settings.public.url + game.accessCode + "/";
}

function getCurrentPlayer(){
  var playerID = Session.get("playerID");

  if (playerID) {
    return Players.findOne(playerID);
  }
}

function generateAccessCode(){
  var code = "";
  var possible = "abcdefghijklmnopqrstuvwxyz";

    for(var i=0; i < 6; i++){
      code += possible.charAt(Math.floor(Math.random() * possible.length));
    }

    return code;
}

function generateNewGame(){
  var game = {
    accessCode: generateAccessCode(),
    state: "waitingForPlayers",
    location: null,
    lengthInMinutes: 20,
    endTime: null,
    paused: false,
    pausedTime: null,

    rounds: [null, null, null, null, null],
    //propsal: [],
    approveVotes: null,
    rejectVote: null,
    proposalCount: 0,
    proposing: true,
    proposedMissionVoting: false,
    mission: false,
    numOnMission: 0,

    onMission: null
  };

  var gameID = Games.insert(game);
  game = Games.findOne(gameID);

  return game;
}

function generateNewPlayer(game, name){
  var player = {
    gameID: game._id,
    name: name,
    role: null,
    isSpy: false,
    isFirstPlayer: false,
    isProposing: false,
    isOnProposedMission: false,
    voted: false,
    isOnMission: false,
  };

  var playerID = Players.insert(player);

  return Players.findOne(playerID);
}

function resetUserState(){
  var player = getCurrentPlayer();

  if (player){
    Players.remove(player._id);
  }

  Session.set("gameID", null);
  Session.set("playerID", null);
}

function trackGameState () {
  var gameID = Session.get("gameID");
  var playerID = Session.get("playerID");

  if (!gameID || !playerID){
    return;
  }

  var game = Games.findOne(gameID);
  var player = Players.findOne(playerID);

  if (!game || !player){
    Session.set("gameID", null);
    Session.set("playerID", null);
    Session.set("currentView", "startMenu");
    return;
  }

  if(game.state === "inProgress"){
    Session.set("currentView", "gameView");
  } else if (game.state === "waitingForPlayers") {
    Session.set("currentView", "lobby");
  }
}

function leaveGame () {
  var player = getCurrentPlayer();

  Session.set("currentView", "startMenu");
  Players.remove(player._id);

  Session.set("playerID", null);
}

function hasHistoryApi () {
  return !!(window.history && window.history.pushState);
}

initUserLanguage();

Meteor.setInterval(function () {
  Session.set('time', new Date());
}, 1000);

if (hasHistoryApi()){
  function trackUrlState () {
    var accessCode = null;
    var game = getCurrentGame();
    if (game){
      accessCode = game.accessCode;
    } else {
      accessCode = Session.get('urlAccessCode');
    }

    var currentURL = '/';
    if (accessCode){
      currentURL += accessCode+'/';
    }
    window.history.pushState(null, null, currentURL);
  }
  Tracker.autorun(trackUrlState);
}
Tracker.autorun(trackGameState);

window.onbeforeunload = resetUserState;
window.onpagehide = resetUserState;

FlashMessages.configure({
  autoHide: true,
  autoScroll: false
});

Template.main.helpers({
  whichView: function() {
    return Session.get('currentView');
  },
  language: function() {
    return getUserLanguage();
  },
  textDirection: function() {
    return getLanguageDirection();
  }
});

Template.footer.helpers({
  languages: getLanguageList
})

Template.footer.events({
  'click .btn-set-language': function (event) {
    var language = $(event.target).data('language');
    setUserLanguage(language);
  },
  'change .language-select': function (event) {
    var language = event.target.value;
    setUserLanguage(language);
  }
})

Template.startMenu.events({
  'click #btn-new-game': function () {
    Session.set("currentView", "createGame");
  },
  'click #btn-join-game': function () {
    Session.set("currentView", "joinGame");
  }
});

Template.startMenu.helpers({
  alternativeURL: function() {
    return "#";//Meteor.settings.public.alternative;
  }
});

Template.startMenu.rendered = function () {
  resetUserState();
};

Template.createGame.events({
  'submit #create-game': function (event) {
    var playerName = event.target.playerName.value;

    if (!playerName || Session.get('loading')) {
      return false;
    }

    var game = generateNewGame();
    var player = generateNewPlayer(game, playerName);

    Meteor.subscribe('games', game.accessCode);

    Session.set("loading", true);

    Meteor.subscribe('players', game._id, function onReady(){
      Session.set("loading", false);

      Session.set("gameID", game._id);
      Session.set("playerID", player._id);
      Session.set("currentView", "lobby");
    });

    return false;
  },
  'click .btn-back': function () {
    Session.set("currentView", "startMenu");
    return false;
  }
});

Template.createGame.helpers({
  isLoading: function() {
    return Session.get('loading');
  }
});

Template.createGame.rendered = function (event) {
  $("#player-name").focus();
};

Template.joinGame.events({
  'submit #join-game': function (event) {
    var accessCode = event.target.accessCode.value;
    var playerName = event.target.playerName.value;

    if (!playerName || Session.get('loading')) {
      return false;
    }

    accessCode = accessCode.trim();
    accessCode = accessCode.toLowerCase();

    Session.set("loading", true);

    Meteor.subscribe('games', accessCode, function onReady(){
      Session.set("loading", false);

      var game = Games.findOne({
        accessCode: accessCode
      });

      if (game) {
        Meteor.subscribe('players', game._id);
        player = generateNewPlayer(game, playerName);

        if (game.state === "inProgress") {
          var default_role = game.location.roles[game.location.roles.length - 1];
          Players.update(player._id, {$set: {role: default_role}});
        }

        Session.set('urlAccessCode', null);
        Session.set("gameID", game._id);
        Session.set("playerID", player._id);
        Session.set("currentView", "lobby");
      } else {
        FlashMessages.sendError(TAPi18n.__("ui.invalid access code"));
        //GAnalytics.event("game-actions", "invalidcode");
      }
    });

    return false;
  },
  'click .btn-back': function () {
    Session.set('urlAccessCode', null);
    Session.set("currentView", "startMenu");
    return false;
  }
});

Template.joinGame.helpers({
  isLoading: function() {
    return Session.get('loading');
  }
});

Template.joinGame.rendered = function (event) {
  resetUserState();

  var urlAccessCode = Session.get('urlAccessCode');

  if (urlAccessCode){
    $("#access-code").val(urlAccessCode);
    $("#access-code").hide();
    $("#player-name").focus();
  } else {
    $("#access-code").focus();
  }
};

Template.lobby.helpers({
  game: function () {
    return getCurrentGame();
  },
  accessLink: function () {
    return getAccessLink();
  },
  player: function () {
    return getCurrentPlayer();
  },
  players: function () {
    var game = getCurrentGame();
    var currentPlayer = getCurrentPlayer();

    if (!game) {
      return null;
    }

    var players = Players.find({'gameID': game._id}, {'sort': {'name': 1}}).fetch();

    players.forEach(function(player){
      if (player._id === currentPlayer._id){
        player.isCurrent = true;
      }
    });

    return players;
  },
  isLoading: function() {
    var game = getCurrentGame();
    return game.state === 'settingUp';
  },
  notEnoughPeople: function() {
    var game = getCurrentGame();
    console.log("here: " + Players.find({gameID: game._id}).fetch().length);
    return Players.find({gameID: game._id}).fetch().length < 5;
  }
});

Template.lobby.events({
  'click .btn-leave': leaveGame,
  'click .btn-start': function () {
    var game = getCurrentGame();
    if (Players.find({gameID: game._id}).fetch().length >= 5) {
      Games.update(game._id, {$set: {state: 'settingUp'}});
      Games.update(game._id, {$set: {numOnMission: getNumMissionPlayers()}});
    } else {
      console.log("Too few players");
    }
  },
  'click .btn-toggle-qrcode': function () {
    $(".qrcode-container").toggle();
  },
  'click .btn-remove-player': function (event) {
    var playerID = $(event.currentTarget).data('player-id');
    Players.remove(playerID);
  },
  'click .btn-edit-player': function (event) {
    var game = getCurrentGame();
    resetUserState();
    Session.set('urlAccessCode', game.accessCode);
    Session.set('currentView', 'joinGame');
  }
});

Template.lobby.rendered = function (event) {
  var url = getAccessLink();
  var qrcodesvg = new Qrcodesvg(url, "qrcode", 250);
  qrcodesvg.draw();
};

function getTimeRemaining(){
  var game = getCurrentGame();
  var localEndTime = game.endTime - TimeSync.serverOffset();

  if (game.paused){
    var localPausedTime = game.pausedTime - TimeSync.serverOffset();
    var timeRemaining = localEndTime - localPausedTime;
  } else {
    var timeRemaining = localEndTime - Session.get('time');
  }

  if (timeRemaining < 0) {
    timeRemaining = 0;
  }

  return timeRemaining;
}

Template.gameView.helpers({
  game: getCurrentGame,
  player: getCurrentPlayer,
  players: function () {
    var game = getCurrentGame();

    if (!game){
      return null;
    }

    var players = Players.find({
      'gameID': game._id
    }, {'sort': {'name' : 1}});

    return players;
  },
  locations: function () {
    return locations;
  },
  gameFinished: function () {
    var timeRemaining = getTimeRemaining();

    return timeRemaining === 0;
  },
  timeRemaining: function () {
    var timeRemaining = getTimeRemaining();

    return moment(timeRemaining).format('mm[<span>:</span>]ss');
  }
});

Template.gameView.events({
  'click .btn-leave': leaveGame,
  'click .btn-end': function () {
    var game = getCurrentGame();
    Games.update(game._id, {$set: {state: 'waitingForPlayers'}});
  },
  'click .btn-toggle-status': function () {
    $(".status-container-content").toggle();
  },
  'click .game-countdown': function () {
    var game = getCurrentGame();
    var currentServerTime = TimeSync.serverTime(moment());

    if(game.paused){
      var newEndTime = game.endTime - game.pausedTime + currentServerTime;
      Games.update(game._id, {$set: {paused: false, pausedTime: null, endTime: newEndTime}});
    } else {
      Games.update(game._id, {$set: {paused: true, pausedTime: currentServerTime}});
    }
  },
  'click .player-name': function (event) {
    if (getCurrentPlayer().isProposing) {
      event.target.className = 'player-name-selected';
      addToProposal(Session.get("gameID"), event.target.dataset.name);
    }
  },
  'click .player-name-selected': function(event) {
    removeFromProposal(Session.get("gameID"), event.target.dataset.name);
    event.target.className = 'player-name';
  },
  'click .location-name': function (event) {
    event.target.className = 'location-name-striked';
  },
  'click .location-name-striked': function(event) {
    event.target.className = 'location-name';
  },
  'click .btn-proposal-submit': function() {
    submitProposal();
  }
});
<commit_msg>remove console<commit_after>Handlebars.registerHelper('toCapitalCase', function(str) {
  return str.charAt(0).toUpperCase() + str.slice(1);
});

function initUserLanguage() {
  var language = amplify.store("language");

  if (language){
    Session.set("language", language);
  }

  setUserLanguage(getUserLanguage());
}

function getUserLanguage() {
  var language = Session.get("language");

  if (language){
    return language;
  } else {
    return "en";
  }
};

function setUserLanguage(language) {
  TAPi18n.setLanguage(language).done(function () {
    Session.set("language", language);
    amplify.store("language", language);
  });
}

function getLanguageDirection() {
  var language = getUserLanguage()
  var rtlLanguages = ['he', 'ar'];

  if ($.inArray(language, rtlLanguages) !== -1) {
    return 'rtl';
  } else {
    return 'ltr';
  }
}

function getLanguageList() {
  var languages = TAPi18n.getLanguages();
  var languageList = _.map(languages, function(value, key) {
    var selected = "";

    if (key == getUserLanguage()){
      selected = "selected";
    }

    // Gujarati isn't handled automatically by tap-i18n,
    // so we need to set the language name manually
    if (value.name == "gu"){
        value.name = "ગુજરાતી";
    }

    return {
      code: key,
      selected: selected,
      languageDetails: value
    };
  });

  if (languageList.length <= 1){
    return null;
  }

  return languageList;
}

function getCurrentGame(){
  var gameID = Session.get("gameID");

  if (gameID) {
    return Games.findOne(gameID);
  }
}

function getAccessLink(){
  var game = getCurrentGame();

  if (!game){
    return;
  }

  return Meteor.settings.public.url + game.accessCode + "/";
}

function getCurrentPlayer(){
  var playerID = Session.get("playerID");

  if (playerID) {
    return Players.findOne(playerID);
  }
}

function generateAccessCode(){
  var code = "";
  var possible = "abcdefghijklmnopqrstuvwxyz";

    for(var i=0; i < 6; i++){
      code += possible.charAt(Math.floor(Math.random() * possible.length));
    }

    return code;
}

function generateNewGame(){
  var game = {
    accessCode: generateAccessCode(),
    state: "waitingForPlayers",
    location: null,
    lengthInMinutes: 20,
    endTime: null,
    paused: false,
    pausedTime: null,

    rounds: [null, null, null, null, null],
    //propsal: [],
    approveVotes: null,
    rejectVote: null,
    proposalCount: 0,
    proposing: true,
    proposedMissionVoting: false,
    mission: false,
    numOnMission: 0,

    onMission: null
  };

  var gameID = Games.insert(game);
  game = Games.findOne(gameID);

  return game;
}

function generateNewPlayer(game, name){
  var player = {
    gameID: game._id,
    name: name,
    role: null,
    isSpy: false,
    isFirstPlayer: false,
    isProposing: false,
    isOnProposedMission: false,
    voted: false,
    isOnMission: false,
  };

  var playerID = Players.insert(player);

  return Players.findOne(playerID);
}

function resetUserState(){
  var player = getCurrentPlayer();

  if (player){
    Players.remove(player._id);
  }

  Session.set("gameID", null);
  Session.set("playerID", null);
}

function trackGameState () {
  var gameID = Session.get("gameID");
  var playerID = Session.get("playerID");

  if (!gameID || !playerID){
    return;
  }

  var game = Games.findOne(gameID);
  var player = Players.findOne(playerID);

  if (!game || !player){
    Session.set("gameID", null);
    Session.set("playerID", null);
    Session.set("currentView", "startMenu");
    return;
  }

  if(game.state === "inProgress"){
    Session.set("currentView", "gameView");
  } else if (game.state === "waitingForPlayers") {
    Session.set("currentView", "lobby");
  }
}

function leaveGame () {
  var player = getCurrentPlayer();

  Session.set("currentView", "startMenu");
  Players.remove(player._id);

  Session.set("playerID", null);
}

function hasHistoryApi () {
  return !!(window.history && window.history.pushState);
}

initUserLanguage();

Meteor.setInterval(function () {
  Session.set('time', new Date());
}, 1000);

if (hasHistoryApi()){
  function trackUrlState () {
    var accessCode = null;
    var game = getCurrentGame();
    if (game){
      accessCode = game.accessCode;
    } else {
      accessCode = Session.get('urlAccessCode');
    }

    var currentURL = '/';
    if (accessCode){
      currentURL += accessCode+'/';
    }
    window.history.pushState(null, null, currentURL);
  }
  Tracker.autorun(trackUrlState);
}
Tracker.autorun(trackGameState);

window.onbeforeunload = resetUserState;
window.onpagehide = resetUserState;

FlashMessages.configure({
  autoHide: true,
  autoScroll: false
});

Template.main.helpers({
  whichView: function() {
    return Session.get('currentView');
  },
  language: function() {
    return getUserLanguage();
  },
  textDirection: function() {
    return getLanguageDirection();
  }
});

Template.footer.helpers({
  languages: getLanguageList
})

Template.footer.events({
  'click .btn-set-language': function (event) {
    var language = $(event.target).data('language');
    setUserLanguage(language);
  },
  'change .language-select': function (event) {
    var language = event.target.value;
    setUserLanguage(language);
  }
})

Template.startMenu.events({
  'click #btn-new-game': function () {
    Session.set("currentView", "createGame");
  },
  'click #btn-join-game': function () {
    Session.set("currentView", "joinGame");
  }
});

Template.startMenu.helpers({
  alternativeURL: function() {
    return "#";//Meteor.settings.public.alternative;
  }
});

Template.startMenu.rendered = function () {
  resetUserState();
};

Template.createGame.events({
  'submit #create-game': function (event) {
    var playerName = event.target.playerName.value;

    if (!playerName || Session.get('loading')) {
      return false;
    }

    var game = generateNewGame();
    var player = generateNewPlayer(game, playerName);

    Meteor.subscribe('games', game.accessCode);

    Session.set("loading", true);

    Meteor.subscribe('players', game._id, function onReady(){
      Session.set("loading", false);

      Session.set("gameID", game._id);
      Session.set("playerID", player._id);
      Session.set("currentView", "lobby");
    });

    return false;
  },
  'click .btn-back': function () {
    Session.set("currentView", "startMenu");
    return false;
  }
});

Template.createGame.helpers({
  isLoading: function() {
    return Session.get('loading');
  }
});

Template.createGame.rendered = function (event) {
  $("#player-name").focus();
};

Template.joinGame.events({
  'submit #join-game': function (event) {
    var accessCode = event.target.accessCode.value;
    var playerName = event.target.playerName.value;

    if (!playerName || Session.get('loading')) {
      return false;
    }

    accessCode = accessCode.trim();
    accessCode = accessCode.toLowerCase();

    Session.set("loading", true);

    Meteor.subscribe('games', accessCode, function onReady(){
      Session.set("loading", false);

      var game = Games.findOne({
        accessCode: accessCode
      });

      if (game) {
        Meteor.subscribe('players', game._id);
        player = generateNewPlayer(game, playerName);

        if (game.state === "inProgress") {
          var default_role = game.location.roles[game.location.roles.length - 1];
          Players.update(player._id, {$set: {role: default_role}});
        }

        Session.set('urlAccessCode', null);
        Session.set("gameID", game._id);
        Session.set("playerID", player._id);
        Session.set("currentView", "lobby");
      } else {
        FlashMessages.sendError(TAPi18n.__("ui.invalid access code"));
        //GAnalytics.event("game-actions", "invalidcode");
      }
    });

    return false;
  },
  'click .btn-back': function () {
    Session.set('urlAccessCode', null);
    Session.set("currentView", "startMenu");
    return false;
  }
});

Template.joinGame.helpers({
  isLoading: function() {
    return Session.get('loading');
  }
});

Template.joinGame.rendered = function (event) {
  resetUserState();

  var urlAccessCode = Session.get('urlAccessCode');

  if (urlAccessCode){
    $("#access-code").val(urlAccessCode);
    $("#access-code").hide();
    $("#player-name").focus();
  } else {
    $("#access-code").focus();
  }
};

Template.lobby.helpers({
  game: function () {
    return getCurrentGame();
  },
  accessLink: function () {
    return getAccessLink();
  },
  player: function () {
    return getCurrentPlayer();
  },
  players: function () {
    var game = getCurrentGame();
    var currentPlayer = getCurrentPlayer();

    if (!game) {
      return null;
    }

    var players = Players.find({'gameID': game._id}, {'sort': {'name': 1}}).fetch();

    players.forEach(function(player){
      if (player._id === currentPlayer._id){
        player.isCurrent = true;
      }
    });

    return players;
  },
  isLoading: function() {
    var game = getCurrentGame();
    return game.state === 'settingUp';
  },
  notEnoughPeople: function() {
    var game = getCurrentGame();
    return Players.find({gameID: game._id}).fetch().length < 5;
  }
});

Template.lobby.events({
  'click .btn-leave': leaveGame,
  'click .btn-start': function () {
    var game = getCurrentGame();
    if (Players.find({gameID: game._id}).fetch().length >= 5) {
      Games.update(game._id, {$set: {state: 'settingUp'}});
      Games.update(game._id, {$set: {numOnMission: getNumMissionPlayers()}});
    } else {
      console.log("Too few players");
    }
  },
  'click .btn-toggle-qrcode': function () {
    $(".qrcode-container").toggle();
  },
  'click .btn-remove-player': function (event) {
    var playerID = $(event.currentTarget).data('player-id');
    Players.remove(playerID);
  },
  'click .btn-edit-player': function (event) {
    var game = getCurrentGame();
    resetUserState();
    Session.set('urlAccessCode', game.accessCode);
    Session.set('currentView', 'joinGame');
  }
});

Template.lobby.rendered = function (event) {
  var url = getAccessLink();
  var qrcodesvg = new Qrcodesvg(url, "qrcode", 250);
  qrcodesvg.draw();
};

function getTimeRemaining(){
  var game = getCurrentGame();
  var localEndTime = game.endTime - TimeSync.serverOffset();

  if (game.paused){
    var localPausedTime = game.pausedTime - TimeSync.serverOffset();
    var timeRemaining = localEndTime - localPausedTime;
  } else {
    var timeRemaining = localEndTime - Session.get('time');
  }

  if (timeRemaining < 0) {
    timeRemaining = 0;
  }

  return timeRemaining;
}

Template.gameView.helpers({
  game: getCurrentGame,
  player: getCurrentPlayer,
  players: function () {
    var game = getCurrentGame();

    if (!game){
      return null;
    }

    var players = Players.find({
      'gameID': game._id
    }, {'sort': {'name' : 1}});

    return players;
  },
  locations: function () {
    return locations;
  },
  gameFinished: function () {
    var timeRemaining = getTimeRemaining();

    return timeRemaining === 0;
  },
  timeRemaining: function () {
    var timeRemaining = getTimeRemaining();

    return moment(timeRemaining).format('mm[<span>:</span>]ss');
  }
});

Template.gameView.events({
  'click .btn-leave': leaveGame,
  'click .btn-end': function () {
    var game = getCurrentGame();
    Games.update(game._id, {$set: {state: 'waitingForPlayers'}});
  },
  'click .btn-toggle-status': function () {
    $(".status-container-content").toggle();
  },
  'click .game-countdown': function () {
    var game = getCurrentGame();
    var currentServerTime = TimeSync.serverTime(moment());

    if(game.paused){
      var newEndTime = game.endTime - game.pausedTime + currentServerTime;
      Games.update(game._id, {$set: {paused: false, pausedTime: null, endTime: newEndTime}});
    } else {
      Games.update(game._id, {$set: {paused: true, pausedTime: currentServerTime}});
    }
  },
  'click .player-name': function (event) {
    if (getCurrentPlayer().isProposing) {
      event.target.className = 'player-name-selected';
      addToProposal(Session.get("gameID"), event.target.dataset.name);
    }
  },
  'click .player-name-selected': function(event) {
    removeFromProposal(Session.get("gameID"), event.target.dataset.name);
    event.target.className = 'player-name';
  },
  'click .location-name': function (event) {
    event.target.className = 'location-name-striked';
  },
  'click .location-name-striked': function(event) {
    event.target.className = 'location-name';
  },
  'click .btn-proposal-submit': function() {
    submitProposal();
  }
});
<|endoftext|>


<commit_before>var CONFIG = {server: "http://localhost:8052"};
var ME = {id: undefined, akey: undefined};

function ajax(params) {
    var qstring = "", qparams = [];
    var uri = CONFIG.server + params.uri, key;

    if (params.data !== undefined) {
        for (key in params.data) {
            if (params.data.hasOwnProperty(key)) {
                qparams.push(encodeURIComponent(key) + "=" + encodeURIComponent(params.data[key]));
            }
        }
        qstring = qparams.join("&");
    }
    if (qstring !== "") {
        uri = uri + "?" + qstring;
    }

    var doc = new XMLHttpRequest();
    doc.onreadystatechange = function () {
        var json = undefined;
        if (doc.readyState == XMLHttpRequest.HEADERS_RECEIVED) {
            console.log("HEADERS_RECEIVED");
        } else if (doc.readyState == XMLHttpRequest.DONE) {
            console.log("DONE " + doc.status);
            console.log(doc.responseText);
            if (doc.status == 200) {
                json = eval("(" + doc.responseText + ")");
                if (params.success !== undefined) {
                    params.success(json);
                }
                else {
                    // default
                    WorkerScript.sendMessage({action: params.action, success: true, response: json});
                }
            } else {
                if (doc.status == 403) {
                    try {
                        json = eval("(" + doc.responseText + ")");
                    } catch (err) {
                        // console.log("failed to parse response: " + doc.responseText);
                        // try to dig the stuff from headers instead
                        json = {"code": doc.getResponseHeader("X-Error-Code"),
                                "message": doc.getResponseHeader("X-Error-Message")};
                    }
                }
                if (params.error !== undefined) {
                    params.error(doc, json);
                } else {
                    // default error handler
                    WorkerScript.sendMessage({action: params.action, success: false, response: json});
                }
            }
        }
    }

    console.log("ajaxing to " + uri);
    doc.open("GET", uri);
    doc.send();
}

function register(playerName) {
    console.log("registering " + playerName);
    var player = {player_name: playerName};
    ajax({uri: "/ajax/player/register", data: {player: JSON.stringify(player)},
         action: "register",
         success: function (response) {
             ME.id = response.id;
             ME.akey = response.akey;
             console.log("id is " + ME.id + ", akey is " + ME.akey);
             WorkerScript.sendMessage({action: "register", success: true, response: response});
        }});
}

function quit() {
    console.log("quitting");
    ajax({uri: "/ajax/player/quit", data: {akey: ME.akey},
         action: "quit",
         success: function (response) {
             ME.id = undefined;
             ME.akey = undefined;
             WorkerScript.sendMessage({action: "quit", success: true, response: response});
        }});
}

function createGame(callback) {
    ajax({uri: "/ajax/game/create", data: {akey: ME.akey},
         action: "createGame",
         success: function (response) {
             ME.gameId = response;
             console.log("game ID " + ME.gameId);
             if (callback !== undefined) {
                 callback();
             } else {
                 WorkerScript.sendMessage({action: "createGame", success: true, response: response});
             }
         }});
}

function startGameWithBots() {
    ajax({uri: "/ajax/game/start_with_bots", data: {akey: ME.akey, game_id: ME.gameId},
         action: "startGameWithBots"});
}

function getGameInfo() {
    ajax({uri: "/ajax/game/get_info", data: {game_id: ME.gameId},
         action: "getGameInfo",
         success: function (response) {
             WorkerScript.sendMessage({action: "getGameInfo", success: true, response: response, state:ME});
         }});
}

function listGames(model, listAll) {
    ajax({uri: "/ajax/game/list",
             action: "listGames",
             success: function (response) {
                          var game;
                          //console.log("listGames: " + JSON.stringify(response));
                          model.clear();
                          for (var gameId in response) {
                              if (response.hasOwnProperty(gameId)) {
                                  // TODO: should get joinable from server
                                  game = {"gameId": gameId, "joinable": response[gameId].length < 4 };
                                  // generate a string with player names
                                  try {
                                      game.players = response[gameId].map(function (i) { return i.player_name; }).join(", ");
                                  } catch (error) {
                                      console.log("could not get players: " + error);
                                  }
                                  if (listAll || game.joinable) {
                                      model.append(game);
                                  }
                              }
                          }
                          model.sync();
                          WorkerScript.sendMessage({action: "listGames", success: true});
                      }});
}

WorkerScript.onMessage = function (message) {
    console.log("onMessage " + JSON.stringify(message));
    switch (message.action) {
    case "register":
        register(message.playerName);
        break;
    case "quit":
        quit();
        break;
    case "leaveGame":
        ajax({uri: "/ajax/game/leave", data: {akey: ME.akey, game_id: ME.gameId},
                 action: message.action,
                 success: function (json) {
                              ME.gameID = undefined;
                              WorkerScript.sendMessage({action: message.action, success: true, response: json});
                          },
                 error: function (doc, json) {
                            ME.gameID = undefined;
                            WorkerScript.sendMessage({action: message.action, success: false, response: json});
                        }
             });
        break;
    case "quickStart":
        createGame(startGameWithBots);
        break;
    case "pollEvents":
        ajax({uri: "/ajax/get_events", data: {akey: ME.akey}, action: message.action});
        break;
    case "getGameState":
        ajax({uri: "/ajax/game/get_state", data: {akey: ME.akey, game_id: ME.gameId},
             action: message.action});
        break;
    case "getGameInfo":
        getGameInfo();
        break;
    case "listGames":
        listGames(message.model, message.listAll);
        break;
    case "playCard":
        ajax({uri: "/ajax/game/play_card", data: {akey: ME.akey, game_id: ME.gameId,
             card: JSON.stringify(message.card)}, action: "playCard"});
        break;
    default:
        break;
    }
}

<commit_msg>Use JSON.parse instead of eval<commit_after>var CONFIG = {server: "http://localhost:8052"};
var ME = {id: undefined, akey: undefined};

function ajax(params) {
    var qstring = "", qparams = [];
    var uri = CONFIG.server + params.uri, key;

    if (params.data !== undefined) {
        for (key in params.data) {
            if (params.data.hasOwnProperty(key)) {
                qparams.push(encodeURIComponent(key) + "=" + encodeURIComponent(params.data[key]));
            }
        }
        qstring = qparams.join("&");
    }
    if (qstring !== "") {
        uri = uri + "?" + qstring;
    }

    var doc = new XMLHttpRequest();
    doc.onreadystatechange = function () {
        var json = undefined;
        if (doc.readyState == XMLHttpRequest.HEADERS_RECEIVED) {
            console.log("HEADERS_RECEIVED");
        } else if (doc.readyState == XMLHttpRequest.DONE) {
            console.log("DONE " + doc.status);
            console.log(doc.responseText);
            if (doc.status == 200) {
                json = JSON.parse(doc.responseText);
                if (params.success !== undefined) {
                    params.success(json);
                }
                else {
                    // default
                    WorkerScript.sendMessage({action: params.action, success: true, response: json});
                }
            } else {
                if (doc.status == 403) {
                    try {
                        json = eval("(" + doc.responseText + ")");
                    } catch (err) {
                        // console.log("failed to parse response: " + doc.responseText);
                        // try to dig the stuff from headers instead
                        json = {"code": doc.getResponseHeader("X-Error-Code"),
                                "message": doc.getResponseHeader("X-Error-Message")};
                    }
                }
                if (params.error !== undefined) {
                    params.error(doc, json);
                } else {
                    // default error handler
                    WorkerScript.sendMessage({action: params.action, success: false, response: json});
                }
            }
        }
    }

    console.log("ajaxing to " + uri);
    doc.open("GET", uri);
    doc.send();
}

function register(playerName) {
    console.log("registering " + playerName);
    var player = {player_name: playerName};
    ajax({uri: "/ajax/player/register", data: {player: JSON.stringify(player)},
         action: "register",
         success: function (response) {
             ME.id = response.id;
             ME.akey = response.akey;
             console.log("id is " + ME.id + ", akey is " + ME.akey);
             WorkerScript.sendMessage({action: "register", success: true, response: response});
        }});
}

function quit() {
    console.log("quitting");
    ajax({uri: "/ajax/player/quit", data: {akey: ME.akey},
         action: "quit",
         success: function (response) {
             ME.id = undefined;
             ME.akey = undefined;
             WorkerScript.sendMessage({action: "quit", success: true, response: response});
        }});
}

function createGame(callback) {
    ajax({uri: "/ajax/game/create", data: {akey: ME.akey},
         action: "createGame",
         success: function (response) {
             ME.gameId = response;
             console.log("game ID " + ME.gameId);
             if (callback !== undefined) {
                 callback();
             } else {
                 WorkerScript.sendMessage({action: "createGame", success: true, response: response});
             }
         }});
}

function startGameWithBots() {
    ajax({uri: "/ajax/game/start_with_bots", data: {akey: ME.akey, game_id: ME.gameId},
         action: "startGameWithBots"});
}

function getGameInfo() {
    ajax({uri: "/ajax/game/get_info", data: {game_id: ME.gameId},
         action: "getGameInfo",
         success: function (response) {
             WorkerScript.sendMessage({action: "getGameInfo", success: true, response: response, state:ME});
         }});
}

function listGames(model, listAll) {
    ajax({uri: "/ajax/game/list",
             action: "listGames",
             success: function (response) {
                          var game;
                          //console.log("listGames: " + JSON.stringify(response));
                          model.clear();
                          for (var gameId in response) {
                              if (response.hasOwnProperty(gameId)) {
                                  // TODO: should get joinable from server
                                  game = {"gameId": gameId, "joinable": response[gameId].length < 4 };
                                  // generate a string with player names
                                  try {
                                      game.players = response[gameId].map(function (i) { return i.player_name; }).join(", ");
                                  } catch (error) {
                                      console.log("could not get players: " + error);
                                  }
                                  if (listAll || game.joinable) {
                                      model.append(game);
                                  }
                              }
                          }
                          model.sync();
                          WorkerScript.sendMessage({action: "listGames", success: true});
                      }});
}

WorkerScript.onMessage = function (message) {
    console.log("onMessage " + JSON.stringify(message));
    switch (message.action) {
    case "register":
        register(message.playerName);
        break;
    case "quit":
        quit();
        break;
    case "leaveGame":
        ajax({uri: "/ajax/game/leave", data: {akey: ME.akey, game_id: ME.gameId},
                 action: message.action,
                 success: function (json) {
                              ME.gameID = undefined;
                              WorkerScript.sendMessage({action: message.action, success: true, response: json});
                          },
                 error: function (doc, json) {
                            ME.gameID = undefined;
                            WorkerScript.sendMessage({action: message.action, success: false, response: json});
                        }
             });
        break;
    case "quickStart":
        createGame(startGameWithBots);
        break;
    case "pollEvents":
        ajax({uri: "/ajax/get_events", data: {akey: ME.akey}, action: message.action});
        break;
    case "getGameState":
        ajax({uri: "/ajax/game/get_state", data: {akey: ME.akey, game_id: ME.gameId},
             action: message.action});
        break;
    case "getGameInfo":
        getGameInfo();
        break;
    case "listGames":
        listGames(message.model, message.listAll);
        break;
    case "playCard":
        ajax({uri: "/ajax/game/play_card", data: {akey: ME.akey, game_id: ME.gameId,
             card: JSON.stringify(message.card)}, action: "playCard"});
        break;
    default:
        break;
    }
}

<|endoftext|>


<commit_before>import { typeOf } from '@ember/utils';
import en from "ilios-common/locales/en/translations";
import fr from "ilios-common/locales/fr/translations";
import es from "ilios-common/locales/es/translations";

export function initialize(app) {
  const intl = app.lookup('service:intl');
  intl.addTranslations("en", withFlattenedKeys(en));
  intl.addTranslations("fr", withFlattenedKeys(fr));
  intl.addTranslations("es", withFlattenedKeys(es));
}

function withFlattenedKeys(object) {
  const result = {};

  Object.keys(object).forEach(function(key) {
    var value = object[key];

    if (typeOf(value) === 'object') {
      value = withFlattenedKeys(value);

      Object.keys(value).forEach(function(suffix) {
        result[`${key}.${suffix}`] = value[suffix];
      });
    } else {
      result[key] = value;
    }
  });

  return result;
}

export default {
  name: 'load-common-translations',
  initialize
};
<commit_msg>rm obsolete initializer.<commit_after><|endoftext|>


<commit_before>/*jslint indent: 2 */
/*global $: false, document: false, togglbutton: false, createTag:false*/

'use strict';

togglbutton.render('.window-header:not(.toggl)', {observe: true}, function (elem) {
  var link, container = createTag('div', 'button-link trello-tb-wrapper'),
    duration,
    trackedContainer = createTag('div', 'toggl-tracked'),
    trackedElem = $('.other-actions'),
    titleElem = $('.window-title-text', elem),
    projectElem = $('.board-header > a'),
    descriptionElem = $('.js-move-card');

  link = togglbutton.createTimerLink({
    className: 'trello',
    description: titleElem.innerText,
    projectName: projectElem.innerText,
    calculateTotal: true
  });

  container.appendChild(link);
  descriptionElem.parentNode.insertBefore(container, descriptionElem);

  // Add Tracked time text
  duration = togglbutton.calculateTrackedTime(titleElem.innerText);
  trackedContainer.innerHTML = "
Time tracked

" + duration + "
";
  trackedElem.parentNode.insertBefore(trackedContainer, trackedElem);
});

/* Checklist buttons */
togglbutton.render('.checklist-item-details:not(.toggl)', {observe: true}, function (elem) {
  var link,
    projectElem = $('.board-header > a'),
    titleElem = $('.window-title-text'),
    taskElem = $('.checklist-item-details-text', elem);

  link = togglbutton.createTimerLink({
    className: 'trello',
    buttonType: 'minimal',
    projectName: projectElem.innerText,
    description: titleElem.innerText + ' - ' + taskElem.innerText,
  });

  link.classList.add('checklist-item-button');
  elem.parentNode.appendChild(link);
});
<commit_msg>Fixed copied card issue in trello, closes #429<commit_after>/*jslint indent: 2 */
/*global $: false, document: false, togglbutton: false, createTag:false*/

'use strict';

togglbutton.render('.window-header:not(.toggl)', {observe: true}, function (elem) {
  var link, container = createTag('div', 'button-link trello-tb-wrapper'),
    duration,
    descFunc,
    titleElem = $('.window-title-text', elem),
    trackedContainer = createTag('div', 'toggl-tracked'),
    trackedElem = $('.other-actions'),
    projectElem = $('.board-header > a'),
    descriptionElem = $('.js-move-card');

  descFunc = function () {
    return titleElem.innerText;
  };

  link = togglbutton.createTimerLink({
    className: 'trello',
    description: descFunc,
    projectName: projectElem.innerText,
    calculateTotal: true
  });

  container.appendChild(link);
  descriptionElem.parentNode.insertBefore(container, descriptionElem);

  // Add Tracked time text
  duration = togglbutton.calculateTrackedTime(titleElem.innerText);
  trackedContainer.innerHTML = "
Time tracked

" + duration + "
";
  trackedElem.parentNode.insertBefore(trackedContainer, trackedElem);
});

/* Checklist buttons */
togglbutton.render('.checklist-item-details:not(.toggl)', {observe: true}, function (elem) {
  var link,
    projectElem = $('.board-header > a'),
    titleElem = $('.window-title-text'),
    taskElem = $('.checklist-item-details-text', elem);

  link = togglbutton.createTimerLink({
    className: 'trello',
    buttonType: 'minimal',
    projectName: projectElem.innerText,
    description: titleElem.innerText + ' - ' + taskElem.innerText,
  });

  link.classList.add('checklist-item-button');
  elem.parentNode.appendChild(link);
});
<|endoftext|>


<commit_before>module.exports = function(send, ctrl) {

  this.send = send;

  var anaMoveTimeout;
  var anaDestsTimeout;

  var anaDestsCache = {};

  var handlers = {
    step: function(data) {
      ctrl.addStep(data.step, data.path);
      clearTimeout(anaMoveTimeout);
    },
    stepFailure: function(data) {
      console.log(data);
      clearTimeout(anaMoveTimeout);
      ctrl.reset();
    },
    dests: function(data) {
      anaDestsCache[data.path] = data;
      ctrl.addDests(data.dests, data.path);
      clearTimeout(anaDestsTimeout);
    },
    destsFailure: function(data) {
      console.log(data);
      clearTimeout(anaDestsTimeout);
    }
  };

  this.receive = function(type, data) {
    if (handlers[type]) {
      handlers[type](data);
      return true;
    }
    return false;
  }.bind(this);

  this.sendAnaMove = function(req) {
    withoutStandardVariant(req);
    this.send('anaMove', req);
    anaMoveTimeout = setTimeout(this.sendAnaMove.bind(this, req), 3000);
  }.bind(this);

  this.sendAnaDests = function(req) {
    withoutStandardVariant(req);
    if (anaDestsCache[req.path]) return handlers.dest(anaDestsCache[req.path]);
    this.send('anaDests', req);
    anaDestsTimeout = setTimeout(this.sendAnaDests.bind(this, req), 3000);
  }.bind(this);

  this.sendForecasts = function(req) {
    this.send('forecasts', req);
  }.bind(this);

  var withoutStandardVariant = function(obj) {
    if (obj.variant === 'standard') delete obj.variant;
  };
}
<commit_msg>prevent analysis socket flood<commit_after>module.exports = function(send, ctrl) {

  this.send = send;

  var anaMoveTimeout;
  var anaDestsTimeout;

  var anaDestsCache = {};

  var handlers = {
    step: function(data) {
      ctrl.addStep(data.step, data.path);
      clearTimeout(anaMoveTimeout);
    },
    stepFailure: function(data) {
      console.log(data);
      clearTimeout(anaMoveTimeout);
      ctrl.reset();
    },
    dests: function(data) {
      anaDestsCache[data.path] = data;
      ctrl.addDests(data.dests, data.path);
      clearTimeout(anaDestsTimeout);
    },
    destsFailure: function(data) {
      console.log(data);
      clearTimeout(anaDestsTimeout);
    }
  };

  this.receive = function(type, data) {
    if (handlers[type]) {
      handlers[type](data);
      return true;
    }
    return false;
  }.bind(this);

  this.sendAnaMove = function(req) {
    clearTimeout(anaMoveTimeout);
    withoutStandardVariant(req);
    this.send('anaMove', req);
    anaMoveTimeout = setTimeout(this.sendAnaMove.bind(this, req), 3000);
  }.bind(this);

  this.sendAnaDests = function(req) {
    clearTimeout(anaDestsTimeout);
    withoutStandardVariant(req);
    if (anaDestsCache[req.path]) return handlers.dest(anaDestsCache[req.path]);
    this.send('anaDests', req);
    anaDestsTimeout = setTimeout(this.sendAnaDests.bind(this, req), 3000);
  }.bind(this);

  this.sendForecasts = function(req) {
    this.send('forecasts', req);
  }.bind(this);

  var withoutStandardVariant = function(obj) {
    if (obj.variant === 'standard') delete obj.variant;
  };
}
<|endoftext|>


<commit_before>
var svg = document.querySelector('svg');

function color_css2rgb(css) {
  css = css.toLowerCase();

  if (!css.match(/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/)) {
    return;
  }

  css = css.replace(/^#/,'');

  var bytes = css.length/3;
  var max = Math.pow(16, bytes) - 1;
  return [
    Math.round(255 * parseInt(css.slice(0, bytes), 16) / max),
    Math.round(255 * parseInt(css.slice(bytes * 1,bytes * 2), 16) / max),
    Math.round(255 * parseInt(css.slice(bytes * 2), 16) / max)
  ];
}

function stopsToCoons(stops) {
  var cursor = [0,0];
  var coons = [];
  coons.push(cursor.slice(0));
  var colors = [];

  for(var si=0; si<stops.length; si++) {
    var stop = stops[si];

    var path = stop.getAttribute('path').split(/\s+/);
    console.assert(path.length === 4);
    for(var i=1; i<4; i++) {
      var coord = path[i].split(',');
      var dx = parseInt(coord[0]);
      var dy = parseInt(coord[1]);
      coons.push([cursor[0]+dx, cursor[1]+dy]);
      if(i === 3) {
        cursor[0] += dx;
        cursor[1] += dy;
      }
    }

    var pairs = stop.getAttribute('style').split(';');
    var stopColor='#000000', stopOpacity=1;
    for(var pi=0; pi<pairs.length; pi++) {
      var pair = pairs[pi].split(':');
      if(pair[0] === 'stop-color') {
        stopColor = pair[1];
      } else if(pair[0] === 'stop-opacity') {
        stopOpacity = parseInt(pair[1]);
      }
    }
    var rgba = color_css2rgb(stopColor);
    rgba[3] = Math.round(255 * stopOpacity);
    colors.push(rgba);
  }

  coons.pop(); // The first point gets added twice, because it's a closed loop

  return {coons:coons,colors:colors};
}

function getMeshGradientAABB(patchData) {
  var xmin = Infinity;
  var xmax = -Infinity;
  var ymin = Infinity;
  var ymax = -Infinity;

  for(var i=0; i<patchData.length; i++) {
    var data = patchData[i];
    for(var j=0; j<data.coons.length; j++) {
      var point = data.coons[j];
      xmin = Math.min(xmin, point[0]);
      xmax = Math.max(xmax, point[0]);
      ymin = Math.min(ymin, point[1]);
      ymax = Math.max(ymax, point[1]);
    }
  }

  return {xmin:xmin, ymin:ymin, xmax:xmax, ymax:ymax};
}

function setClip(ctx, pathdata) {
  ctx.beginPath();
  var to, cp1, cp2;
  var cursor = [0,0];
  var parts = pathdata.split(' ');
  var cmd;
  console.log(parts);
  for(var i=0; i<parts.length; ) {
    if(parts[i].indexOf(',') < 0) {
      cmd = parts[i];
      i++;
    } else {
      // Otherwise we continue to assume same command for further tokens
    }
    switch(cmd) {
      case 'm':
        to = parts[i++].split(',');
        cursor[0] += parseFloat(to[0]);
        cursor[1] += parseFloat(to[1]);
        ctx.moveTo(cursor[0], cursor[1]);
        break;
      case 'c':
        cp1 = parts[i++].split(',');
        cp2 = parts[i++].split(',');
        to = parts[i++].split(',');
        ctx.bezierCurveTo(
          cursor[0]+parseFloat(cp1[0]), cursor[1]+parseFloat(cp1[1]),
          cursor[0]+parseFloat(cp2[0]), cursor[1]+parseFloat(cp2[1]),
          cursor[0]+parseFloat(to[0]), cursor[1]+parseFloat(to[1])
        );
        cursor[0] += parseFloat(to[0]);
        cursor[1] += parseFloat(to[1]);
        break;
      case 'M':
        to = parts[i++].split(',');
        cursor[0] = parseFloat(to[0]);
        cursor[1] = parseFloat(to[1]);
        ctx.moveTo(cursor[0], cursor[1]);
        break;
      case 'C':
        cp1 = parts[i++].split(',');
        cp2 = parts[i++].split(',');
        to = parts[i++].split(',');
        ctx.bezierCurveTo(
          parseFloat(cp1[0]), parseFloat(cp1[1]),
          parseFloat(cp2[0]), parseFloat(cp2[1]),
          parseFloat(to[0]), parseFloat(to[1])
        );
        cursor[0] = parseFloat(to[0]);
        cursor[1] = parseFloat(to[1]);
        break;
      case 'z':
      case 'Z':
        break;
      default:
        console.log('unexpected token', parts[i]);
        i++;
        break;
    }
  }
  ctx.clip();
}

function meshGradToImg(patchData, mgx, mgy, elem) {

  var aabb = getMeshGradientAABB(patchData);
  var width = aabb.xmax-aabb.xmin;
  var height = aabb.ymax-aabb.ymin;

  var canvas = document.createElementNS('http://www.w3.org/1999/xhtml','canvas');
  canvas.width = width;
  canvas.height = height;
  var ctx = canvas.getContext('2d');
  var imgdata = ctx.getImageData(0,0,width,height);

  // Render mesh gradient
  for(var i=0; i<patchData.length; i++) {
    var data = patchData[i];
    draw_bezier_patch(
      imgdata.data, width,height,
      interpolateCoons(data.coons), data.colors);
  }

  ctx.putImageData(imgdata, 0,0);

  // Set clip
  var target = document.createElementNS('http://www.w3.org/1999/xhtml','canvas');
  target.width = width;
  target.height = height;
  var tctx = target.getContext('2d');
  if(elem.tagName === 'path') {
    setClip(tctx, elem.getAttribute('d'));
    tctx.drawImage(canvas, 0,0, width, height);
  } else if(elem.tagName === 'ellipse') {
    target = canvas;
    console.warn('todo');
  } else {
    target = canvas;
    console.warn('todo', elem.tagName);
  }

  // convert canvas to image element
  var img = document.createElementNS('http://www.w3.org/2000/svg','image');
  img.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', target.toDataURL());
  img.setAttribute('x', mgx);
  img.setAttribute('y', mgy);
  img.setAttribute('width',''+width);
  img.setAttribute('height',''+height);
  return img;
}

function searchForMeshGrads(node, callback) {

  var style = node.getAttribute('style');
  if(style) {
    var pairs = style.split(';');
    for(var j=0; j<pairs.length; j++) {
      var pair = pairs[j];
      var value = pair.split(':')[1];
      var result = /url\(#(meshGradient\d+)\)/.exec(value);
      if(result && result.length > 1) {
        callback(node, result[1]);
      }
    }
  }

  for(var i=0; i<node.children.length; i++) {
    var child = node.children[i];
    searchForMeshGrads(child, callback);
  }
}

function removeFill(node) {
  var style = node.getAttribute('style');
  var pairs = style.split(';');
  var newpairs = [];
  for(var j=0; j<pairs.length; j++) {
    var pair = pairs[j];
    if(!pair.startsWith('fill')) {
      newpairs.push(pair);
    }
  }
  newpairs.push('fill:none');
  node.setAttribute('style', newpairs.join(';'));
}

function replaceElements(mgmap) {
  var keys = Object.keys(mgmap);
  searchForMeshGrads(svg, function (elem, mgid) {
    console.log('replacing for', elem.getAttribute('id'), mgid);
    if(keys.indexOf(mgid) >= 0) {
      var mgdata = mgmap[mgid];
      var img = meshGradToImg(mgdata.patchData, mgdata.x, mgdata.y, elem);
      svg.insertBefore(img, elem);
      removeFill(elem);
    }
  });
}

function run() {
  var meshGradMap = {};
  var meshGradients = document.querySelectorAll('meshGradient');
  for(var i=0; i<meshGradients.length; i++) {
    var patchData = [];
    var mg = meshGradients[i];
    var mgid = mg.getAttribute('id');
    var mgx = mg.getAttribute('x');
    var mgy = mg.getAttribute('y');
    var rows = mg.querySelectorAll('meshRow');
    for(var j=0; j<rows.length; j++) {
      var row = rows[j];
      var patches = row.querySelectorAll('meshPatch');
      for(var k=0; k<patches.length; k++) {
        var patch = patches[k];
        var stops = patch.querySelectorAll('stop');
        if(j === 0 && k === 0) {
          console.assert(stops.length === 4);
          var data = stopsToCoons(stops);
          patchData.push(data);
        } else if(j === 0 && k !== 0) {
          console.assert(stops.length === 2);
        } else if(j !== 0 && k === 0) {
          console.assert(stops.length === 2);
        } else if(j !== 0 && k !== 0) {
          console.assert(stops.length === 1);
        }
      }
    }
    meshGradMap[mgid] = {patchData:patchData, x:mgx,y:mgy};
  }

  replaceElements(meshGradMap);
}

run();

<commit_msg>Fix the offset issue<commit_after>
var svg = document.querySelector('svg');

function color_css2rgb(css) {
  css = css.toLowerCase();

  if (!css.match(/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/)) {
    return;
  }

  css = css.replace(/^#/,'');

  var bytes = css.length/3;
  var max = Math.pow(16, bytes) - 1;
  return [
    Math.round(255 * parseInt(css.slice(0, bytes), 16) / max),
    Math.round(255 * parseInt(css.slice(bytes * 1,bytes * 2), 16) / max),
    Math.round(255 * parseInt(css.slice(bytes * 2), 16) / max)
  ];
}

function stopsToCoons(stops) {
  var cursor = [0,0];
  var coons = [];
  coons.push(cursor.slice(0));
  var colors = [];

  for(var si=0; si<stops.length; si++) {
    var stop = stops[si];

    var path = stop.getAttribute('path').split(/\s+/);
    console.assert(path.length === 4);
    for(var i=1; i<4; i++) {
      var coord = path[i].split(',');
      var dx = parseInt(coord[0]);
      var dy = parseInt(coord[1]);
      coons.push([cursor[0]+dx, cursor[1]+dy]);
      if(i === 3) {
        cursor[0] += dx;
        cursor[1] += dy;
      }
    }

    var pairs = stop.getAttribute('style').split(';');
    var stopColor='#000000', stopOpacity=1;
    for(var pi=0; pi<pairs.length; pi++) {
      var pair = pairs[pi].split(':');
      if(pair[0] === 'stop-color') {
        stopColor = pair[1];
      } else if(pair[0] === 'stop-opacity') {
        stopOpacity = parseInt(pair[1]);
      }
    }
    var rgba = color_css2rgb(stopColor);
    rgba[3] = Math.round(255 * stopOpacity);
    colors.push(rgba);
  }

  coons.pop(); // The first point gets added twice, because it's a closed loop

  return {coons:coons,colors:colors};
}

function getMeshGradientAABB(patchData) {
  var xmin = Infinity;
  var xmax = -Infinity;
  var ymin = Infinity;
  var ymax = -Infinity;

  for(var i=0; i<patchData.length; i++) {
    var data = patchData[i];
    for(var j=0; j<data.coons.length; j++) {
      var point = data.coons[j];
      xmin = Math.min(xmin, point[0]);
      xmax = Math.max(xmax, point[0]);
      ymin = Math.min(ymin, point[1]);
      ymax = Math.max(ymax, point[1]);
    }
  }

  return {xmin:xmin, ymin:ymin, xmax:xmax, ymax:ymax};
}

function setClip(ctx, pathdata, offset) {
  ctx.beginPath();
  var to, cp1, cp2;
  var cursor = [0,0];
  var parts = pathdata.split(' ');
  var cmd;
  console.log(parts);
  for(var i=0; i<parts.length; ) {
    if(parts[i].indexOf(',') < 0) {
      cmd = parts[i];
      i++;
    } else {
      // Otherwise we continue to assume same command for further tokens
    }
    switch(cmd) {
      case 'm':
        to = parts[i++].split(',');
        cursor[0] += parseFloat(to[0]);
        cursor[1] += parseFloat(to[1]);
        cursor[0] -= offset[0];
        cursor[1] -= offset[1];
        ctx.moveTo(cursor[0], cursor[1]);
        break;
      case 'c':
        cp1 = parts[i++].split(',');
        cp2 = parts[i++].split(',');
        to = parts[i++].split(',');
        ctx.bezierCurveTo(
          cursor[0]+parseFloat(cp1[0]), cursor[1]+parseFloat(cp1[1]),
          cursor[0]+parseFloat(cp2[0]), cursor[1]+parseFloat(cp2[1]),
          cursor[0]+parseFloat(to[0]), cursor[1]+parseFloat(to[1])
        );
        cursor[0] += parseFloat(to[0]);
        cursor[1] += parseFloat(to[1]);
        break;
      case 'M':
        to = parts[i++].split(',');
        cursor[0] = parseFloat(to[0]);
        cursor[1] = parseFloat(to[1]);
        ctx.moveTo(cursor[0], cursor[1]);
        break;
      case 'C':
        cp1 = parts[i++].split(',');
        cp2 = parts[i++].split(',');
        to = parts[i++].split(',');
        ctx.bezierCurveTo(
          parseFloat(cp1[0]), parseFloat(cp1[1]),
          parseFloat(cp2[0]), parseFloat(cp2[1]),
          parseFloat(to[0]), parseFloat(to[1])
        );
        cursor[0] = parseFloat(to[0]);
        cursor[1] = parseFloat(to[1]);
        break;
      case 'z':
      case 'Z':
        break;
      default:
        console.log('unexpected token', parts[i]);
        i++;
        break;
    }
  }
  ctx.clip();
}

function meshGradToImg(patchData, mgx, mgy, elem) {

  var aabb = getMeshGradientAABB(patchData);
  var width = aabb.xmax-aabb.xmin;
  var height = aabb.ymax-aabb.ymin;

  var canvas = document.createElementNS('http://www.w3.org/1999/xhtml','canvas');
  canvas.width = width;
  canvas.height = height;
  var ctx = canvas.getContext('2d');
  var imgdata = ctx.getImageData(0,0,width,height);

  // Render mesh gradient
  for(var i=0; i<patchData.length; i++) {
    var data = patchData[i];
    draw_bezier_patch(
      imgdata.data, width,height,
      interpolateCoons(data.coons), data.colors);
  }

  ctx.putImageData(imgdata, 0,0);

  // Set clip
  var target = document.createElementNS('http://www.w3.org/1999/xhtml','canvas');
  target.width = width;
  target.height = height;
  var tctx = target.getContext('2d');
  if(elem.tagName === 'path') {
    setClip(tctx, elem.getAttribute('d'), [parseInt(mgx), parseInt(mgy)]);
    tctx.drawImage(canvas, 0,0, width, height);
  } else if(elem.tagName === 'ellipse') {
    target = canvas;
    console.warn('todo');
  } else {
    target = canvas;
    console.warn('todo', elem.tagName);
  }

  // convert canvas to image element
  var img = document.createElementNS('http://www.w3.org/2000/svg','image');
  img.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', target.toDataURL());
  img.setAttribute('x', mgx);
  img.setAttribute('y', mgy);
  img.setAttribute('width',''+width);
  img.setAttribute('height',''+height);
  return img;
}

function searchForMeshGrads(node, callback) {

  var style = node.getAttribute('style');
  if(style) {
    var pairs = style.split(';');
    for(var j=0; j<pairs.length; j++) {
      var pair = pairs[j];
      var value = pair.split(':')[1];
      var result = /url\(#(meshGradient\d+)\)/.exec(value);
      if(result && result.length > 1) {
        callback(node, result[1]);
      }
    }
  }

  for(var i=0; i<node.children.length; i++) {
    var child = node.children[i];
    searchForMeshGrads(child, callback);
  }
}

function removeFill(node) {
  var style = node.getAttribute('style');
  var pairs = style.split(';');
  var newpairs = [];
  for(var j=0; j<pairs.length; j++) {
    var pair = pairs[j];
    if(!pair.startsWith('fill')) {
      newpairs.push(pair);
    }
  }
  newpairs.push('fill:none');
  node.setAttribute('style', newpairs.join(';'));
}

function replaceElements(mgmap) {
  var keys = Object.keys(mgmap);
  searchForMeshGrads(svg, function (elem, mgid) {
    console.log('replacing for', elem.getAttribute('id'), mgid);
    if(keys.indexOf(mgid) >= 0) {
      var mgdata = mgmap[mgid];
      var img = meshGradToImg(mgdata.patchData, mgdata.x, mgdata.y, elem);
      svg.insertBefore(img, elem);
      removeFill(elem);
    }
  });
}

function run() {
  var meshGradMap = {};
  var meshGradients = document.querySelectorAll('meshGradient');
  for(var i=0; i<meshGradients.length; i++) {
    var patchData = [];
    var mg = meshGradients[i];
    var mgid = mg.getAttribute('id');
    var mgx = mg.getAttribute('x');
    var mgy = mg.getAttribute('y');
    var rows = mg.querySelectorAll('meshRow');
    for(var j=0; j<rows.length; j++) {
      var row = rows[j];
      var patches = row.querySelectorAll('meshPatch');
      for(var k=0; k<patches.length; k++) {
        var patch = patches[k];
        var stops = patch.querySelectorAll('stop');
        if(j === 0 && k === 0) {
          console.assert(stops.length === 4);
          var data = stopsToCoons(stops);
          patchData.push(data);
        } else if(j === 0 && k !== 0) {
          console.assert(stops.length === 2);
        } else if(j !== 0 && k === 0) {
          console.assert(stops.length === 2);
        } else if(j !== 0 && k !== 0) {
          console.assert(stops.length === 1);
        }
      }
    }
    meshGradMap[mgid] = {patchData:patchData, x:mgx,y:mgy};
  }

  replaceElements(meshGradMap);
}

run();

<|endoftext|>


<commit_before>/*
	vec Question Format
	一个问题的集合称为 结构
	结构是一个有序的数组，每个元素称为题素

	题素由描述(description)和答案集(question)组成

	目前有三种类型的题素，单选题素，多选题素，描述题素

	单选题素和多选题素都是数组类型（区别在于对象属性是'question'和'questions'
	描述题素则由字符串'why'标识

	可选题素中可以有一个或者多个答案元（define)，通常是字符串类型，但是也可以使用数组类型，我们称为复合答案元
	复合答案元由描述字和类型组成，目前只支持 why 类型（也就是描述题素）

	vqfs游标
	vqfc游标
*/
class VQFChoice {
	onChoice(){
		this.choicePool.forEach((f, cursor, choicePool) => {
			f(cursor, choicePool);
		});
	}
	backChecked(){
		if (this.isMulti) {
			return this.elePool.filter(eleObj => {
				return eleObj.choiceEle.checked === true;
			});
		} else {
			return this.elePool.find(eleObj => {
				if (eleObj.choiceEle.checked) {
					return eleObj.choiceEle.checked;
				}
			})
		}
	}
	addChecked(vqfcCursor, element){
		// vqfContent.clearChecked();
		this.checked = true;
		this.checkedCursor = vqfcCursor;
		this.current = element;
	}
	removeChecked(vqfcCursor, element){
		// vqfContent.clearChecked();
		delete this.checked;
		delete this.checkedCursor;
		delete this.current;
	}
	/**
	* @param define 定义元
	* @param parent 父元素（class^="vqfs-")
	* @param name 是vqfs-xxx中的"xxx"
	* @param vqfcCursor
	*/
	add(define, parent, name, vqfcCursor, vqfContent){
		let label = document.createElement('label');
		let choiceEle = document.createElement('input');
		let description = document.createElement('span');

		choiceEle.vqfChoice = this;

		const element = {
			label,
			choiceEle,
			description,
			vqfcCursor
		};

		if (this.isMulti) {
			choiceEle.type = 'checkbox';
		} else {
			choiceEle.type = 'radio';
		}

		choiceEle.addEventListener('click', (e) => {
			if (choiceEle.checked) {
				this.addChecked(vqfcCursor, element);
			} else {
				this.removeChecked(vqfcCursor, element);
			}

			this.onChoice(e);
		});

		choiceEle.name = `vqfc${vqfContent.layerPrefix}-${name}`;

		$(label).append(choiceEle);
		$(label).append(description);

		if (parent) {
			$(parent).append(label);
		}

		if (typeof(define) === 'object') {
			$(description).text(define.description);
			if (define.type === 'why') {
				let why = document.createElement('textarea');
				element.why = why;
				$(parent).append(why);
			} else if (define.type === 'choice') {

			} else {
				console.error(define);
				throw new Error(`无效的复合单元(unknown type "${define.type})"`);
			}

			if (define.extends !== undefined) {
				console.warn(define);
				class VQFExtends extends VQF {
					exLoad(defineExtends){
						if (Array.isArray(defineExtends)) {
							this.load(defineExtends);
						} else if (typeof(defineExtends) === 'object') {
							this.load([defineExtends]);
						} else {
							throw new Error('扩展题素既不是数组也不是对象');
						}
					}
				}
				const vqfExtends = new VQFExtends;
				vqfExtends.parentVqf = vqfContent;
				vqfExtends.layerCursor = vqfContent.layerCursor + 1;
				vqfExtends.initLayer();
				const vqfExtendsEle = document.createElement('div');
				vqfExtendsEle.classList.add('extends');
				vqfExtends.bind(vqfExtendsEle);
				vqfExtends.exLoad(define.extends);
				vqfExtends.makeHTML();

				element.extends = vqfExtends;
				this.extends = vqfExtends;

				$(parent).append(vqfExtendsEle);
			}

		} else {
			$(description).text(define);
		}
		this.elePool.push(element);
		return element;
	}
	remove(){

	}
	constructor(isMulti){
		this.type = 'choice';
		this.choicePool = [];
		this.isMulti = isMulti;
		this.elePool = [];
	}
}
class WhyDefine {
	setElementProperty(ele=this.ele){
		Object.defineProperty(this, 'value', {
			get(){ return this.ele.value },
			set(text){ this.ele.value = text},
		});
	}
	constructor(ele=document.createElement('textarea')){
		this.type = 'why';
		this.ele = ele;
		this.setElementProperty();
	}
}
class VQF {
	/**
	* @param layerPrefix 层前缀
	*/
	constructor(layerPrefix){
		this.checkPool = [];
		this.pool = [];
		this.layerCursor = 0;
		this.initLayer();
	}
	initLayer(){
		this.layerPrefix = `-${this.layerCursor}`;
	}

	fetchCheck(){
		this.checkPool.forEach((fn, cursor, pool) => {
			fn(cursor, pool);
		});
	}

	makeQuestion(question, parent, vqfsCursor, isMulti){
		let choice = new VQFChoice(isMulti);
		question.forEach((define, vqfcCursor) => {
			// console.warn(define);
			choice.add(define, parent, vqfsCursor, vqfcCursor, this);
		});
		choice.choicePool.push((fnOffset, choicePool) => {
			this.fetchCheck(fnOffset, choicePool);
		});
		this.pool.push(choice);
	}
	makeStructItemHTML(structItem, cursor, struct){
		let structElement = document.createElement('div');
		let question = document.createElement('div');

		question.classList.add('description');

		structElement.id = `vqfs-${cursor}`;
		structElement.setAttribute('layer', this.layerCursor);

		$(question).text(structItem.description);
		$(structElement).append(question);

		if (typeof(structItem) === 'object') {
			let vThis = this;
			this.questionTypeRouter(structItem, {
				single(){
					vThis.makeQuestion.apply(vThis, [structItem['question'], structElement, cursor, false]);
				},
				multi(){
					vThis.makeQuestion.apply(vThis, [structItem['questions'], structElement, cursor, true]);
				},
				why(){
					let why = new WhyDefine();
					$(question).append(why.ele);

					vThis.pool.push(why);
				},
			});
		}

		$(this.ele).append(structElement);
	}
	bind(ele){
		this.ele = ele;
	}
	makeHTML(){
		this.struct.forEach((question, cursor, struct) => {
			this.makeStructItemHTML(question, cursor, struct);
		});
	}
	load(value){
		this.value = value;
		try {
			if (typeof(value) === 'string') {
				this.struct = JSON.parse(this.value);
			} else {
				this.struct = value;
			}
		} catch (e) {
			let err = new Error('vqf数据解析错误');
			console.error(err);
			throw err;
		}
	}

	questionTypeRouter(structItem, cbObj){
		if (Array.isArray(structItem['question'])) {
			return cbObj.single(structItem);
		} else if (Array.isArray(structItem['questions'])) {
			return cbObj.multi(structItem);
		} else if (structItem['question'].type === 'why') {
			return cbObj.why(structItem);
		} else {
			console.error(structItem);
			throw new Error('该题素不能被正确解析');
		}
	}

	collect(){
		let limit = 8;
		const formItem = $('#collect > [id^="vqfs"]');
		var collectData = (currentVqf) => {
			return currentVqf.pool.map((choice, vqfsCursor) => {
				const defineQuestion = {};
				if (choice.isMulti && choice.type === 'choice') {
					return choice.backChecked().map(eleObj => {
						const defineQuestion = {
							choiced: eleObj.vqfcCursor,
						};
						if (eleObj.why) {
							defineQuestion.why = eleObj.why.value;
						}
						if (eleObj.extends) {
							defineQuestion.extends = collectData(eleObj.extends);
						}
						return defineQuestion;
					});
				}
				/* 检查单选项是否存在 */
				else if (!choice.isMulti && choice.type === 'choice') {
					const currentChoice = choice.elePool[choice.checkedCursor];
					if (currentChoice === undefined) {
						console.warn(choice.elePool, choice.checkedCursor, choice.elePool[choice.checkedCursor]);
						throw new Error('存在未填项');
					}
					if (currentChoice.why) {
						defineQuestion.why = currentChoice.why.value;
					}

					if (typeof choice.checkedCursor === "number" &&
						isFinite(choice.checkedCursor) &&
						Math.floor(choice.checkedCursor) === choice.checkedCursor
					){
						defineQuestion.choiced = choice.checkedCursor;
					} else {
						throw new Error('存在未填项');
					}

					/* 是否存在Extends */
					if (currentChoice.extends) {
						defineQuestion.extends = collectData(currentChoice.extends);
					}

					return defineQuestion;

				} else if (choice.type === 'why') {
					return {why: choice.value};
				} else {
					throw new Error(`位置的定义类型(${choice.type})`);
				}
			});
		};
		const collectDataStruct = collectData(this);
		console.warn(collectDataStruct);
		return collectDataStruct;
	}
}
<commit_msg>支持 placeholder<commit_after>/*
	vec Question Format
	一个问题的集合称为 结构
	结构是一个有序的数组，每个元素称为题素

	题素由描述(description)和答案集(question)组成

	目前有三种类型的题素，单选题素，多选题素，描述题素

	单选题素和多选题素都是数组类型（区别在于对象属性是'question'和'questions'
	描述题素则由字符串'why'标识

	可选题素中可以有一个或者多个答案元（define)，通常是字符串类型，但是也可以使用数组类型，我们称为复合答案元
	复合答案元由描述字和类型组成，目前只支持 why 类型（也就是描述题素）

	vqfs游标
	vqfc游标
*/
class VQFChoice {
	onChoice(){
		this.choicePool.forEach((f, cursor, choicePool) => {
			f(cursor, choicePool);
		});
	}
	backChecked(){
		if (this.isMulti) {
			return this.elePool.filter(eleObj => {
				return eleObj.choiceEle.checked === true;
			});
		} else {
			return this.elePool.find(eleObj => {
				if (eleObj.choiceEle.checked) {
					return eleObj.choiceEle.checked;
				}
			})
		}
	}
	addChecked(vqfcCursor, element){
		// vqfContent.clearChecked();
		this.checked = true;
		this.checkedCursor = vqfcCursor;
		this.current = element;
	}
	removeChecked(vqfcCursor, element){
		// vqfContent.clearChecked();
		delete this.checked;
		delete this.checkedCursor;
		delete this.current;
	}
	/**
	* @param define 定义元
	* @param parent 父元素（class^="vqfs-")
	* @param name 是vqfs-xxx中的"xxx"
	* @param vqfcCursor
	*/
	add(define, parent, name, vqfcCursor, vqfContent){
		let label = document.createElement('label');
		let choiceEle = document.createElement('input');
		let description = document.createElement('span');

		choiceEle.vqfChoice = this;

		const element = {
			label,
			choiceEle,
			description,
			vqfcCursor
		};

		if (this.isMulti) {
			choiceEle.type = 'checkbox';
		} else {
			choiceEle.type = 'radio';
		}

		choiceEle.addEventListener('click', (e) => {
			if (choiceEle.checked) {
				this.addChecked(vqfcCursor, element);
			} else {
				this.removeChecked(vqfcCursor, element);
			}

			this.onChoice(e);
		});

		choiceEle.name = `vqfc${vqfContent.layerPrefix}-${name}`;

		$(label).append(choiceEle);
		$(label).append(description);

		if (parent) {
			$(parent).append(label);
		}

		if (typeof(define) === 'object') {
			$(description).text(define.description);
			if (define.type === 'why') {
				let why = document.createElement('textarea');
				if (define.placeholder) {
					why.setAttribute('placeholder', define.placeholder)
				}
				element.why = why;
				$(parent).append(why);
			} else if (define.type === 'choice') {

			} else {
				console.error(define);
				throw new Error(`无效的复合单元(unknown type "${define.type})"`);
			}

			if (define.extends !== undefined) {
				console.warn(define);
				class VQFExtends extends VQF {
					exLoad(defineExtends){
						if (Array.isArray(defineExtends)) {
							this.load(defineExtends);
						} else if (typeof(defineExtends) === 'object') {
							this.load([defineExtends]);
						} else {
							throw new Error('扩展题素既不是数组也不是对象');
						}
					}
				}
				const vqfExtends = new VQFExtends;
				vqfExtends.parentVqf = vqfContent;
				vqfExtends.layerCursor = vqfContent.layerCursor + 1;
				vqfExtends.initLayer();
				const vqfExtendsEle = document.createElement('div');
				vqfExtendsEle.classList.add('extends');
				vqfExtends.bind(vqfExtendsEle);
				vqfExtends.exLoad(define.extends);
				vqfExtends.makeHTML();

				element.extends = vqfExtends;
				this.extends = vqfExtends;

				$(parent).append(vqfExtendsEle);
			}

		} else {
			$(description).text(define);
		}
		this.elePool.push(element);
		return element;
	}
	remove(){

	}
	constructor(isMulti){
		this.type = 'choice';
		this.choicePool = [];
		this.isMulti = isMulti;
		this.elePool = [];
	}
}
class WhyDefine {
	setElementProperty(ele=this.ele){
		Object.defineProperty(this, 'value', {
			get(){ return this.ele.value },
			set(text){ this.ele.value = text},
		});
	}
	constructor(ele=document.createElement('textarea')){
		this.type = 'why';
		this.ele = ele;
		this.setElementProperty();
	}
}
class VQF {
	/**
	* @param layerPrefix 层前缀
	*/
	constructor(layerPrefix){
		this.checkPool = [];
		this.pool = [];
		this.layerCursor = 0;
		this.initLayer();
	}
	initLayer(){
		this.layerPrefix = `-${this.layerCursor}`;
	}

	fetchCheck(){
		this.checkPool.forEach((fn, cursor, pool) => {
			fn(cursor, pool);
		});
	}

	makeQuestion(question, parent, vqfsCursor, isMulti){
		let choice = new VQFChoice(isMulti);
		question.forEach((define, vqfcCursor) => {
			// console.warn(define);
			choice.add(define, parent, vqfsCursor, vqfcCursor, this);
		});
		choice.choicePool.push((fnOffset, choicePool) => {
			this.fetchCheck(fnOffset, choicePool);
		});
		this.pool.push(choice);
	}
	makeStructItemHTML(structItem, cursor, struct){
		let structElement = document.createElement('div');
		let question = document.createElement('div');

		question.classList.add('description');

		structElement.id = `vqfs-${cursor}`;
		structElement.setAttribute('layer', this.layerCursor);

		$(question).text(structItem.description);
		$(structElement).append(question);

		if (typeof(structItem) === 'object') {
			let vThis = this;
			this.questionTypeRouter(structItem, {
				single(){
					vThis.makeQuestion.apply(vThis, [structItem['question'], structElement, cursor, false]);
				},
				multi(){
					vThis.makeQuestion.apply(vThis, [structItem['questions'], structElement, cursor, true]);
				},
				why(){
					let why = new WhyDefine();
					$(question).append(why.ele);

					vThis.pool.push(why);
				},
			});
		}

		$(this.ele).append(structElement);
	}
	bind(ele){
		this.ele = ele;
	}
	makeHTML(){
		this.struct.forEach((question, cursor, struct) => {
			this.makeStructItemHTML(question, cursor, struct);
		});
	}
	load(value){
		this.value = value;
		try {
			if (typeof(value) === 'string') {
				this.struct = JSON.parse(this.value);
			} else {
				this.struct = value;
			}
		} catch (e) {
			let err = new Error('vqf数据解析错误');
			console.error(err);
			throw err;
		}
	}

	questionTypeRouter(structItem, cbObj){
		if (Array.isArray(structItem['question'])) {
			return cbObj.single(structItem);
		} else if (Array.isArray(structItem['questions'])) {
			return cbObj.multi(structItem);
		} else if (structItem['question'].type === 'why') {
			return cbObj.why(structItem);
		} else {
			console.error(structItem);
			throw new Error('该题素不能被正确解析');
		}
	}

	collect(){
		let limit = 8;
		const formItem = $('#collect > [id^="vqfs"]');
		var collectData = (currentVqf) => {
			return currentVqf.pool.map((choice, vqfsCursor) => {
				const defineQuestion = {};
				if (choice.isMulti && choice.type === 'choice') {
					return choice.backChecked().map(eleObj => {
						const defineQuestion = {
							choiced: eleObj.vqfcCursor,
						};
						if (eleObj.why) {
							defineQuestion.why = eleObj.why.value;
						}
						if (eleObj.extends) {
							defineQuestion.extends = collectData(eleObj.extends);
						}
						return defineQuestion;
					});
				}
				/* 检查单选项是否存在 */
				else if (!choice.isMulti && choice.type === 'choice') {
					const currentChoice = choice.elePool[choice.checkedCursor];
					if (currentChoice === undefined) {
						console.warn(choice.elePool, choice.checkedCursor, choice.elePool[choice.checkedCursor]);
						throw new Error('存在未填项');
					}
					if (currentChoice.why) {
						defineQuestion.why = currentChoice.why.value;
					}

					if (typeof choice.checkedCursor === "number" &&
						isFinite(choice.checkedCursor) &&
						Math.floor(choice.checkedCursor) === choice.checkedCursor
					){
						defineQuestion.choiced = choice.checkedCursor;
					} else {
						throw new Error('存在未填项');
					}

					/* 是否存在Extends */
					if (currentChoice.extends) {
						defineQuestion.extends = collectData(currentChoice.extends);
					}

					return defineQuestion;

				} else if (choice.type === 'why') {
					return {why: choice.value};
				} else {
					throw new Error(`位置的定义类型(${choice.type})`);
				}
			});
		};
		const collectDataStruct = collectData(this);
		console.warn(collectDataStruct);
		return collectDataStruct;
	}
}
<|endoftext|>


<commit_before>(function (ns, d3, _, $, undefined) {

    var defaults = {
        chart: {
            padding: {
                top: 3,
                bottom: 25,
                left: 40,
                right: 5
            }
        },

        xAxis: {
            rangePadding: 0,
            innerTickSize: 0,
            outerTickSize: 0,
            tickPadding: 6,
            titlePadding: 0,
            firstAndLast: true,
            labels: {
            }
        },

        yAxis: {
            min: 0,
            max: undefined,
            smartAxis: true,
            innerTickSize: 6,
            outerTickSize: 6,
            tickPadding: 4,
            titlePadding: 0,
            labels: {
                format: 's' // d3 formats
            }
        }
    };

    function extractScaleDomain(domain, min, max) {
        var dataMax = _.max(domain);
        var dataMin = _.min(domain);

        // we want null || undefined for all this comparasons
        // that == null gives us
        if (min == null && max == null) {
            return [dataMin, dataMax];
        }

        if (min == null) {
            return [Math.min(dataMin, max), max];
        }

        if (max == null) {
            return [min, Math.max(min, dataMax)];
        }

        return [min, max];
    }

    var cartesian = {
        dataSrc: [],

        init: function (options) {

            this.options = $.extend(true, {}, defaults, options);

            if (this.options.xAxis.title || this.options.yAxis.title) {
                this.titleOneEm = Narwhal.utils.textBounds('ABCD', 'axis-title').height;
                if(this.options.xAxis.title) {
                    this.options.chart.padding.bottom += this.titleOneEm; // should be 1em
                }

                if(this.options.yAxis.title) {
                    this.options.chart.padding.left += this.titleOneEm; // should be 1em
                }
            }

            if (!this.options.xAxis.firstAndLast) {
                this.options.chart.padding.right += 15;
            }

            return this;
        },

        computeXScale: function () {
            if (!this.xDomain) throw new Error('You are trying to render without setting data (xDomain).');

            this.scaleGenerator = _.nw.xScaleFactory(this.dataSrc, this.options);
            this.xScale = this.scaleGenerator.scale(this.xDomain);
            this.rangeBand = this.scaleGenerator.rangeBand();
        },

        computeYScale: function () {
            if (!this.yDomain) throw new Error('You are trying to render without setting data (yDomain).');
            var yScaleDomain = extractScaleDomain(this.yDomain, this.options.yAxis.min, this.options.yAxis.max);

            this.yScale = d3.scale.linear()
                .domain(yScaleDomain)
                .range([this.options.chart.plotHeight, 0]);

            // if we are not using smartAxis we use d3's nice() domain
            if (!this.options.yAxis.smartAxis)
                this.yScale.nice();
        },

        computeScales: function () {
            this.adjustDomain();
            this.computeXScale();
            this.computeYScale();

            return this;
        },

        xAxis: function () {
            var y = this.options.chart.plotHeight + this.options.chart.padding.top;
            var xAxis = this.scaleGenerator.axis().orient('bottom');

            this._xAxisGroup = this.svg
                .append('g')
                .attr('class', 'x axis')
                .attr('transform', 'translate(' + this.options.chart.padding.left + ',' + y + ')')
                .call(xAxis);

            this.scaleGenerator.postProcessAxis(this._xAxisGroup);

            return this;
        },

        yAxis: function () {
            var options = this.options.yAxis;
            var tickValues = this._extractYTickValues(this.yDomain, options.min, options.max);
            var numTicks = this._numYTicks(this.yDomain, options.min, options.max);
            var format = d3.format(options.labels.format);
            var yAxis = d3.svg.axis()
                .scale(this.yScale)
                .tickFormat(format)
                .tickSize(options.innerTickSize, options.outerTickSize)
                .tickPadding(options.tickPadding)
                .orient('left')
                .ticks(numTicks)
                .tickValues(tickValues);

            this._yAxisGroup = this.svg.append('g')
                .attr('class', 'y axis')
                .attr('transform', 'translate(' + this.options.chart.padding.left + ',' + this.options.chart.padding.top + ')');

            this._yAxisGroup.call(yAxis)
                .selectAll('text')
                    .attr('dy', '.9em');

            return this;
        },

        axisLabels: function () {
            var lineHeightAdjustment = this.titleOneEm * 0.25; // add 25% of font-size for a complete line-height

            if (this.options.xAxis.title) {
                this._xAxisGroup.append('text')
                    .attr('class', 'x axis-title')
                    .attr('text-anchor', 'end')
                    .attr('y', this.options.chart.padding.bottom - lineHeightAdjustment)
                    .attr('dx', this.options.chart.plotWidth)
                    .attr('dy', -this.options.xAxis.titlePadding)
                    .text(this.options.xAxis.title);
            }

            if (this.options.yAxis.title) {
                this._yAxisGroup.append('text')
                    .attr('class', 'y axis-title')
                    .attr('text-anchor', 'end')
                    .attr('transform', 'rotate(-90)')
                    .attr('y', -this.options.chart.padding.left + this.titleOneEm - lineHeightAdjustment)
                    .attr('dx', 0)
                    .attr('dy', this.options.yAxis.titlePadding)
                    .text(this.options.yAxis.title);
            }

        },

        render: function () {
            this.composeOptions();

            this.calcMetrics();

            this.computeScales();

            this.baseRender();

            this.xAxis()
                .yAxis()
                .axisLabels();

            this.renderVisualizations();

            return this;
        },

        datum: function (d, index) {
            if(_.isObject(d) && _.isArray(d.data))
                return _.map(d.data, _.bind(this.datum, this));

            return {
                y: _.isObject(d) ? d.y : d,
                x: _.isObject(d) ? d.x : this.options.xAxis.categories ? this.options.xAxis.categories[index] : index
            };
        },

        data: function (series) {

            if (series instanceof Array && !series.length) {
                return this;
            } else if (series instanceof Array && !series[0].data) {
                this.dataSrc = _.map(series, _.bind(this.datum, this));
                this.xDomain = this.extractXDomain(this.dataSrc);
                this.yDomain = this.extractYDomain(this.dataSrc);
                this.yMax = this.getYAxisDataMax(this.dataSrc);
                this.yMin = this.getYAxisDataMin(this.dataSrc);
            } else if (series instanceof Array && series[0].data) {
                // we have an array of series
                _.each(series, function (set) { this.data(set.data); }, this);
            }

            return this;
        },

        getYAxisDataMax: function (datums) {
            return Math.max(this.yMax || 0, _.max(_.pluck(datums, 'y')));
        },

        getYAxisDataMin: function (datums) {
            return Math.min(this.yMin || 0, _.min(_.pluck(datums, 'y')));
        },

        extractXDomain: function(datums) {
            return  this.options.xAxis.categories ? this.options.xAxis.categories : _.pluck(datums, 'x');
        },

        extractYDomain: function (datums) {
            var max = this.yDomain ? Math.max(this.yDomain[1], _.max(_.pluck(datums, 'y'))) : _.max(_.pluck(datums, 'y'));
            var min = this.yDomain ? Math.min(this.yDomain[0], _.min(_.pluck(datums, 'y'))) : _.min(_.pluck(datums, 'y'));

            return [min, max];
        },

        adjustDomain: function () {
            this.yDomain = this.yDomain ? this.options.yAxis.smartAxis ? [this.yDomain[0], _.nw.niceRound(this.yDomain[1])] : this.yDomain : [0, 10];
            this.xDomain = this.xDomain ? this.xDomain : [];
        },

        _extractYTickValues: function (domain, min, max) {

            function smartAxisValues() {
                var adjustedDomain = _.nw.merge(domain, this.yMax);
                // we want to be able to remove parameters with default values
                // so to remove the default yAxis.min: 0, you pass yAxis.min: null
                // and for that we need to to a truely comparison here (to get null or undefined)
                if (min == null && max == null)
                    return adjustedDomain;

                if (min == null) {
                    return max > this.yMin ? _.nw.merge([max], adjustedDomain) : [max];
                }

                if (max == null) {
                    if (min >= this.yMax) return [min];
                    adjustedDomain[0] = min;

                    return adjustedDomain;
                }

                return _.nw.merge([min, max], this.yMax);
            }

            return this.options.yAxis.smartAxis ? smartAxisValues.call(this) : undefined;
        },

        _numYTicks: function (domain, min, max) {
            function regularAxisYTicks() {
                return this.options.yAxis.ticks != null ? this.options.yAxis.ticks : undefined;
            }

            function smartAxisYTicks() {
                return 3; // 0, dataMax and niceMax
            }

            return this.options.yAxis.smartAxis ? smartAxisYTicks.call(this) : regularAxisYTicks.call(this);
        }

    };

    window[ns].prototype.expose('cartesian', cartesian);

})('Narwhal', window.d3, window._, window.jQuery);
<commit_msg>Change default xAxis tick padding<commit_after>(function (ns, d3, _, $, undefined) {

    var defaults = {
        chart: {
            padding: {
                top: 3,
                bottom: 25,
                left: 40,
                right: 5
            }
        },

        xAxis: {
            rangePadding: 0,
            innerTickSize: 0,
            outerTickSize: 0,
            tickPadding: 10,
            titlePadding: 0,
            firstAndLast: true,
            labels: {
            }
        },

        yAxis: {
            min: 0,
            max: undefined,
            smartAxis: true,
            innerTickSize: 6,
            outerTickSize: 6,
            tickPadding: 4,
            titlePadding: 0,
            labels: {
                format: 's' // d3 formats
            }
        }
    };

    function extractScaleDomain(domain, min, max) {
        var dataMax = _.max(domain);
        var dataMin = _.min(domain);

        // we want null || undefined for all this comparasons
        // that == null gives us
        if (min == null && max == null) {
            return [dataMin, dataMax];
        }

        if (min == null) {
            return [Math.min(dataMin, max), max];
        }

        if (max == null) {
            return [min, Math.max(min, dataMax)];
        }

        return [min, max];
    }

    var cartesian = {
        dataSrc: [],

        init: function (options) {

            this.options = $.extend(true, {}, defaults, options);

            if (this.options.xAxis.title || this.options.yAxis.title) {
                this.titleOneEm = Narwhal.utils.textBounds('ABCD', 'axis-title').height;
                if(this.options.xAxis.title) {
                    this.options.chart.padding.bottom += this.titleOneEm; // should be 1em
                }

                if(this.options.yAxis.title) {
                    this.options.chart.padding.left += this.titleOneEm; // should be 1em
                }
            }

            if (!this.options.xAxis.firstAndLast) {
                this.options.chart.padding.right += 15;
            }

            return this;
        },

        computeXScale: function () {
            if (!this.xDomain) throw new Error('You are trying to render without setting data (xDomain).');

            this.scaleGenerator = _.nw.xScaleFactory(this.dataSrc, this.options);
            this.xScale = this.scaleGenerator.scale(this.xDomain);
            this.rangeBand = this.scaleGenerator.rangeBand();
        },

        computeYScale: function () {
            if (!this.yDomain) throw new Error('You are trying to render without setting data (yDomain).');
            var yScaleDomain = extractScaleDomain(this.yDomain, this.options.yAxis.min, this.options.yAxis.max);

            this.yScale = d3.scale.linear()
                .domain(yScaleDomain)
                .range([this.options.chart.plotHeight, 0]);

            // if we are not using smartAxis we use d3's nice() domain
            if (!this.options.yAxis.smartAxis)
                this.yScale.nice();
        },

        computeScales: function () {
            this.adjustDomain();
            this.computeXScale();
            this.computeYScale();

            return this;
        },

        xAxis: function () {
            var y = this.options.chart.plotHeight + this.options.chart.padding.top;
            var xAxis = this.scaleGenerator.axis().orient('bottom');

            this._xAxisGroup = this.svg
                .append('g')
                .attr('class', 'x axis')
                .attr('transform', 'translate(' + this.options.chart.padding.left + ',' + y + ')')
                .call(xAxis);

            this.scaleGenerator.postProcessAxis(this._xAxisGroup);

            return this;
        },

        yAxis: function () {
            var options = this.options.yAxis;
            var tickValues = this._extractYTickValues(this.yDomain, options.min, options.max);
            var numTicks = this._numYTicks(this.yDomain, options.min, options.max);
            var format = d3.format(options.labels.format);
            var yAxis = d3.svg.axis()
                .scale(this.yScale)
                .tickFormat(format)
                .tickSize(options.innerTickSize, options.outerTickSize)
                .tickPadding(options.tickPadding)
                .orient('left')
                .ticks(numTicks)
                .tickValues(tickValues);

            this._yAxisGroup = this.svg.append('g')
                .attr('class', 'y axis')
                .attr('transform', 'translate(' + this.options.chart.padding.left + ',' + this.options.chart.padding.top + ')');

            this._yAxisGroup.call(yAxis)
                .selectAll('text')
                    .attr('dy', '.9em');

            return this;
        },

        axisLabels: function () {
            var lineHeightAdjustment = this.titleOneEm * 0.25; // add 25% of font-size for a complete line-height

            if (this.options.xAxis.title) {
                this._xAxisGroup.append('text')
                    .attr('class', 'x axis-title')
                    .attr('text-anchor', 'end')
                    .attr('y', this.options.chart.padding.bottom - lineHeightAdjustment)
                    .attr('dx', this.options.chart.plotWidth)
                    .attr('dy', -this.options.xAxis.titlePadding)
                    .text(this.options.xAxis.title);
            }

            if (this.options.yAxis.title) {
                this._yAxisGroup.append('text')
                    .attr('class', 'y axis-title')
                    .attr('text-anchor', 'end')
                    .attr('transform', 'rotate(-90)')
                    .attr('y', -this.options.chart.padding.left + this.titleOneEm - lineHeightAdjustment)
                    .attr('dx', 0)
                    .attr('dy', this.options.yAxis.titlePadding)
                    .text(this.options.yAxis.title);
            }

        },

        render: function () {
            this.composeOptions();

            this.calcMetrics();

            this.computeScales();

            this.baseRender();

            this.xAxis()
                .yAxis()
                .axisLabels();

            this.renderVisualizations();

            return this;
        },

        datum: function (d, index) {
            if(_.isObject(d) && _.isArray(d.data))
                return _.map(d.data, _.bind(this.datum, this));

            return {
                y: _.isObject(d) ? d.y : d,
                x: _.isObject(d) ? d.x : this.options.xAxis.categories ? this.options.xAxis.categories[index] : index
            };
        },

        data: function (series) {

            if (series instanceof Array && !series.length) {
                return this;
            } else if (series instanceof Array && !series[0].data) {
                this.dataSrc = _.map(series, _.bind(this.datum, this));
                this.xDomain = this.extractXDomain(this.dataSrc);
                this.yDomain = this.extractYDomain(this.dataSrc);
                this.yMax = this.getYAxisDataMax(this.dataSrc);
                this.yMin = this.getYAxisDataMin(this.dataSrc);
            } else if (series instanceof Array && series[0].data) {
                // we have an array of series
                _.each(series, function (set) { this.data(set.data); }, this);
            }

            return this;
        },

        getYAxisDataMax: function (datums) {
            return Math.max(this.yMax || 0, _.max(_.pluck(datums, 'y')));
        },

        getYAxisDataMin: function (datums) {
            return Math.min(this.yMin || 0, _.min(_.pluck(datums, 'y')));
        },

        extractXDomain: function(datums) {
            return  this.options.xAxis.categories ? this.options.xAxis.categories : _.pluck(datums, 'x');
        },

        extractYDomain: function (datums) {
            var max = this.yDomain ? Math.max(this.yDomain[1], _.max(_.pluck(datums, 'y'))) : _.max(_.pluck(datums, 'y'));
            var min = this.yDomain ? Math.min(this.yDomain[0], _.min(_.pluck(datums, 'y'))) : _.min(_.pluck(datums, 'y'));

            return [min, max];
        },

        adjustDomain: function () {
            this.yDomain = this.yDomain ? this.options.yAxis.smartAxis ? [this.yDomain[0], _.nw.niceRound(this.yDomain[1])] : this.yDomain : [0, 10];
            this.xDomain = this.xDomain ? this.xDomain : [];
        },

        _extractYTickValues: function (domain, min, max) {

            function smartAxisValues() {
                var adjustedDomain = _.nw.merge(domain, this.yMax);
                // we want to be able to remove parameters with default values
                // so to remove the default yAxis.min: 0, you pass yAxis.min: null
                // and for that we need to to a truely comparison here (to get null or undefined)
                if (min == null && max == null)
                    return adjustedDomain;

                if (min == null) {
                    return max > this.yMin ? _.nw.merge([max], adjustedDomain) : [max];
                }

                if (max == null) {
                    if (min >= this.yMax) return [min];
                    adjustedDomain[0] = min;

                    return adjustedDomain;
                }

                return _.nw.merge([min, max], this.yMax);
            }

            return this.options.yAxis.smartAxis ? smartAxisValues.call(this) : undefined;
        },

        _numYTicks: function (domain, min, max) {
            function regularAxisYTicks() {
                return this.options.yAxis.ticks != null ? this.options.yAxis.ticks : undefined;
            }

            function smartAxisYTicks() {
                return 3; // 0, dataMax and niceMax
            }

            return this.options.yAxis.smartAxis ? smartAxisYTicks.call(this) : regularAxisYTicks.call(this);
        }

    };

    window[ns].prototype.expose('cartesian', cartesian);

})('Narwhal', window.d3, window._, window.jQuery);
<|endoftext|>