<commit_before>// ==UserScript==
// @name         GameBanana UI Tweaks
// @namespace    http://gamebanana.com/members/1328950
// @version      0.01
// @description  Set of GameBanana User Interface tweaks writen in CSS & JS
// @author       Yogensia
// @match        http://*.gamebanana.com/*
// @grant        none
// ==/UserScript==

// Licensed under MIT License, for more info:
// https://raw.githubusercontent.com/yogensia/gb-ui-tweaks/master/LICENSE

// DOCUMENT OUTLINE
// 1. SETUP
// 2. CSS INIT

// SETUP
// ==================================================================

// variables
var VERSION = "0.01"; // script version
var EDGECSS = false; // use development version of CSS

// comment to enable console logging
console.log = function() {}

// CSS INIT
// ==================================================================

// DOM ready
$(function() {

	// if no uberstyle is found
	if ( $("link[href*='/uberstyles/']").length = 0 ) {
		// add CSS
		if ( EDGECSS == true ) {
			var gbUiTweaksCSS = '<link rel="stylesheet" href="http://yogensia.com/gamebanana/gb-ui-tweaks/gb-ui-tweaks.css">';
		} else {
			var gbUiTweaksCSS = '<link rel="stylesheet" href="https://rawgit.com/yogensia/gb-ui-tweaks/master/gb-ui-tweaks.css">';
		}
		$("head").append(gbUiTweaksCSS);
	}

});<commit_msg>fix uberstyle detection and minor tweaks to code<commit_after>// ==UserScript==
// @name         GameBanana UI Tweaks
// @namespace    http://gamebanana.com/members/1328950
// @version      0.01
// @description  Set of GameBanana User Interface tweaks writen in CSS & JS
// @author       Yogensia
// @match        http://*.gamebanana.com/*
// @grant        none
// ==/UserScript==

// Licensed under MIT License, for more info:
// https://raw.githubusercontent.com/yogensia/gb-ui-tweaks/master/LICENSE

// DOCUMENT OUTLINE
// 1. SETUP
// 2. CSS INIT

// SETUP
// ==================================================================

// variables
var GBUIT_VERSION = "0.01"; // script version
var GBUIT_EDGECSS = true; // use development version of CSS

console.log("GBUIT: INIT");

// CSS INIT
// ==================================================================

// DOM ready
$(function() {

	// if no uberstyle is found
	if ( $("link[href*='/uberstyles/']").length == 0 ) {
		console.log("GBUIT: No uber found, continuing...");
		// add CSS
		if ( GBUIT_EDGECSS == true ) {
			console.warn("GBUIT: EdgeCSS is enabled!");
			var gbUiTweaksCSS = '<link rel="stylesheet" href="http://yogensia.com/gamebanana/gb-ui-tweaks/gb-ui-tweaks.css">';
		} else {
			var gbUiTweaksCSS = '<link rel="stylesheet" href="https://rawgit.com/yogensia/gb-ui-tweaks/master/gb-ui-tweaks.css">';
		}
		$("head").append(gbUiTweaksCSS);
	} else {
		console.warn("GBUIT: Uber found, aborting tweaks!");
	}

});<|endoftext|>


<commit_before>// this function sets up a GH object on which subseqent calls will be made.
// login is handled via the oath token of the currently logged in user.  this limits
// each user to 5000 github requests per hour.
var setUp = function(token) {
  var GitHubApi = require("github");
  var github = new GitHubApi({
    version: "3.0.0",
    protocol: "https",
    host: "api.github.com",
    pathPrefix: "",
    timeout: 5000, // responses will time out after this many milliseconds
    headers: {
    }
  });
  github.authenticate({
    type: "token",
    token: token // oauth token from current user
  });
  console.log("Logged in to Github!")
  return github;
}

// this method checks to see if a user exists for the requested username
var checkGHUser = function (github, user) {
  return new Promise(function(resolve, reject){
    github.repos.getFromUser({
      user:user,
      per_page:1
    }, function(err,result){
      if (err){
        console.log("User name not valid.")
      }else{
        resolve(result)
      }
    })
  })
}
// this method takes a user name and a GH object (generated from setUp)
// and retrieves all of a user's repos. it returns a promise onto which we
// chain the next function below
var getRepoNamesChain = function (user, github) {
  return new Promise(function(resolve, reject){
    console.log("Getting repo names...")
    var names = []; // array to store names of repos as strings

    github.repos.getFromUser({
      user: user, // user we want to search for
      sort: "updated", // order by most recently updated
      per_page: 100, // number of repos we want to see (100 max)
    }, function(err, res) {
      if (err){
        console.log("CLOG ERROR @repos: "+err)
        return err;
      }
      for (var h=0; h < res.length; h++){
        //if (res[h].fork == false) {
          names.push(res[h].name); // construct the array of repo names
        //}
      }
      console.log("Found "+names.length+" repos")
      // checkAuthors(user, github, stamp, profile, names, resp);
      resolve(names);
    })
  })
}

// this method makes sure that the user being searched for is a contributor
// on the repo in question before we search for it.  essentially it takes a user
// name and an array of repos and edits the array so as to remove any repo
// to which the user has not contributed.  it returns a promise onto which the next
// function is chained (below)
var checkAuthors = function (user, github, names) {
  return new Promise(function(resolve, reject){
    console.log("Checking authorship of user per repo...")
    var counter = 0;
    var originalNumRepos = names.length;
    for (var i=0; i<names.length; i++) {
      github.repos.getContributors({
        user: user, // user we want to search for
        per_page: 100, // number of repos we want to see (100 max)
        repo: names[i]
      }, function(err, res) {
        if (err){
          console.log("Error checking contributors: "+err);
        }
        if (res) {
          var use_this_repo = false;
          for (var j=0; j<res.length; j++){
            if (res[j].login) {
              if (res[j].login == user){
                use_this_repo = true;
              }
            }
          }
          if (!use_this_repo){
            names.splice(this.j, 1);
          }
        }
        if (counter++ == originalNumRepos-1){
          console.log("Disregarding "+(originalNumRepos-(names.length))+" repos because "+user+" is not a contributor")
          // getCommitMessages(user, github, stamp, profile, names, resp);
          resolve(names);
        }
      }.bind({i:i}))
    }
  })
}

// this method takes an array of strings (GH repos) and finds all commit messages on those
//repos, checking to make the author we're searching for actually authored those commitMessages
// it returns a promise onto which the next function is called.
var getCommitMessages = function (user, github, names){
  return new Promise(function(resolve, reject){
    console.log("Getting Commit Messages...")
    var callsDone = 0; // count calls done so we know when all calls have returned from github
    var nameMsgMap = {}; // object that will store the repo names as keys and the commit messages as values
    for (var i = 0; i < names.length; i++) { // for each repo name, make a call to github for all commit messages on that repo
      github.repos.getCommits({
        user: user,
        repo: names[i], // the current repo being searched
        per_page: 100
      }, function(error, response){

        if (response) {
          console.log("Messages from @"+names[this.i]+" retrieved!"+"("+(callsDone+1)+")") // sucess message
          var msgs = []; // array to hold every message on a given repo
          // make sure the user in question is the author of the commit
          for (var a = 0; a < response.length; a++){
            if (response[a]['committer']) {
              if (response[a]['committer']['login'] == user) {
                msgs.push(response[a]['commit']['message']) // add message onto the array
              }
            }
          }
          nameMsgMap[names[this.i].replace(/\./g,' ')] = msgs; // construct the object so the key is the repo and the value is the array of commit messages
        }
        if (error) {
          console.log("ERROR in GH CALL @"+names[this.i]+": "+error)
        }
        if (++callsDone == names.length){ // check to see if we've done the total number of calls.  if we have, the number of calls will equal the number of repos
          console.log("Got Commit Messages!"); // success message
          // stamp.data.commitMessages = nameMsgMap // set the commitMessages column on the stamp to be the object we constructed above
          // getLangs(user, github, stamp, profile, names, resp) // now we call the getLangs method, to get all langugages on each repo
          var responseObject = {
                            names: names,
                            nameMsgMap: nameMsgMap
                            }
          resolve(responseObject);
        }
      }.bind({i:i})) // bind i so we can use it to figure out which repo we're currently searching.  this is needed in order to match repos to commit messages

    }
  })
}
// this function takes a user and a list of repos.  it fins the language breakdown per repo and returns that
// information through a promise.  after this promise is fufilled (back in the profilescontroller) we save all this
// data to the stamp and render it
var getLangs = function(user, github, names){
  return new Promise(function(resolve, reject){
    console.log("Getting Languages...")
    var calls = 0;
    var nameLangMap = {};
    for (var i = 0; i < names.length; i++) {
      github.repos.getLanguages({
        user: user, // github user name
        repo: names[i], // current repo
        per_page: 100
      }, function(error, response){
        if (error) {
          console.log("ERROR in GH CALL @"+names[this.i]+": "+error)
        }
        if (response) {
          console.log("Languages from @"+names[this.i]+" retrieved!"+"("+(calls+1)+")") // success message
          nameLangMap[names[this.i].replace(/\./g,' ')] = response; // constructing the object
        }
        if (++calls == names.length){ // check to see if all calls have returned
          console.log("Got Languages!") // success message
          resolve(nameLangMap)
        }
      }.bind({i:i})) // bind i from the for loop so we have access to it each callback
    }
  })
}
// this method should only be called after getLangs.  it parses through
// the languages object in a stamp and finds the total number of languages
// and then stores that value in langTotals column in our stamp object
var parseLangs = function (allLangs) {
  var langStats = {};
    for (var key1 in allLangs) {
      for (var key2 in allLangs[key1]) {
        if (key2 != 'meta') {
          if (langStats.hasOwnProperty(key2)) {
            langStats[key2]+=allLangs[key1][key2];
          }
          else {
            langStats[key2]=allLangs[key1][key2];
          }
        }
      }
    }
    return langStats;
}
// this method uses the total languages column in the database
// to calculate the percentage of each language the user has
// written in
var langAverages = function(langStats){
  var langAverages = {};
  var sum  = 0
    for (var key1 in langStats){
      sum += langStats[key1]
    }
    for (var lang in langStats) {
      langAverages[lang]=langStats[lang]/sum
    }
  return langAverages;
}
// this method should only be called after getCommitMessages.  it parses through
// the messages object in a stamp and then finds the average message length, which
// it stores in the averageMessageLength column
var msgAverages = function (messages){
  var averageLength=0;
  var numMessages=0;
  var totalLength=0;
  for (var key in messages){
    for (var i = 0; i < messages[key].length; i++){
      totalLength += messages[key][i].length
      numMessages++;
    }
  }
  averageLength = totalLength/numMessages;
  return averageLength;
}
// export functions for use in profilesController
module.exports = {
  setUp: setUp,
  getRepoNamesChain: getRepoNamesChain,
  getCommitMessages: getCommitMessages,
  checkGHUser: checkGHUser,
  checkAuthors: checkAuthors,
  getCommitMessages: getCommitMessages,
  getLangs: getLangs,
  langAverages: langAverages,
  parseLangs: parseLangs,
  msgAverages: msgAverages
}
<commit_msg>fixed checkauthors function<commit_after>// this function sets up a GH object on which subseqent calls will be made.
// login is handled via the oath token of the currently logged in user.  this limits
// each user to 5000 github requests per hour.
var setUp = function(token) {
  var GitHubApi = require("github");
  var github = new GitHubApi({
    version: "3.0.0",
    protocol: "https",
    host: "api.github.com",
    pathPrefix: "",
    timeout: 5000, // responses will time out after this many milliseconds
    headers: {
    }
  });
  github.authenticate({
    type: "token",
    token: token // oauth token from current user
  });
  console.log("Logged in to Github!")
  return github;
}

// this method checks to see if a user exists for the requested username
var checkGHUser = function (github, user) {
  return new Promise(function(resolve, reject){
    github.repos.getFromUser({
      user:user,
      per_page:1
    }, function(err,result){
      if (err){
        console.log("User name not valid.")
      }else{
        resolve(result)
      }
    })
  })
}
// this method takes a user name and a GH object (generated from setUp)
// and retrieves all of a user's repos. it returns a promise onto which we
// chain the next function below
var getRepoNamesChain = function (user, github) {
  return new Promise(function(resolve, reject){
    console.log("Getting repo names...")
    var names = []; // array to store names of repos as strings

    github.repos.getFromUser({
      user: user, // user we want to search for
      sort: "updated", // order by most recently updated
      per_page: 100, // number of repos we want to see (100 max)
    }, function(err, res) {
      if (err){
        console.log("CLOG ERROR @repos: "+err)
        return err;
      }
      for (var h=0; h < res.length; h++){
        //if (res[h].fork == false) {
          names.push(res[h].name); // construct the array of repo names
        //}
      }
      console.log("Found "+names.length+" repos")
      // checkAuthors(user, github, stamp, profile, names, resp);
      resolve(names);
    })
  })
}

// this method makes sure that the user being searched for is a contributor
// on the repo in question before we search for it.  essentially it takes a user
// name and an array of repos and edits the array so as to remove any repo
// to which the user has not contributed.  it returns a promise onto which the next
// function is chained (below)
var checkAuthors = function (user, github, names) {
  return new Promise(function(resolve, reject){
    console.log("Checking authorship of user per repo...")
    // console.log(names)
    var counter = 0;
    var originalNumRepos = names.length;
    var removeCount = [];
    for (var i=0; i<names.length; i++) {
      github.repos.getContributors({
        user: user, // user we want to search for
        per_page: 100, // number of repos we want to see (100 max)
        repo: names[i]
      }, function(err, res) {
        if (err){
          console.log("Error checking contributors: "+err);
        }
        if (res) {
          var use_this_repo = false;
          for (var j=0; j<res.length; j++){
            if (res[j].login) {
              if (res[j].login == user){
                use_this_repo = true;
              }
            }
          }
          if (!use_this_repo){
            removeCount.push(names[this.i]);
          }
        }
        if (counter++ == originalNumRepos-1){
          for (var k=0; k<removeCount.length; k++){
            names.splice(names.indexOf(removeCount[k]), 1)
          }
          console.log("Disregarding "+(originalNumRepos-(names.length))+" repos because "+user+" is not a contributor")
          resolve(names);
        }
      }.bind({i:i}))
    }
  })
}

// this method takes an array of strings (GH repos) and finds all commit messages on those
//repos, checking to make the author we're searching for actually authored those commitMessages
// it returns a promise onto which the next function is called.
var getCommitMessages = function (user, github, names){
  return new Promise(function(resolve, reject){
    console.log("Getting Commit Messages...")
    var callsDone = 0; // count calls done so we know when all calls have returned from github
    var nameMsgMap = {}; // object that will store the repo names as keys and the commit messages as values
    for (var i = 0; i < names.length; i++) { // for each repo name, make a call to github for all commit messages on that repo
      github.repos.getCommits({
        user: user,
        repo: names[i], // the current repo being searched
        per_page: 100
      }, function(error, response){

        if (response) {
          console.log("Messages from @"+names[this.i]+" retrieved!"+"("+(callsDone+1)+")") // sucess message
          var msgs = []; // array to hold every message on a given repo
          // make sure the user in question is the author of the commit
          for (var a = 0; a < response.length; a++){
            if (response[a]['committer']) {
              if (response[a]['committer']['login'] == user) {
                msgs.push(response[a]['commit']['message']) // add message onto the array
              }
            }
          }
          nameMsgMap[names[this.i].replace(/\./g,' ')] = msgs; // construct the object so the key is the repo and the value is the array of commit messages
        }
        if (error) {
          console.log("ERROR in GH CALL @"+names[this.i]+": "+error)
        }
        if (++callsDone == names.length){ // check to see if we've done the total number of calls.  if we have, the number of calls will equal the number of repos
          console.log("Got Commit Messages!"); // success message
          // stamp.data.commitMessages = nameMsgMap // set the commitMessages column on the stamp to be the object we constructed above
          // getLangs(user, github, stamp, profile, names, resp) // now we call the getLangs method, to get all langugages on each repo
          var responseObject = {
                            names: names,
                            nameMsgMap: nameMsgMap
                            }
          resolve(responseObject);
        }
      }.bind({i:i})) // bind i so we can use it to figure out which repo we're currently searching.  this is needed in order to match repos to commit messages

    }
  })
}
// this function takes a user and a list of repos.  it fins the language breakdown per repo and returns that
// information through a promise.  after this promise is fufilled (back in the profilescontroller) we save all this
// data to the stamp and render it
var getLangs = function(user, github, names){
  return new Promise(function(resolve, reject){
    console.log("Getting Languages...")
    var calls = 0;
    var nameLangMap = {};
    for (var i = 0; i < names.length; i++) {
      github.repos.getLanguages({
        user: user, // github user name
        repo: names[i], // current repo
        per_page: 100
      }, function(error, response){
        if (error) {
          console.log("ERROR in GH CALL @"+names[this.i]+": "+error)
        }
        if (response) {
          console.log("Languages from @"+names[this.i]+" retrieved!"+"("+(calls+1)+")") // success message
          nameLangMap[names[this.i].replace(/\./g,' ')] = response; // constructing the object
        }
        if (++calls == names.length){ // check to see if all calls have returned
          console.log("Got Languages!") // success message
          resolve(nameLangMap)
        }
      }.bind({i:i})) // bind i from the for loop so we have access to it each callback
    }
  })
}
// this method should only be called after getLangs.  it parses through
// the languages object in a stamp and finds the total number of languages
// and then stores that value in langTotals column in our stamp object
var parseLangs = function (allLangs) {
  var langStats = {};
    for (var key1 in allLangs) {
      for (var key2 in allLangs[key1]) {
        if (key2 != 'meta') {
          if (langStats.hasOwnProperty(key2)) {
            langStats[key2]+=allLangs[key1][key2];
          }
          else {
            langStats[key2]=allLangs[key1][key2];
          }
        }
      }
    }
    return langStats;
}
// this method uses the total languages column in the database
// to calculate the percentage of each language the user has
// written in
var langAverages = function(langStats){
  var langAverages = {};
  var sum  = 0
    for (var key1 in langStats){
      sum += langStats[key1]
    }
    for (var lang in langStats) {
      langAverages[lang]=langStats[lang]/sum
    }
  return langAverages;
}
// this method should only be called after getCommitMessages.  it parses through
// the messages object in a stamp and then finds the average message length, which
// it stores in the averageMessageLength column
var msgAverages = function (messages){
  var averageLength=0;
  var numMessages=0;
  var totalLength=0;
  for (var key in messages){
    for (var i = 0; i < messages[key].length; i++){
      totalLength += messages[key][i].length
      numMessages++;
    }
  }
  averageLength = totalLength/numMessages;
  return averageLength;
}
// export functions for use in profilesController
module.exports = {
  setUp: setUp,
  getRepoNamesChain: getRepoNamesChain,
  getCommitMessages: getCommitMessages,
  checkGHUser: checkGHUser,
  checkAuthors: checkAuthors,
  getCommitMessages: getCommitMessages,
  getLangs: getLangs,
  langAverages: langAverages,
  parseLangs: parseLangs,
  msgAverages: msgAverages
}
<|endoftext|>


<commit_before><commit_msg>nosleep and fullscreen fix try<commit_after><|endoftext|>


<commit_before>/* ***** BEGIN LICENSE BLOCK *****
 *
 * Copyright (c) 2009 Aptana, Inc.
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * ***** END LICENSE BLOCK ***** */

ActiveRecord.asynchronous = false; //deprecated until we have HTML5 support

var Synchronization = {};

Synchronization.resultSetNotifications = {};

Synchronization.notifications = {};

Synchronization.setupNotifications = function setupNotifications(record)
{
    if(!record.id)
    {
        return false;
    }
    if(!Synchronization.notifications[record.tableName])
    {
        Synchronization.notifications[record.tableName] = {};
    }
    if(!Synchronization.notifications[record.tableName][record.id])
    {
        Synchronization.notifications[record.tableName][record.id] = {};
    }
    return true;
};

Synchronization.triggerSynchronizationNotifications = function triggerSynchronizationNotifications(record,event_name)
{
    if(!Synchronization.setupNotifications(record))
    {
        return false;
    }
    if(event_name == 'afterSave')
    {
        var found_records = Synchronization.notifications[record.tableName][record.id];
        for(var internal_count_id in found_records)
        {
            if(internal_count_id != record.internalCount)
            {
                var found_record = found_records[internal_count_id];
                var keys = found_record.keys();
                for(var i = 0; i < keys.length; ++i)
                {
                    var key_name = keys[i];
                    found_record.set(key_name,record.get(key_name));
                }
                found_record.notify('synchronization:afterSave');
            }
        }
    }
    else if(event_name == 'afterDestroy' || event_name == 'afterCreate')
    {
        if(Synchronization.resultSetNotifications[record.tableName])
        {
            for(var synchronized_result_set_count in Synchronization.resultSetNotifications[record.tableName])
            {
                var old_result_set = Synchronization.resultSetNotifications[record.tableName][synchronized_result_set_count].resultSet;
                var new_params = ActiveSupport.clone(Synchronization.resultSetNotifications[record.tableName][synchronized_result_set_count].params);
                var new_result_set = record.constructor.find(ActiveSupport.extend(new_params,{synchronize: false}));
                var splices = Synchronization.spliceArgumentsFromResultSetDiff(old_result_set,new_result_set,event_name);
                for(var i = 0; i < splices.length; ++i)
                {
                    old_result_set.splice.apply(old_result_set,splices[i]);
                }
            }
        }
        if(event_name == 'afterDestroy')
        {
            var found_records = Synchronization.notifications[record.tableName][record.id];
            for(var internal_count_id in found_records)
            {
                if(internal_count_id != record.internalCount)
                {
                    found_records[internal_count_id].notify('synchronization:afterDestroy');
                    Synchronization.notifications[record.tableName][record.id][internal_count_id] = null;
                    delete Synchronization.notifications[record.tableName][record.id][internal_count_id];
                }
            }
        }
    }
};

ActiveSupport.extend(ActiveRecord.InstanceMethods,{
    /**
     * Once synchronized a found instance will have it's values updated if
     * other records with the same id change in the database.
     * @alias ActiveRecord.Instance.synchronize
     */
    synchronize: function synchronize()
    {
        Synchronization.setupNotifications(this);
        Synchronization.notifications[this.tableName][this.id][this.internalCount] = this;
    },
    /**
     * Stops the synchronization of the record with the database.
     * @alias ActiveRecord.Instance.stop
     */
    stop: function stop()
    {
        Synchronization.setupNotifications(this);
        Synchronization.notifications[this.tableName][this.id][this.internalCount] = null;
        delete Synchronization.notifications[this.tableName][this.id][this.internalCount];
    }
});

Synchronization.synchronizedResultSetCount = 0;

Synchronization.synchronizeResultSet = function synchronizeResultSet(klass,params,result_set)
{
    result_set.synchronize = function synchronize(){

    };
    ++Synchronization.synchronizedResultSetCount;
    if(!Synchronization.resultSetNotifications[klass.tableName])
    {
        Synchronization.resultSetNotifications[klass.tableName] = {};
    }
    Synchronization.resultSetNotifications[klass.tableName][Synchronization.synchronizedResultSetCount] = {
        resultSet: result_set,
        params: params
    };
    for(var i = 0; i < result_set.length; ++i)
    {
        result_set[i].synchronize();
    }
    result_set.stop = (function result_set_synchronization_stop_generator(table_name,synchronized_result_set_count){
        return function stop(){
            for(var i = 0; i < this.length; ++i)
            {
                this[i].stop();
            }
            Synchronization.resultSetNotifications[table_name][synchronized_result_set_count] = null;
            delete Synchronization.resultSetNotifications[table_name][synchronized_result_set_count];
        };
    })(klass.tableName,Synchronization.synchronizedResultSetCount);
};

Synchronization.spliceArgumentsFromResultSetDiff = function spliceArgumentsFromResultSetDiff(a,b,event_name)
{
    var diffs = [];
    if(event_name == 'afterCreate')
    {
        for(var i = 0; i < b.length; ++i)
        {
            if(!a[i] || (a[i] && (a[i].id != b[i].id)))
            {
                diffs.push([i,null,b[i]]);
                break;
            }
        }
    }
    else if(event_name == 'afterDestroy')
    {
        for(var i = 0; i < a.length; ++i)
        {
            if(!b[i] || (b[i] && (b[i].id != a[i].id)))
            {
                diffs.push([i,1]);
                break;
            }
        }
    }
    return diffs;
};

ActiveRecord.Synchronization = Synchronization;<commit_msg>updated synchronization documentation<commit_after>/* ***** BEGIN LICENSE BLOCK *****
 *
 * Copyright (c) 2009 Aptana, Inc.
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * ***** END LICENSE BLOCK ***** */

ActiveRecord.asynchronous = false; //deprecated until we have HTML5 support

var Synchronization = {};

Synchronization.resultSetNotifications = {};

Synchronization.notifications = {};

Synchronization.setupNotifications = function setupNotifications(record)
{
    if(!record.id)
    {
        return false;
    }
    if(!Synchronization.notifications[record.tableName])
    {
        Synchronization.notifications[record.tableName] = {};
    }
    if(!Synchronization.notifications[record.tableName][record.id])
    {
        Synchronization.notifications[record.tableName][record.id] = {};
    }
    return true;
};

Synchronization.triggerSynchronizationNotifications = function triggerSynchronizationNotifications(record,event_name)
{
    if(!Synchronization.setupNotifications(record))
    {
        return false;
    }
    if(event_name == 'afterSave')
    {
        var found_records = Synchronization.notifications[record.tableName][record.id];
        for(var internal_count_id in found_records)
        {
            if(internal_count_id != record.internalCount)
            {
                var found_record = found_records[internal_count_id];
                var keys = found_record.keys();
                for(var i = 0; i < keys.length; ++i)
                {
                    var key_name = keys[i];
                    found_record.set(key_name,record.get(key_name));
                }
                found_record.notify('synchronization:afterSave');
            }
        }
    }
    else if(event_name == 'afterDestroy' || event_name == 'afterCreate')
    {
        if(Synchronization.resultSetNotifications[record.tableName])
        {
            for(var synchronized_result_set_count in Synchronization.resultSetNotifications[record.tableName])
            {
                var old_result_set = Synchronization.resultSetNotifications[record.tableName][synchronized_result_set_count].resultSet;
                var new_params = ActiveSupport.clone(Synchronization.resultSetNotifications[record.tableName][synchronized_result_set_count].params);
                var new_result_set = record.constructor.find(ActiveSupport.extend(new_params,{synchronize: false}));
                var splices = Synchronization.spliceArgumentsFromResultSetDiff(old_result_set,new_result_set,event_name);
                for(var i = 0; i < splices.length; ++i)
                {
                    old_result_set.splice.apply(old_result_set,splices[i]);
                }
            }
        }
        if(event_name == 'afterDestroy')
        {
            var found_records = Synchronization.notifications[record.tableName][record.id];
            for(var internal_count_id in found_records)
            {
                if(internal_count_id != record.internalCount)
                {
                    found_records[internal_count_id].notify('synchronization:afterDestroy');
                    Synchronization.notifications[record.tableName][record.id][internal_count_id] = null;
                    delete Synchronization.notifications[record.tableName][record.id][internal_count_id];
                }
            }
        }
    }
};

ActiveSupport.extend(ActiveRecord.InstanceMethods,{
    /**
     * Once synchronized a found instance will have it's values updated if
     * other records with the same id change in the database.
     * @alias ActiveRecord.Instance.synchronize
     * @return {null}
     */
    synchronize: function synchronize()
    {
        Synchronization.setupNotifications(this);
        Synchronization.notifications[this.tableName][this.id][this.internalCount] = this;
    },
    /**
     * Stops the synchronization of the record with the database.
     * @alias ActiveRecord.Instance.stop
     * @return {null}
     */
    stop: function stop()
    {
        Synchronization.setupNotifications(this);
        Synchronization.notifications[this.tableName][this.id][this.internalCount] = null;
        delete Synchronization.notifications[this.tableName][this.id][this.internalCount];
    }
});

Synchronization.synchronizedResultSetCount = 0;

Synchronization.synchronizeResultSet = function synchronizeResultSet(klass,params,result_set)
{
    result_set.synchronize = function synchronize(){

    };
    ++Synchronization.synchronizedResultSetCount;
    if(!Synchronization.resultSetNotifications[klass.tableName])
    {
        Synchronization.resultSetNotifications[klass.tableName] = {};
    }
    Synchronization.resultSetNotifications[klass.tableName][Synchronization.synchronizedResultSetCount] = {
        resultSet: result_set,
        params: params
    };
    for(var i = 0; i < result_set.length; ++i)
    {
        result_set[i].synchronize();
    }
    result_set.stop = (function result_set_synchronization_stop_generator(table_name,synchronized_result_set_count){
        return function stop(){
            for(var i = 0; i < this.length; ++i)
            {
                this[i].stop();
            }
            Synchronization.resultSetNotifications[table_name][synchronized_result_set_count] = null;
            delete Synchronization.resultSetNotifications[table_name][synchronized_result_set_count];
        };
    })(klass.tableName,Synchronization.synchronizedResultSetCount);
};

Synchronization.spliceArgumentsFromResultSetDiff = function spliceArgumentsFromResultSetDiff(a,b,event_name)
{
    var diffs = [];
    if(event_name == 'afterCreate')
    {
        for(var i = 0; i < b.length; ++i)
        {
            if(!a[i] || (a[i] && (a[i].id != b[i].id)))
            {
                diffs.push([i,null,b[i]]);
                break;
            }
        }
    }
    else if(event_name == 'afterDestroy')
    {
        for(var i = 0; i < a.length; ++i)
        {
            if(!b[i] || (b[i] && (b[i].id != a[i].id)))
            {
                diffs.push([i,1]);
                break;
            }
        }
    }
    return diffs;
};

ActiveRecord.Synchronization = Synchronization;<|endoftext|>


<commit_before>var fs = require('fs');
var obj = JSON.parse(fs.readFileSync('./quotes.json', 'utf8'));
const db = require('monk')('127.0.0.1/zimon');
const quotes = db.get('quotes');

//Remove old quotes, in case there is something wrong stored in the db and also to avoid duplicates
quotes.remove({}, function(){
  //Insert each element from the quotes config file
  obj.forEach(function(el){
    quotes.insert({text: el.quote})
  });
});<commit_msg>Bugfix for the config quote file<commit_after>var fs = require('fs');
var obj = JSON.parse(fs.readFileSync('./config/quotes.json', 'utf8'));
const db = require('monk')('127.0.0.1/zimon');
const quotes = db.get('quotes');

//Remove old quotes, in case there is something wrong stored in the db and also to avoid duplicates
quotes.remove({}, function(){
  //Insert each element from the quotes config file
  obj.forEach(function(el){
    quotes.insert({text: el.quote})
  });
});<|endoftext|>


<commit_before><commit_msg>Refactor destruct<commit_after><|endoftext|>


<commit_before>var z = {
    win: $(window),
    body: $(document.body),
    page: $('#page'),
    prefix: (function() {
        var s = window.getComputedStyle(document.body, '');
        return (Array.prototype.slice.call(s).join('').match(/moz|webkit|ms|khtml/)||(s.OLink===''&&['o']))[0];
    })(),
    prefixed: function(property) {
        if (!z.prefix) return property;
        return '-' + z.prefix + '-' + property;
    },
    canInstallApps: true,
};

var data_user = $('body').data('user');
_.extend(z, {
    anonymous: data_user.anonymous,
    pre_auth: data_user.pre_auth
});

(function() {
    _.extend(z, {'nav': BrowserUtils()});
    if (!z.nav.browser.firefox || z.nav.browser.mobile ||
        VersionCompare.compareVersions(z.nav.browserVersion, '15.0a1') < 0) {
        z.canInstallApps = false;
    }
})();

$(document).ready(function() {
    // Initialize email links.
    $('span.emaillink').each(function() {
        var $this = $(this);
        $this.find('.i').remove();
        var em = $this.text().split('').reverse().join('');
        $this.prev('a').attr('href', 'mailto:' + em);
    });

    // Initialize webtrends tracking.
    webtrendsAsyncInit();

    // Fake placeholders if we need to.
    $('input[placeholder]').placeholder();
    if (z.readonly) {
        $('form[method=post]')
            .before(gettext('This feature is temporarily disabled while we ' +
                            'perform website maintenance. Please check back ' +
                            'a little later.'))
            .find('button, input, select, textarea').attr('disabled', true).addClass('disabled');
    }
    var data_user = $('body').data('user');
    _.extend(z, {
        anonymous: data_user.anonymous,
        pre_auth: data_user.pre_auth
    });

    if (!z.canInstallApps) {
        $(window).trigger('app_install_disabled');
    }

    z.apps = {};
    if (z.capabilities.webApps) {
        // Get list of installed apps and mark as such.
        r = window.navigator.mozApps.getInstalled();
        r.onsuccess = function() {
            _.each(r.result, function(val) {
                z.apps[val.manifestURL] = val;
                $(window).trigger('app_install_success',
                                  [val, {'manifest_url': val.manifestURL}, false]);
            });
        };
    }
});

/* Fake the placeholder attribute since Firefox 3.6 doesn't support it. */
jQuery.fn.placeholder = function(new_value) {
    /* Bail early if we have built-in placeholder support. */
    if ('placeholder' in document.createElement('input')) {
        return this;
    }

    if (new_value) {
        this.attr('placeholder', new_value);
    }

    if (new_value && this.hasClass('placeholder')) {
        this.val('').blur();
    }

    return this.focus(function() {
        var $this = $(this),
            text = $this.attr('placeholder');

        if ($this.val() == text) {
            $this.val('').removeClass('placeholder');
        }
    }).blur(function() {
        var $this = $(this),
            text = $this.attr('placeholder');

        if ($this.val() == '') {
            $this.val(text).addClass('placeholder');
        }
    }).each(function(){
        /* Remove the placeholder text before submitting the form. */
        var self = $(this);
        self.closest('form').submit(function() {
            if (self.hasClass('placeholder')) {
                self.val('');
            }
        });
    }).blur();
};

function _pd(func) {
    // Prevent-default function wrapper.
    return function(e) {
        e.preventDefault();
        func.apply(this, arguments);
    };
}

function escape_(s) {
    if (typeof s === 'undefined') {
        return;
    }
    return s.replace(/&/g, '&').replace(/>/g, '>').replace(/</g, '<')
            .replace(/'/g, ''').replace(/"/g, '"');
}

z.receiveMessage = function(cb) {
    // Because jQuery chokes, do cross-browser receiving for `postMessage`.
    if (window.addEventListener) {
        window.addEventListener('message', cb, false);
    } else {
        window.attachEvent('onmessage', cb);
    }
};
z.anonymous = JSON.parse(document.body.getAttribute('data-anonymous'));
z.media_url = document.body.getAttribute('data-media-url');
z.readonly = JSON.parse(document.body.getAttribute('data-readonly'));
z.apps = true;
<commit_msg>nuke old webtrends tracking from devhub<commit_after>var z = {
    win: $(window),
    body: $(document.body),
    page: $('#page'),
    prefix: (function() {
        var s = window.getComputedStyle(document.body, '');
        return (Array.prototype.slice.call(s).join('').match(/moz|webkit|ms|khtml/)||(s.OLink===''&&['o']))[0];
    })(),
    prefixed: function(property) {
        if (!z.prefix) return property;
        return '-' + z.prefix + '-' + property;
    },
    canInstallApps: true
};

var data_user = $('body').data('user');
_.extend(z, {
    anonymous: data_user.anonymous,
    pre_auth: data_user.pre_auth
});

(function() {
    _.extend(z, {'nav': BrowserUtils()});
    if (!z.nav.browser.firefox || z.nav.browser.mobile ||
        VersionCompare.compareVersions(z.nav.browserVersion, '15.0a1') < 0) {
        z.canInstallApps = false;
    }
})();

$(document).ready(function() {
    // Initialize email links.
    $('span.emaillink').each(function() {
        var $this = $(this);
        $this.find('.i').remove();
        var em = $this.text().split('').reverse().join('');
        $this.prev('a').attr('href', 'mailto:' + em);
    });

    // Fake placeholders if we need to.
    $('input[placeholder]').placeholder();
    if (z.readonly) {
        $('form[method=post]')
            .before(gettext('This feature is temporarily disabled while we ' +
                            'perform website maintenance. Please check back ' +
                            'a little later.'))
            .find('button, input, select, textarea').attr('disabled', true).addClass('disabled');
    }
    var data_user = $('body').data('user');
    _.extend(z, {
        anonymous: data_user.anonymous,
        pre_auth: data_user.pre_auth
    });

    if (!z.canInstallApps) {
        $(window).trigger('app_install_disabled');
    }

    z.apps = {};
    if (z.capabilities.webApps) {
        // Get list of installed apps and mark as such.
        r = window.navigator.mozApps.getInstalled();
        r.onsuccess = function() {
            _.each(r.result, function(val) {
                z.apps[val.manifestURL] = val;
                $(window).trigger('app_install_success',
                                  [val, {'manifest_url': val.manifestURL}, false]);
            });
        };
    }
});

/* Fake the placeholder attribute since Firefox 3.6 doesn't support it. */
jQuery.fn.placeholder = function(new_value) {
    /* Bail early if we have built-in placeholder support. */
    if ('placeholder' in document.createElement('input')) {
        return this;
    }

    if (new_value) {
        this.attr('placeholder', new_value);
    }

    if (new_value && this.hasClass('placeholder')) {
        this.val('').blur();
    }

    return this.focus(function() {
        var $this = $(this),
            text = $this.attr('placeholder');

        if ($this.val() == text) {
            $this.val('').removeClass('placeholder');
        }
    }).blur(function() {
        var $this = $(this),
            text = $this.attr('placeholder');

        if ($this.val() === '') {
            $this.val(text).addClass('placeholder');
        }
    }).each(function(){
        /* Remove the placeholder text before submitting the form. */
        var self = $(this);
        self.closest('form').submit(function() {
            if (self.hasClass('placeholder')) {
                self.val('');
            }
        });
    }).blur();
};

function _pd(func) {
    // Prevent-default function wrapper.
    return function(e) {
        e.preventDefault();
        func.apply(this, arguments);
    };
}

function escape_(s) {
    if (typeof s === 'undefined') {
        return;
    }
    return s.replace(/&/g, '&').replace(/>/g, '>').replace(/</g, '<')
            .replace(/'/g, ''').replace(/"/g, '"');
}

z.receiveMessage = function(cb) {
    // Because jQuery chokes, do cross-browser receiving for `postMessage`.
    if (window.addEventListener) {
        window.addEventListener('message', cb, false);
    } else {
        window.attachEvent('onmessage', cb);
    }
};
z.anonymous = JSON.parse(document.body.getAttribute('data-anonymous'));
z.media_url = document.body.getAttribute('data-media-url');
z.readonly = JSON.parse(document.body.getAttribute('data-readonly'));
z.apps = true;
<|endoftext|>


<commit_before>/*
NBComet:  Jupyter Notebook extension to track notebook history
*/

define([
    'jquery',
    'base/js/namespace',
    'base/js/utils',
    'base/js/events',
    'notebook/js/cell'
], function(
    $,
    Jupyter,
    utils,
    events,
    cell
){

// VARIABLES
    // Reference object constructors so we can patch their functions
    var Notebook = Jupyter.notebook;
    var ActionHandler = Jupyter.actions;

    // Actions to track. For all available actions see:
    // https://github.com/jupyter/notebook/blob/master/notebook/static/notebook/js/actions.js
    var actions_to_intercept = [
        // execute cells
        'run-cell',
        'run-cell-and-select-next',
        'run-cell-and-insert-below',
        'run-all-cells',
        'run-all-cells-above',
        'run-all-cells-below',
        'confirm-restart-kernel-and-run-all-cells',
        // delete cells
        'delete-cell',
        'undo-cell-deletion',
        // split and merge cells
        'split-cell-at-cursor',
        'merge-cell-with-previous-cell',
        'merge-cell-with-next-cell',
        'merge-selected-cells',
        'merge-cells',
        // insert cells
        'insert-cell-above',
        'insert-cell-below',
        // move cells
        'move-cell-down',
        'move-cell-up',
        // change cell type
        'change-cell-to-markdown',
        'change-cell-to-code',
        'change-cell-to-raw',
        // change display of cell output
        'clear-cell-output',
        'restart-kernel-and-clear-output',
        'toggle-cell-output-collapsed',
        'toggle-cell-output-scrolled',
        'confirm-restart-kernel-and-clear-output'
        // not tracking cut, copy, paste due to inconsistent calling of actions
        // e.g. in Notebok v 5.0.0, paste menu items do not call paste actions
        // also copy-paste shortcuts (e.g., CMD-C) are handled by the browser
        // cut and paste
        // 'cut-cell',
        // 'copy-cell',
        // 'paste-cell-above',
        // 'paste-cell-below',
        // 'paste-cell-replace',
    ];

    // Track cells that have been edited but not executed
    var cellsWithUnexecutedEdits = [];

// TRACK NOTEBOOK OPEN AND CLOSE
    function trackNotebookOpenClose(){
        /* track notebook open and close events */
        trackAction(Notebook, Date.now(), 'notebook-opened', 0, [0]);
        window.onbeforeunload = function(event) {
            trackAction(Notebook, Date.now(), 'notebook-closed', 0, [0]);
        }
    }

// TRACK NOTEBOOK MOVE / RENAME
    function trackRename(){
        oldRename = Notebook.__proto__.rename
        Notebook.__proto__.rename = function(){
            new_name = arguments[0]

            // POST request to rename files
            // rename folder
            // rename db
            // rename ipynb

            return oldRename.apply(this, arguments)
        }
    }

// GENERATE COMET MENU
    function initializeCometMenu(){
        renderCometMenu();
        verifyCometSettings();
        displayCometRecordingStatus();
    }

    function renderCometMenu(){
        /* add menu after help menu for managing Comet recording */
        var mainMenu = $('#help_menu').parent().parent();
        mainMenu.append($('<li>')
            .addClass('dropdown')
            .attr('id', 'comet-header')
            .append($('<a>')
                .addClass('dropdown-toggle')
                .attr('href','#')
                .attr('data-toggle', 'dropdown')
                .text('Comet')
                )
            );

        var cometHeader = $('#comet-header')
        cometHeader.append($('<ul>')
            .addClass('dropdown-menu')
            .attr('id', 'comet-menu')
        );

        var cometMenu = $('#comet-menu');
        cometMenu.append($('<li>')
            .attr('id', 'comet_settings')
            .append($('<a>')
                .attr('href', '#')
                .text('Toggle Recording')
                .click(toggleCometRecording)
            )
        );

        cometMenu.append($('<li>')
            .attr('id', 'comet_settings')
            .append($('<a>')
                .attr('href', '/api/nbcomet/' + Notebook.notebook_path)
                .text('See Comet Data')
            )
        );
    }

    function verifyCometSettings(){
        /* ensure that the notebook has a comet_tracking setting */
        if (Notebook.metadata.comet_tracking === undefined){
            Notebook.metadata.comet_tracking = true;
        }
        // Generate a random 13-digit hexadecimal string to uniquely identify notebook
        if (Notebook.metadata.comet_paths === undefined){
            Notebook.metadata.comet_paths = [];
            cells = Notebook.get_cells()
            for(i = 0; i < cells.length; i++){
                if (cells[i].metadata.comet_cell_id === undefined){
                    cells[i].metadata.comet_cell_id = Math.random().toString(16).substring(2);
                }
            }
        }
    }

    function toggleCometRecording(){
        /* turn recording on and off */
        if(Notebook.metadata.comet_tracking){
            trackAction(Notebook, Date.now(), 'comet-tracking-off', 0, [0]);
            Notebook.metadata.comet_tracking = !Notebook.metadata.comet_tracking;
        }
        else{
            Notebook.metadata.comet_tracking = !Notebook.metadata.comet_tracking;
            trackAction(Notebook, Date.now(), 'comet-tracking-on', 0, [0]);
        }
        displayCometRecordingStatus();
    }

    function displayCometRecordingStatus(){
        /* Set message and update menu-items when tracking turned on / off */
        var message = 'Comet Tracking off';
        var menuText = 'Start Tracking';
        if(Notebook.metadata.comet_tracking){
            message = 'Comet Tracking on';
            menuText = 'Stop Tracking';
        }
        $('#comet_settings').find('a').text(menuText);
        Jupyter.notification_area.widget('notebook').set_message(message, 2000)
    }

// SEND ACTION DATA TO SERVER
    function trackAction(notebook, t, actionName, selectedIndex,
                        selectedIndices){
        /* Send information about data to Comet Server to process */
        if(Notebook.metadata.comet_tracking){
            /* Send data about the action to the Comet server extension */
            var baseUrl = notebook.base_url;
            var notebookUrl =  notebook.notebook_path;
            var url = utils.url_path_join(baseUrl, 'api/nbcomet', notebookUrl);

            var mod = notebook.toJSON();
            var d = JSON.stringify({
                time: t,
                name: actionName,
                index: selectedIndex,
                indices: selectedIndices,
                model: mod
            });

            var settings = {
                processData : false,
                type : 'POST',
                dataType: 'json',
                data: d,
                contentType: 'application/json',
            };

            utils.promising_ajax(url, settings).then(function(value){
                var hashed_nb_path = value['hashed_nb_path']
                var paths = Notebook.metadata.comet_paths

                if(paths.length == 0){
                    var t = Date.now();
                    paths.push([hashed_nb_path, t])
                }
                else if(paths[paths.length-1][0] != hashed_nb_path){
                    var t = Date.now();
                    paths.push([hashed_nb_path, t])
                }
            });
        }
    }

// TRACK COPY PASTE
    function patchCutCopyPaste(){
        /* Track when cells are cut, copied, and pasted */
        // TODO the 'cut_cell' function calls the copy function, so for now cut
        // actions will be tracked twice, and data need to be cleaned later
        var oldCut = Notebook.__proto__.cut_cell;
        var oldCopy = Notebook.__proto__.copy_cell;
        var oldPasteReplace = Notebook.__proto__.paste_cell_replace;
        var oldPasteAbove = Notebook.__proto__.paste_cell_above;
        var oldPasteBelow = Notebook.__proto__.paste_cell_below;

        Notebook.__proto__.cut_cell = function(){
            var t = Date.now();
            var selectedIndex = this.get_selected_index();
            var selectedIndices = this.get_selected_cells_indices();

            oldCut.apply(this, arguments);

            trackAction(this, t, 'cut-cell', selectedIndex, selectedIndices);
        }

        Notebook.__proto__.copy_cell = function(){
            var t = Date.now();
            var selectedIndex = this.get_selected_index();
            var selectedIndices = this.get_selected_cells_indices();

            oldCopy.apply(this, arguments);

            trackAction(this, t, 'copy-cell', selectedIndex, selectedIndices);
        }

        Notebook.__proto__.paste_cell_replace = function(){
            var t = Date.now();
            var selectedIndex = this.get_selected_index();
            var selectedIndices = this.get_selected_cells_indices();

            oldPasteReplace.apply(this, arguments);

            trackAction(this, t, 'paste-cell-replace', selectedIndex,
                        selectedIndices);
        }

        Notebook.__proto__.paste_cell_above = function(){
            var t = Date.now();
            var selectedIndex = this.get_selected_index();
            var selectedIndices = this.get_selected_cells_indices();

            oldPasteAbove.apply(this, arguments);

            trackAction(this, t, 'paste-cell-above', selectedIndex,
                        selectedIndices);
        }

        Notebook.__proto__.paste_cell_below = function(){
            var t = Date.now();
            var selectedIndex = this.get_selected_index();
            var selectedIndices = this.get_selected_cells_indices();

            oldPasteBelow.apply(this, arguments);

            trackAction(this, t, 'paste-cell-below', selectedIndex,
                        selectedIndices);
        }

        // listen for broswer-initiated (e.g. hotkey) cut, copy, paste events
        document.addEventListener('cut', function(){
            if (Notebook.mode == 'command') {
                var t = Date.now();
                var selectedIndex = Notebook.get_selected_index();
                var selectedIndices = Notebook.get_selected_cells_indices();
                trackAction(Notebook, t, 'cut-cell', selectedIndex,
                            selectedIndices);
            }
        });

        document.addEventListener('copy', function(){
            if (Notebook.mode == 'command') {
                var t = Date.now();
                var selectedIndex = Notebook.get_selected_index();
                var selectedIndices = Notebook.get_selected_cells_indices();
                trackAction(Notebook, t, 'copy-cell', selectedIndex,
                            selectedIndices);
            }
        });

        document.addEventListener('paste', function(){
            if (Notebook.mode == 'command') {
                var t = Date.now();
                var selectedIndex = Notebook.get_selected_index();
                var selectedIndices = Notebook.get_selected_cells_indices();
                trackAction(Notebook, t, 'paste-cell-below', selectedIndex,
                            selectedIndices);
            }
        });

    }

// TRACK ACTIONS
    function patchActionHandlerCall(){
        /* Track desired actions */
        var oldCall = ActionHandler.__proto__.call;
        ActionHandler.__proto__.call = function(){
            // remove 'jupter-notebook:' prefix
            var actionName = arguments[0].split(':')[1];
            var actionInList = actions_to_intercept.indexOf(actionName) > -1;
            if(!actionInList){
                oldCall.apply(this, arguments);
            }
            else{
                // oldCall.apply(this, arguments);
                var t = Date.now();
                var selectedIndex = Notebook.get_selected_index();
                var selectedIndices = Notebook.get_selected_cells_indices();

                // Remove executed cells from list of cells with unexecuted changes
                if(actionName.substring(0,3) == 'run'){
                    for(i = 0; i < selectedIndices.length; i++){
                        var j = cellsWithUnexecutedEdits.indexOf(
                            selectedIndices[i])
                        if(j > -1){
                            cellsWithUnexecutedEdits.splice(j, 1);
                        }
                    }
                }

                function trackActionAfterExecution(evt){
                    trackAction(Notebook, t, actionName, selectedIndex,
                                selectedIndices);
                    events.off('kernel_idle.Kernel', trackActionAfterExecution)
                }

                // if executing a Code cell wait for execution to finish
                // notebook v. 5.0.0 added the `finished_execute.CodeCell` event
                // that could make this easier for later versions
                //TODO check if it needs to be a code type cell, or if markdown also does this

                if((actionName.substring(0,3) == 'run'
                    && Notebook.get_cell(selectedIndex).cell_type == 'code')
                    || actionName == 'confirm-restart-kernel-and-run-all-cells'){
                    events.on('kernel_idle.Kernel', trackActionAfterExecution);
                    oldCall.apply(this, arguments);
                }
                // if not executing a code cell just track the action immediately
                else{
                    oldCall.apply(this, arguments);
                    trackAction(this.env.notebook, t, actionName, selectedIndex,
                                selectedIndices);
                }
            }
        }
    }

// TRACK CHANGES TO UNEXECUTED CELLS
    function trackUnexecutedCellChanges(){
        /* Get currently rendered cells to track unexecuted changes */
        patchCellUnselect();
        var cells = Notebook.get_cells();
        for(var i = 0; i < cells.length; i++){
            trackChangesToCell(cells[i]);
        }
        patchInsertCellAtIndex();
    }

    function patchCellUnselect(){
        /* Track when cells are edited but not executed */
        oldCellUnselect = cell.Cell.prototype.unselect;
        cell.Cell.prototype.unselect = function(){
            var i = Notebook.get_selected_index();
            var li = cellsWithUnexecutedEdits.indexOf(i)
            var unexecutedChanges = li > -1;
            if(this.selected && unexecutedChanges){
                var t = Date.now();
                var selectedIndices = Notebook.get_selected_cells_indices();
                trackAction(Notebook, t, 'unselect-cell', i, selectedIndices);
                cellsWithUnexecutedEdits.splice(li, 1);
            }
            oldCellUnselect.apply(this);
        }
    }

    function patchInsertCellAtIndex(){
        /* Get newly created cells to track unexecuted changes */
        var oldInsertCellAtIndex = Notebook.__proto__.insert_cell_at_index;
        Notebook.__proto__.insert_cell_at_index = function(){
            c = oldInsertCellAtIndex.apply(this, arguments);
            trackChangesToCell(c);
            c.metadata.comet_cell_id = Math.random().toString(16).substring(2);
            return c;
        }
    }

    function trackChangesToCell(c){
        /* Track unexecuted changes for a particular cell */
        c.code_mirror.on('change', function(){
            var i = Notebook.get_selected_index();
            if(cellsWithUnexecutedEdits.indexOf(i) == -1){
                cellsWithUnexecutedEdits.push(i);
            }
        });
    }

// LOAD EXTENSION
    function load_extension(){
        /* Called as extension loads and notebook opens */
        console.log('[NBComet] tracking changes to notebook');
        trackNotebookOpenClose();
        initializeCometMenu();
        patchActionHandlerCall();
        patchCutCopyPaste();
        trackUnexecutedCellChanges();
        trackRename();
    }

    return {
        load_jupyter_extension: load_extension,
        load_ipython_extension: load_extension
    };
});
<commit_msg>fix potential issue with checking that cells have unique id<commit_after>/*
NBComet:  Jupyter Notebook extension to track notebook history
*/

define([
    'jquery',
    'base/js/namespace',
    'base/js/utils',
    'base/js/events',
    'notebook/js/cell'
], function(
    $,
    Jupyter,
    utils,
    events,
    cell
){

// VARIABLES
    // Reference object constructors so we can patch their functions
    var Notebook = Jupyter.notebook;
    var ActionHandler = Jupyter.actions;

    // Actions to track. For all available actions see:
    // https://github.com/jupyter/notebook/blob/master/notebook/static/notebook/js/actions.js
    var actions_to_intercept = [
        // execute cells
        'run-cell',
        'run-cell-and-select-next',
        'run-cell-and-insert-below',
        'run-all-cells',
        'run-all-cells-above',
        'run-all-cells-below',
        'confirm-restart-kernel-and-run-all-cells',
        // delete cells
        'delete-cell',
        'undo-cell-deletion',
        // split and merge cells
        'split-cell-at-cursor',
        'merge-cell-with-previous-cell',
        'merge-cell-with-next-cell',
        'merge-selected-cells',
        'merge-cells',
        // insert cells
        'insert-cell-above',
        'insert-cell-below',
        // move cells
        'move-cell-down',
        'move-cell-up',
        // change cell type
        'change-cell-to-markdown',
        'change-cell-to-code',
        'change-cell-to-raw',
        // change display of cell output
        'clear-cell-output',
        'restart-kernel-and-clear-output',
        'toggle-cell-output-collapsed',
        'toggle-cell-output-scrolled',
        'confirm-restart-kernel-and-clear-output'
        // not tracking cut, copy, paste due to inconsistent calling of actions
        // e.g. in Notebok v 5.0.0, paste menu items do not call paste actions
        // also copy-paste shortcuts (e.g., CMD-C) are handled by the browser
        // cut and paste
        // 'cut-cell',
        // 'copy-cell',
        // 'paste-cell-above',
        // 'paste-cell-below',
        // 'paste-cell-replace',
    ];

    // Track cells that have been edited but not executed
    var cellsWithUnexecutedEdits = [];

// TRACK NOTEBOOK OPEN AND CLOSE
    function trackNotebookOpenClose(){
        /* track notebook open and close events */
        trackAction(Notebook, Date.now(), 'notebook-opened', 0, [0]);
        window.onbeforeunload = function(event) {
            trackAction(Notebook, Date.now(), 'notebook-closed', 0, [0]);
        }
    }

// TRACK NOTEBOOK MOVE / RENAME
    function trackRename(){
        oldRename = Notebook.__proto__.rename
        Notebook.__proto__.rename = function(){
            new_name = arguments[0]

            // POST request to rename files
            // rename folder
            // rename db
            // rename ipynb

            return oldRename.apply(this, arguments)
        }
    }

// GENERATE COMET MENU
    function initializeCometMenu(){
        renderCometMenu();
        verifyCometSettings();
        displayCometRecordingStatus();
    }

    function renderCometMenu(){
        /* add menu after help menu for managing Comet recording */
        var mainMenu = $('#help_menu').parent().parent();
        mainMenu.append($('<li>')
            .addClass('dropdown')
            .attr('id', 'comet-header')
            .append($('<a>')
                .addClass('dropdown-toggle')
                .attr('href','#')
                .attr('data-toggle', 'dropdown')
                .text('Comet')
                )
            );

        var cometHeader = $('#comet-header')
        cometHeader.append($('<ul>')
            .addClass('dropdown-menu')
            .attr('id', 'comet-menu')
        );

        var cometMenu = $('#comet-menu');
        cometMenu.append($('<li>')
            .attr('id', 'comet_settings')
            .append($('<a>')
                .attr('href', '#')
                .text('Toggle Recording')
                .click(toggleCometRecording)
            )
        );

        cometMenu.append($('<li>')
            .attr('id', 'comet_settings')
            .append($('<a>')
                .attr('href', '/api/nbcomet/' + Notebook.notebook_path)
                .text('See Comet Data')
            )
        );
    }

    function verifyCometSettings(){
        /* ensure that the notebook has a comet_tracking setting */
        if (Notebook.metadata.comet_tracking === undefined){
            Notebook.metadata.comet_tracking = true;
        }
        // Generate a random 13-digit hexadecimal string to uniquely identify notebook
        if (Notebook.metadata.comet_paths === undefined){
            Notebook.metadata.comet_paths = [];
        }
        // check that cells have right metadata
        cells = Notebook.get_cells()
        for(i = 0; i < cells.length; i++){
            if (cells[i].metadata.comet_cell_id === undefined){
                cells[i].metadata.comet_cell_id = Math.random().toString(16).substring(2);
            }
        }
    }

    function toggleCometRecording(){
        /* turn recording on and off */
        if(Notebook.metadata.comet_tracking){
            trackAction(Notebook, Date.now(), 'comet-tracking-off', 0, [0]);
            Notebook.metadata.comet_tracking = !Notebook.metadata.comet_tracking;
        }
        else{
            Notebook.metadata.comet_tracking = !Notebook.metadata.comet_tracking;
            trackAction(Notebook, Date.now(), 'comet-tracking-on', 0, [0]);
        }
        displayCometRecordingStatus();
    }

    function displayCometRecordingStatus(){
        /* Set message and update menu-items when tracking turned on / off */
        var message = 'Comet Tracking off';
        var menuText = 'Start Tracking';
        if(Notebook.metadata.comet_tracking){
            message = 'Comet Tracking on';
            menuText = 'Stop Tracking';
        }
        $('#comet_settings').find('a').text(menuText);
        Jupyter.notification_area.widget('notebook').set_message(message, 2000)
    }

// SEND ACTION DATA TO SERVER
    function trackAction(notebook, t, actionName, selectedIndex,
                        selectedIndices){
        /* Send information about data to Comet Server to process */
        if(Notebook.metadata.comet_tracking){
            /* Send data about the action to the Comet server extension */
            var baseUrl = notebook.base_url;
            var notebookUrl =  notebook.notebook_path;
            var url = utils.url_path_join(baseUrl, 'api/nbcomet', notebookUrl);

            var mod = notebook.toJSON();
            var d = JSON.stringify({
                time: t,
                name: actionName,
                index: selectedIndex,
                indices: selectedIndices,
                model: mod
            });

            var settings = {
                processData : false,
                type : 'POST',
                dataType: 'json',
                data: d,
                contentType: 'application/json',
            };

            utils.promising_ajax(url, settings).then(function(value){
                var hashed_nb_path = value['hashed_nb_path']
                var paths = Notebook.metadata.comet_paths

                if(paths.length == 0){
                    var t = Date.now();
                    paths.push([hashed_nb_path, t])
                }
                else if(paths[paths.length-1][0] != hashed_nb_path){
                    var t = Date.now();
                    paths.push([hashed_nb_path, t])
                }
            });
        }
    }

// TRACK COPY PASTE
    function patchCutCopyPaste(){
        /* Track when cells are cut, copied, and pasted */
        // TODO the 'cut_cell' function calls the copy function, so for now cut
        // actions will be tracked twice, and data need to be cleaned later
        var oldCut = Notebook.__proto__.cut_cell;
        var oldCopy = Notebook.__proto__.copy_cell;
        var oldPasteReplace = Notebook.__proto__.paste_cell_replace;
        var oldPasteAbove = Notebook.__proto__.paste_cell_above;
        var oldPasteBelow = Notebook.__proto__.paste_cell_below;

        Notebook.__proto__.cut_cell = function(){
            var t = Date.now();
            var selectedIndex = this.get_selected_index();
            var selectedIndices = this.get_selected_cells_indices();

            oldCut.apply(this, arguments);

            trackAction(this, t, 'cut-cell', selectedIndex, selectedIndices);
        }

        Notebook.__proto__.copy_cell = function(){
            var t = Date.now();
            var selectedIndex = this.get_selected_index();
            var selectedIndices = this.get_selected_cells_indices();

            oldCopy.apply(this, arguments);

            trackAction(this, t, 'copy-cell', selectedIndex, selectedIndices);
        }

        Notebook.__proto__.paste_cell_replace = function(){
            var t = Date.now();
            var selectedIndex = this.get_selected_index();
            var selectedIndices = this.get_selected_cells_indices();

            oldPasteReplace.apply(this, arguments);

            trackAction(this, t, 'paste-cell-replace', selectedIndex,
                        selectedIndices);
        }

        Notebook.__proto__.paste_cell_above = function(){
            var t = Date.now();
            var selectedIndex = this.get_selected_index();
            var selectedIndices = this.get_selected_cells_indices();

            oldPasteAbove.apply(this, arguments);

            trackAction(this, t, 'paste-cell-above', selectedIndex,
                        selectedIndices);
        }

        Notebook.__proto__.paste_cell_below = function(){
            var t = Date.now();
            var selectedIndex = this.get_selected_index();
            var selectedIndices = this.get_selected_cells_indices();

            oldPasteBelow.apply(this, arguments);

            trackAction(this, t, 'paste-cell-below', selectedIndex,
                        selectedIndices);
        }

        // listen for broswer-initiated (e.g. hotkey) cut, copy, paste events
        document.addEventListener('cut', function(){
            if (Notebook.mode == 'command') {
                var t = Date.now();
                var selectedIndex = Notebook.get_selected_index();
                var selectedIndices = Notebook.get_selected_cells_indices();
                trackAction(Notebook, t, 'cut-cell', selectedIndex,
                            selectedIndices);
            }
        });

        document.addEventListener('copy', function(){
            if (Notebook.mode == 'command') {
                var t = Date.now();
                var selectedIndex = Notebook.get_selected_index();
                var selectedIndices = Notebook.get_selected_cells_indices();
                trackAction(Notebook, t, 'copy-cell', selectedIndex,
                            selectedIndices);
            }
        });

        document.addEventListener('paste', function(){
            if (Notebook.mode == 'command') {
                var t = Date.now();
                var selectedIndex = Notebook.get_selected_index();
                var selectedIndices = Notebook.get_selected_cells_indices();
                trackAction(Notebook, t, 'paste-cell-below', selectedIndex,
                            selectedIndices);
            }
        });

    }

// TRACK ACTIONS
    function patchActionHandlerCall(){
        /* Track desired actions */
        var oldCall = ActionHandler.__proto__.call;
        ActionHandler.__proto__.call = function(){
            // remove 'jupter-notebook:' prefix
            var actionName = arguments[0].split(':')[1];
            var actionInList = actions_to_intercept.indexOf(actionName) > -1;
            if(!actionInList){
                oldCall.apply(this, arguments);
            }
            else{
                // oldCall.apply(this, arguments);
                var t = Date.now();
                var selectedIndex = Notebook.get_selected_index();
                var selectedIndices = Notebook.get_selected_cells_indices();

                // Remove executed cells from list of cells with unexecuted changes
                if(actionName.substring(0,3) == 'run'){
                    for(i = 0; i < selectedIndices.length; i++){
                        var j = cellsWithUnexecutedEdits.indexOf(
                            selectedIndices[i])
                        if(j > -1){
                            cellsWithUnexecutedEdits.splice(j, 1);
                        }
                    }
                }

                function trackActionAfterExecution(evt){
                    trackAction(Notebook, t, actionName, selectedIndex,
                                selectedIndices);
                    events.off('kernel_idle.Kernel', trackActionAfterExecution)
                }

                // if executing a Code cell wait for execution to finish
                // notebook v. 5.0.0 added the `finished_execute.CodeCell` event
                // that could make this easier for later versions
                //TODO check if it needs to be a code type cell, or if markdown also does this

                if((actionName.substring(0,3) == 'run'
                    && Notebook.get_cell(selectedIndex).cell_type == 'code')
                    || actionName == 'confirm-restart-kernel-and-run-all-cells'){
                    events.on('kernel_idle.Kernel', trackActionAfterExecution);
                    oldCall.apply(this, arguments);
                }
                // if not executing a code cell just track the action immediately
                else{
                    oldCall.apply(this, arguments);
                    trackAction(this.env.notebook, t, actionName, selectedIndex,
                                selectedIndices);
                }
            }
        }
    }

// TRACK CHANGES TO UNEXECUTED CELLS
    function trackUnexecutedCellChanges(){
        /* Get currently rendered cells to track unexecuted changes */
        patchCellUnselect();
        var cells = Notebook.get_cells();
        for(var i = 0; i < cells.length; i++){
            trackChangesToCell(cells[i]);
        }
        patchInsertCellAtIndex();
    }

    function patchCellUnselect(){
        /* Track when cells are edited but not executed */
        oldCellUnselect = cell.Cell.prototype.unselect;
        cell.Cell.prototype.unselect = function(){
            var i = Notebook.get_selected_index();
            var li = cellsWithUnexecutedEdits.indexOf(i)
            var unexecutedChanges = li > -1;
            if(this.selected && unexecutedChanges){
                var t = Date.now();
                var selectedIndices = Notebook.get_selected_cells_indices();
                trackAction(Notebook, t, 'unselect-cell', i, selectedIndices);
                cellsWithUnexecutedEdits.splice(li, 1);
            }
            oldCellUnselect.apply(this);
        }
    }

    function patchInsertCellAtIndex(){
        /* Get newly created cells to track unexecuted changes */
        var oldInsertCellAtIndex = Notebook.__proto__.insert_cell_at_index;
        Notebook.__proto__.insert_cell_at_index = function(){
            c = oldInsertCellAtIndex.apply(this, arguments);
            trackChangesToCell(c);
            c.metadata.comet_cell_id = Math.random().toString(16).substring(2);
            return c;
        }
    }

    function trackChangesToCell(c){
        /* Track unexecuted changes for a particular cell */
        c.code_mirror.on('change', function(){
            var i = Notebook.get_selected_index();
            if(cellsWithUnexecutedEdits.indexOf(i) == -1){
                cellsWithUnexecutedEdits.push(i);
            }
        });
    }

// LOAD EXTENSION
    function load_extension(){
        /* Called as extension loads and notebook opens */
        console.log('[NBComet] tracking changes to notebook');
        trackNotebookOpenClose();
        initializeCometMenu();
        patchActionHandlerCall();
        patchCutCopyPaste();
        trackUnexecutedCellChanges();
        trackRename();
    }

    return {
        load_jupyter_extension: load_extension,
        load_ipython_extension: load_extension
    };
});
<|endoftext|>


<commit_before>(function (root, factory) {
	if(typeof define === "function" && define.amd) {
		define(function(){
			return factory();
		});
	} else if(typeof module === "object" && module.exports) {
		module.exports = factory();
	} else {
		root.ViewEESVGRenderer = factory();
	}
}(this, function () {

	// ---------------
	// --- HELPERS ---
	// ---------------

	var SVGNS = 'http://www.w3.org/2000/svg';

	function polarToCartesian(centerX, centerY, radius, angleInDegrees, angleInRadians) {
		if (!angleInRadians)
			angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;

		return {
			x: centerX + (radius * Math.cos(angleInRadians)),
			y: centerY + (radius * Math.sin(angleInRadians))
		};
	}

	function describeArc(x, y, radius, startAngle, endAngle){

		console.log (startAngle, endAngle);

		var start = polarToCartesian(x, y, radius, startAngle);
		var end   = polarToCartesian(x, y, radius, endAngle);

//		var start = polarToCartesian(x, y, radius, endAngle);
//		var end   = polarToCartesian(x, y, radius, startAngle);

		var arcSweep = endAngle - startAngle <= 180 ? "1" : "0";

		var d = [
			"M", start.x.toFixed (3), start.y.toFixed (3),
			"A", radius, radius, 0, arcSweep, 0, end.x.toFixed (3), end.y.toFixed (3)
		].join(" ");

		return d;
	}

	function describeArcRadians (x, y, radius, startAngle, endAngle){

//		var start = polarToCartesian(x, y, radius, null, startAngle);
//		var end   = polarToCartesian(x, y, radius, null, endAngle);

		var start = polarToCartesian(x, y, radius, null, endAngle + 2*Math.PI);
		var end   = polarToCartesian(x, y, radius, null, startAngle + 2*Math.PI);

		var arcSweep = endAngle - startAngle <= Math.PI ? "0" : "1";

		var d = [
			"M", start.x.toFixed (3), start.y.toFixed (3),
			"A", radius, radius, 0, arcSweep, 0, end.x.toFixed (3), end.y.toFixed (3)
		].join(" ");

		return d;
	}

	// ---------------
	// --- DRAWING ---
	// ---------------

	function SVGRenderer (board) {
		var svg = board.svg;

		this.el    = svg;
		this.board = board;

		this.warnings = [];
	}

	SVGRenderer.prototype = Object.create (ViewEERenderer.prototype);

	SVGRenderer.prototype.getScope = function (ctx, attrs) {

		var g = ctx.querySelector ('g[name="'+attrs.name+'"]');
		if (!g) {
			g = MakeEl ('g', {xmlns: SVGNS, name: attrs.name});
			ctx.appendChild (g);
		}

		for (var a in attrs) {
			g.setAttributeNS (null, a, attrs[a]);
		}

		return g;
	}

	SVGRenderer.prototype.draw = function() {
		var svg = this.el;

		var board = this.board;

		while (svg.firstChild) {
			svg.removeChild (svg.firstChild);
		}

		board.ratio = 1;

		var scaleX = board.scale * board.baseScale * board.ratio * (board.boardFlipped ? -1.0 : 1.0);
		var scaleY = (board.coordYFlip ? 1 : -1) * board.scale  * board.baseScale * board.ratio;
		var scaleTransY = board.coordYFlip ? 0 : parseFloat (svg.style.height);

		var transX = (board.boardFlipped ? -board.nativeBounds[2] : -(board.nativeBounds[0]));
		var transY = -board.nativeBounds[1]; //board.coordYFlip ? 0 : board.nativeBounds[1]; //-board.nativeBounds[1];

		var g;

		svg.appendChild (MakeEl ('g', {
			xmlns: SVGNS,
			transform: 'matrix('+scaleX+', 0, 0, '+scaleY+', 0, '+scaleTransY+')', // board.scale*board.baseScale
		}, g = MakeEl ('g', {
			xmlns: SVGNS,
			className: 'container',
			transform: 'translate('+transX+', '+transY+')'
		})));

		var layerOrder = board.boardFlipped ? board.reverseRenderLayerOrder : board.renderLayerOrder;
		for (var layerKey in layerOrder) {
			var layerId = layerOrder[layerKey];
			if (!board.visibleLayers[layerId]) { continue };
			board.layerRenderFunctions[layerId](this, board, g);
		}

	}

	SVGRenderer.prototype.drawSingleWire = function (wire, ctx) {
		var path = this.drawWire (wire, ctx);
		path.setAttributeNS (null, 'stroke', wire.strokeStyle);
		path.setAttributeNS (null, 'stroke-width', wire.width);
		path.setAttributeNS (null, 'fill', "none");
	}

	SVGRenderer.prototype.drawWire = function (wire, ctx) {

		var attrs = {xmlns: SVGNS};

		var lineDash;
		if (wire.style === "longdash") {
			lineDash = [3];
		} else if (wire.style === "shortdash") {
			lineDash = [1];
		} else if (wire.style === "dashdot") {
			lineDash = [3, 1, 1, 1];
		}

		if (lineDash) attrs["stroke-dasharray"] = lineDash.join (', ');

		if (wire.cap && wire.cap === "flat") {
			attrs["stroke-linecap"] = "butt";
		} else {
			attrs["stroke-linecap"] = "round";
		}

		if (wire.curve) {

			var rotate = (wire.rot ? parseFloat(wire.rot.substr (wire.rot.indexOf ("R") + 1)) : 0)/180*Math.PI;

			var radiusX, radiusY;
			radiusX = radiusY = wire.radius;
			if (wire.radius.constructor === Array) {
				radiusX = wire.radius[0];
				radiusY = wire.radius[1];
			}

			attrs.d = describeArcRadians (wire.x, wire.y, radiusX, rotate + wire.start, rotate + wire.start + wire.angle);
			// attrs.transform = 'scale('+radiusX+','+radiusY+') translate('+wire.x+', '+wire.y+')';
		} else {
			attrs.d = ['M', wire.x1, wire.y1, 'L', wire.x2, wire.y2].join (' ');
		}

		var path = MakeEl ('path', attrs);
		ctx.appendChild (path);
		return path;

	}

	SVGRenderer.prototype.drawHole = function (hole, ctx) {

		var board = this.board;

		ctx.appendChild (MakeEl ('circle', {
			xmlns: SVGNS,
			cx: hole.x,
			cy: hole.y,
			fill: "none",
			r: hole.drill/2 + hole.strokeWidth/2,
			stroke: hole.strokeStyle,
			'stroke-width': hole.strokeWidth,
		}));
	}

	SVGRenderer.prototype.drawText = function (attrs, text, ctx) {
		var x = attrs.x || text.x,
			y = attrs.y || text.y,
			rot = attrs.rot || text.rot || "R0",
			size = text.size,
			flipText = attrs.flipText !== undefined ? attrs.flipText : text.flipText;

		var board = this.board;

		var content = attrs.content || text.content;
		var color   = attrs.color;

		var textAngle = board.angleForRot (rot);

		//rotation from 90.1 to 270 causes Eagle to draw labels 180 degrees rotated with top right anchor point
		var degrees  = textAngle.degrees,
			textRot  = board.matrixForRot(rot),
			fontSize = 10;

		var font = ''+fontSize+'pt vector';	//Use a regular font size - very small sizes seem to mess up spacing / kerning

		var strings = content.split (/\r?\n/);
		var stringOffset = (text.interlinear || 50) * fontSize / 100;

		var scale = size / fontSize;
		var scaleX = scale,
			scaleY = (board.coordYFlip ? 1 : -1)*scale;

		var textCtx = this.getScope (ctx, {
			name: 'text',
			transform: 'matrix('+[textRot[0],textRot[2],textRot[1],textRot[3],x,y].join(', ')+')'
		});

		textCtx.appendChild (MakeEl ('text', {
			xmlns: SVGNS,
			'font-size': fontSize + 'pt',
			'font-family': 'vector',
			transform: 'scale('+scaleX+', '+scaleY+')',
			fill: color
		}, content));

		return;

		if (0) { // enable to draw zero points for text
			ctx.save();
			ctx.beginPath();
			ctx.arc(0, 0, 1, 0, 2 * Math.PI, false);
			ctx.fillStyle = board.viaPadColor();
			ctx.fill();
			ctx.restore();
		}

		ctx.save();
		ctx.fillStyle = color;
		ctx.font = font;
		ctx.translate(x,y);

		ctx.transform (textRot[0],textRot[2],textRot[1],textRot[3],0,0);
		var textBlockHeight = (strings.length - 1) * (stringOffset + fontSize);
		var textBlockWidth = 0;
		strings.forEach (function (string, idx) {
			textBlockWidth = Math.max (textBlockWidth, ctx.measureText(string).width);
		}, this);
		ctx.scale (scaleX, scaleY);

		var xOffset = 0;
		if (flipText) {
			var xMult = {center: 0, left: 1, right: 1};
			var yMult = {middle: 0, bottom: -1, top: 1};
			ctx.translate (
				xMult[text.align || "left"] * textBlockWidth,
				yMult[text.valign || "bottom"] * (textBlockHeight + fontSize)
			);
			if (!textAngle.spin) ctx.scale(-1,-1);
		}

		if (0) { // enable to draw zero points for text origin
			ctx.save();
			ctx.beginPath();
			ctx.arc(0, 0, 1, 0, 2 * Math.PI, false);
			ctx.fillStyle = "b00";
			ctx.fill();
			ctx.restore();
		}

		if (text.align)  ctx.textAlign = text.align;
		if (text.valign) ctx.textBaseline = text.valign;

		strings.forEach (function (string, idx) {
			var yOffset = idx * (stringOffset + fontSize);
			if (text.valign === "middle") {
				yOffset -= textBlockHeight/2;
			} else if (text.valign === "bottom") {
				yOffset -= textBlockHeight;
			}
			ctx.fillText(string, xOffset, yOffset);
		}, this);

		ctx.restore();
	}

	SVGRenderer.prototype.drawFilledPoly = function (poly, ctx) {
		var dAttr = [];
		poly.points.forEach (function (point, idx) {
			if (idx === 0)
				dAttr.push ('M')
			else
				dAttr.push ('L')
			dAttr.push (point.x);
			dAttr.push (point.y);
		});
		dAttr.push ('z');

		ctx.appendChild (MakeEl ('path', {
			className: 'package-rect',
			xmlns: SVGNS,
			d: dAttr.join (' '),
			fill: poly.fillStyle,
			//"stroke-width": board.minLineWidth,
			"stroke-linecap": "round"
		}));
	}

	SVGRenderer.prototype.drawFilledCircle = function (poly, ctx) {

		ctx.appendChild (MakeEl ('circle', {
			className: 'package-circle',
			xmlns: SVGNS,
			cx: poly.x,
			cy: poly.y,
			r: poly.radius,
			fill: poly.fillStyle,
			//"stroke-width": board.minLineWidth,
			"stroke-linecap": "round"
		}));
	}

	SVGRenderer.prototype.dimCanvas = function(ctx, alpha) {

		var board = this.board;

		ctx.appendChild (MakeEl ('rect', {
			xmlns: SVGNS,
			x: board.boardFlipped ? board.nativeBounds[2] : board.nativeBounds[0],
			y: board.nativeBounds[1],
			width: board.nativeSize[0],
			height: board.nativeSize[1],
			fill: 'rgba(255,255,255,'+alpha+')'
		}));
//		ctx.save();
//		ctx.setTransform(1, 0, 0, 1, 0, 0);
//		ctx.globalCompositeOperation = 'destination-out';
//		ctx.fillStyle =
//		ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
//		ctx.restore();
	};

	return SVGRenderer;

}));
<commit_msg>need to add interactivity by design<commit_after>(function (root, factory) {
	if(typeof define === "function" && define.amd) {
		define(function(){
			return factory();
		});
	} else if(typeof module === "object" && module.exports) {
		module.exports = factory();
	} else {
		root.ViewEESVGRenderer = factory();
	}
}(this, function () {

	// ---------------
	// --- HELPERS ---
	// ---------------

	var SVGNS = 'http://www.w3.org/2000/svg';

	function polarToCartesian(centerX, centerY, radius, angleInDegrees, angleInRadians) {
		if (!angleInRadians)
			angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;

		return {
			x: centerX + (radius * Math.cos(angleInRadians)),
			y: centerY + (radius * Math.sin(angleInRadians))
		};
	}

	function describeArc(x, y, radius, startAngle, endAngle){

		console.log (startAngle, endAngle);

		var start = polarToCartesian(x, y, radius, startAngle);
		var end   = polarToCartesian(x, y, radius, endAngle);

//		var start = polarToCartesian(x, y, radius, endAngle);
//		var end   = polarToCartesian(x, y, radius, startAngle);

		var arcSweep = endAngle - startAngle <= 180 ? "1" : "0";

		var d = [
			"M", start.x.toFixed (3), start.y.toFixed (3),
			"A", radius, radius, 0, arcSweep, 0, end.x.toFixed (3), end.y.toFixed (3)
		].join(" ");

		return d;
	}

	function describeArcRadians (x, y, radius, startAngle, endAngle){

//		var start = polarToCartesian(x, y, radius, null, startAngle);
//		var end   = polarToCartesian(x, y, radius, null, endAngle);

		var start = polarToCartesian(x, y, radius, null, endAngle + 2*Math.PI);
		var end   = polarToCartesian(x, y, radius, null, startAngle + 2*Math.PI);

		var arcSweep = endAngle - startAngle <= Math.PI ? "0" : "1";

		var d = [
			"M", start.x.toFixed (3), start.y.toFixed (3),
			"A", radius, radius, 0, arcSweep, 0, end.x.toFixed (3), end.y.toFixed (3)
		].join(" ");

		return d;
	}

	// ---------------
	// --- DRAWING ---
	// ---------------

	function SVGRenderer (board) {
		var svg = board.svg;

		this.el    = svg;
		this.board = board;

		this.warnings = [];
	}

	SVGRenderer.prototype = Object.create (ViewEERenderer.prototype);

	SVGRenderer.prototype.getScope = function (ctx, attrs) {

		var g = ctx.querySelector ('g[name="'+attrs.name+'"]');
		if (!g) {
			g = MakeEl ('g', {xmlns: SVGNS, name: attrs.name});
			ctx.appendChild (g);
		}

		for (var a in attrs) {
			g.setAttributeNS (null, a, attrs[a]);
		}

		return g;
	}

	SVGRenderer.prototype.draw = function() {
		var svg = this.el;

		var board = this.board;

		if (board.interactive && board.interactive.destroy)
			board.interactive.destroy ();

		while (svg.firstChild) {
			svg.removeChild (svg.firstChild);
		}

		board.ratio = 1;

		var scaleX = board.scale * board.baseScale * board.ratio * (board.boardFlipped ? -1.0 : 1.0);
		var scaleY = (board.coordYFlip ? 1 : -1) * board.scale  * board.baseScale * board.ratio;
		var scaleTransY = board.coordYFlip ? 0 : parseFloat (svg.style.height);

		var transX = (board.boardFlipped ? -board.nativeBounds[2] : -(board.nativeBounds[0]));
		var transY = -board.nativeBounds[1]; //board.coordYFlip ? 0 : board.nativeBounds[1]; //-board.nativeBounds[1];

		var g;

		svg.appendChild (MakeEl ('g', {
			xmlns: SVGNS,
			transform: 'matrix('+scaleX+', 0, 0, '+scaleY+', 0, '+scaleTransY+')', // board.scale*board.baseScale
		}, g = MakeEl ('g', {
			xmlns: SVGNS,
			className: 'container',
			transform: 'translate('+transX+', '+transY+')'
		})));

		var layerOrder = board.boardFlipped ? board.reverseRenderLayerOrder : board.renderLayerOrder;
		for (var layerKey in layerOrder) {
			var layerId = layerOrder[layerKey];
			if (!board.visibleLayers[layerId]) { continue };
			board.layerRenderFunctions[layerId](this, board, g);
		}

		if (board.initInteractive)
			board.initInteractive ();

	}

	SVGRenderer.prototype.drawSingleWire = function (wire, ctx) {
		var path = this.drawWire (wire, ctx);
		path.setAttributeNS (null, 'stroke', wire.strokeStyle);
		path.setAttributeNS (null, 'stroke-width', wire.width);
		path.setAttributeNS (null, 'fill', "none");
	}

	SVGRenderer.prototype.drawWire = function (wire, ctx) {

		var attrs = {xmlns: SVGNS};

		var lineDash;
		if (wire.style === "longdash") {
			lineDash = [3];
		} else if (wire.style === "shortdash") {
			lineDash = [1];
		} else if (wire.style === "dashdot") {
			lineDash = [3, 1, 1, 1];
		}

		if (lineDash) attrs["stroke-dasharray"] = lineDash.join (', ');

		if (wire.cap && wire.cap === "flat") {
			attrs["stroke-linecap"] = "butt";
		} else {
			attrs["stroke-linecap"] = "round";
		}

		if (wire.curve) {

			var rotate = (wire.rot ? parseFloat(wire.rot.substr (wire.rot.indexOf ("R") + 1)) : 0)/180*Math.PI;

			var radiusX, radiusY;
			radiusX = radiusY = wire.radius;
			if (wire.radius.constructor === Array) {
				radiusX = wire.radius[0];
				radiusY = wire.radius[1];
			}

			attrs.d = describeArcRadians (wire.x, wire.y, radiusX, rotate + wire.start, rotate + wire.start + wire.angle);
			// attrs.transform = 'scale('+radiusX+','+radiusY+') translate('+wire.x+', '+wire.y+')';
		} else {
			attrs.d = ['M', wire.x1, wire.y1, 'L', wire.x2, wire.y2].join (' ');
		}

		var path = MakeEl ('path', attrs);
		ctx.appendChild (path);
		return path;

	}

	SVGRenderer.prototype.drawHole = function (hole, ctx) {

		var board = this.board;

		ctx.appendChild (MakeEl ('circle', {
			xmlns: SVGNS,
			cx: hole.x,
			cy: hole.y,
			fill: "none",
			r: hole.drill/2 + hole.strokeWidth/2,
			stroke: hole.strokeStyle,
			'stroke-width': hole.strokeWidth,
		}));
	}

	SVGRenderer.prototype.drawText = function (attrs, text, ctx) {
		var x = attrs.x || text.x,
			y = attrs.y || text.y,
			rot = attrs.rot || text.rot || "R0",
			size = text.size,
			flipText = attrs.flipText !== undefined ? attrs.flipText : text.flipText;

		var board = this.board;

		var content = attrs.content || text.content;
		var color   = attrs.color;

		var textAngle = board.angleForRot (rot);

		//rotation from 90.1 to 270 causes Eagle to draw labels 180 degrees rotated with top right anchor point
		var degrees  = textAngle.degrees,
			textRot  = board.matrixForRot(rot),
			fontSize = 10;

		var font = ''+fontSize+'pt vector';	//Use a regular font size - very small sizes seem to mess up spacing / kerning

		var strings = content.split (/\r?\n/);
		var stringOffset = (text.interlinear || 50) * fontSize / 100;

		var scale = size / fontSize;
		var scaleX = scale,
			scaleY = (board.coordYFlip ? 1 : -1)*scale;

		var textCtx = this.getScope (ctx, {
			name: 'text',
			transform: 'matrix('+[textRot[0],textRot[2],textRot[1],textRot[3],x,y].join(', ')+')'
		});

		textCtx.appendChild (MakeEl ('text', {
			xmlns: SVGNS,
			'font-size': fontSize + 'pt',
			'font-family': 'vector',
			transform: 'scale('+scaleX+', '+scaleY+')',
			fill: color
		}, content));

		return;

		if (0) { // enable to draw zero points for text
			ctx.save();
			ctx.beginPath();
			ctx.arc(0, 0, 1, 0, 2 * Math.PI, false);
			ctx.fillStyle = board.viaPadColor();
			ctx.fill();
			ctx.restore();
		}

		ctx.save();
		ctx.fillStyle = color;
		ctx.font = font;
		ctx.translate(x,y);

		ctx.transform (textRot[0],textRot[2],textRot[1],textRot[3],0,0);
		var textBlockHeight = (strings.length - 1) * (stringOffset + fontSize);
		var textBlockWidth = 0;
		strings.forEach (function (string, idx) {
			textBlockWidth = Math.max (textBlockWidth, ctx.measureText(string).width);
		}, this);
		ctx.scale (scaleX, scaleY);

		var xOffset = 0;
		if (flipText) {
			var xMult = {center: 0, left: 1, right: 1};
			var yMult = {middle: 0, bottom: -1, top: 1};
			ctx.translate (
				xMult[text.align || "left"] * textBlockWidth,
				yMult[text.valign || "bottom"] * (textBlockHeight + fontSize)
			);
			if (!textAngle.spin) ctx.scale(-1,-1);
		}

		if (0) { // enable to draw zero points for text origin
			ctx.save();
			ctx.beginPath();
			ctx.arc(0, 0, 1, 0, 2 * Math.PI, false);
			ctx.fillStyle = "b00";
			ctx.fill();
			ctx.restore();
		}

		if (text.align)  ctx.textAlign = text.align;
		if (text.valign) ctx.textBaseline = text.valign;

		strings.forEach (function (string, idx) {
			var yOffset = idx * (stringOffset + fontSize);
			if (text.valign === "middle") {
				yOffset -= textBlockHeight/2;
			} else if (text.valign === "bottom") {
				yOffset -= textBlockHeight;
			}
			ctx.fillText(string, xOffset, yOffset);
		}, this);

		ctx.restore();
	}

	SVGRenderer.prototype.drawFilledPoly = function (poly, ctx) {
		var dAttr = [];
		poly.points.forEach (function (point, idx) {
			if (idx === 0)
				dAttr.push ('M')
			else
				dAttr.push ('L')
			dAttr.push (point.x);
			dAttr.push (point.y);
		});
		dAttr.push ('z');

		ctx.appendChild (MakeEl ('path', {
			className: 'package-rect',
			xmlns: SVGNS,
			d: dAttr.join (' '),
			fill: poly.fillStyle,
			//"stroke-width": board.minLineWidth,
			"stroke-linecap": "round"
		}));
	}

	SVGRenderer.prototype.drawFilledCircle = function (poly, ctx) {

		ctx.appendChild (MakeEl ('circle', {
			className: 'package-circle',
			xmlns: SVGNS,
			cx: poly.x,
			cy: poly.y,
			r: poly.radius,
			fill: poly.fillStyle,
			//"stroke-width": board.minLineWidth,
			"stroke-linecap": "round"
		}));
	}

	SVGRenderer.prototype.dimCanvas = function(ctx, alpha) {

		var board = this.board;

		ctx.appendChild (MakeEl ('rect', {
			xmlns: SVGNS,
			x: board.boardFlipped ? board.nativeBounds[2] : board.nativeBounds[0],
			y: board.nativeBounds[1],
			width: board.nativeSize[0],
			height: board.nativeSize[1],
			fill: 'rgba(255,255,255,'+alpha+')'
		}));
//		ctx.save();
//		ctx.setTransform(1, 0, 0, 1, 0, 0);
//		ctx.globalCompositeOperation = 'destination-out';
//		ctx.fillStyle =
//		ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
//		ctx.restore();
	};

	return SVGRenderer;

}));
<|endoftext|>


<commit_before>// replace with your username and branch to customize after forking
var URL = rawGitUrl("audibleblink", "simplify");

function rawGitUrl(ghHandle, branchName){
    return "//rawgit.com/" + ghHandle + "/boom/" + branchName + "/lib/boom.js";
}

var js = document.createElement('script');
js.src = URL;
document.head.appendChild(js);
<commit_msg>set default branch if configs to master<commit_after>// CUSTOMIZING: replace with your username and branch after forking
// Default branch is MASTER
var URL = rawGitUrl("audibleblink", null);

function rawGitUrl(ghHandle, branchName){
    branchName = branchName || "master"
    return "//rawgit.com/" + ghHandle + "/boom/" + branchName + "/lib/boom.js";
}

var js = document.createElement('script');
js.src = URL;
document.head.appendChild(js);
<|endoftext|>