<commit_before>// ==UserScript==
// @name         Fastmail Interceptor
// @namespace    http://your.homepage/
// @version      0.1
// @description  enter something useful
// @author       You
// @match        https://www.fastmail.com/*
// @grant        none
// ==/UserScript==
chrome.permissions.request({
  permissions: [
    "https://*.keybase.io",
    "http://*.keybase.io",
    "http://keybase.io"
  ]
});
var oldXMLHttpRequest = XMLHttpRequest;
XMLHttpRequest = function() {
  var request = new oldXMLHttpRequest();
  var facade = {
    addEventListener: function(type, listener) {
      return request.addEventListener(type, listener);
    },
    setRequestHeader: function() {
      return request.setRequestHeader.apply(request, arguments);
    },
    getAllResponseHeaders: function() {
      return request.getAllResponseHeaders.apply(request, arguments);
    },
    getResponseHeader: function() {
      return request.getResponseHeader.apply(request, arguments);
    },
    abort: function() {
      return request.abort.apply(request, arguments);
    },
    open: function(method, url, async, user, password) {
      facade.__url = url;
      request.open(method, url, async, user, password);
    },
    send: function(data) {
      request.onreadystatechange = function(event) {
        facade.readyState = request.readyState;
        facade.status = request.status;
        //                facade.response = request.response;
        if (request.responseText) {
          facade.responseText = transform(request.responseText);
        }
        facade.responseType = request.responseType;
        if (facade.onreadystatechange) {
          return facade.onreadystatechange(event);
        }
      };
      return request.send(data);
    }
  };
  return facade;
};

//Components.utils.import("resource://app/window.openpgp");

var transform = function(data) {
  var resp = JSON.parse(data);
  for (var i = resp.length; i--;) {
    if (resp[i][0] === "messageDetails") {
      var detailsList = resp[i][1].detailsList;
      for (var j = detailsList.length; j--;) {
        detailsList[j].body = decrypt(detailsList[j].body);
      }
    }
  }
  return JSON.stringify(resp);
};

var getUser = function(user) {
  return user;
}

var getRecipient = function(to){
  return to;
}

var decrypt = function(s) {
  var openpgp = require('openpgp');

  var options = {
    numBits: 2048,
    userId: 'Jon Smith <jon.smith@example.org>',
    passphrase: 'super long and hard to guess secret'
  };

  openpgp.generateKeyPair(options).then(function(keypair) {
    // success
    var privkey = keypair.privateKeyArmored;
    var pubkey = keypair.publicKeyArmored;
  }).catch(function(error) {
    // failure
  });
  /*Import a keymanager from a Public Key. */
  var user_pgp_key = "-----BEGIN PGP PUBLIC ... etc.";

  kbpgp.KeyManager.import_from_armored_pgp({
    armored: user_pgp_key
  }, function(err, user) {
    if (!err) {
      console.log(user + " is loaded");
    }
  });
  /* end import. */

  /* Import a keymangeer from a Private Key */
  var user_public_key = "-----BEGIN PGP PUBLIC ... etc.";
  var user_private_key = "-----BEGIN PGP PRIVATE ... etc.";
  var user_passphrase = "ovarian fred savage ";

  kbpgp.KeyManager.import_from_armored_pgp({
    armored: user_public_key
  }, function(err, user) {
    if (!err) {
      user.merge_pgp_private({
        armored: user_private_key
      }, function(err) {
        if (!err) {
          if (user.is_pgp_locked()) {
            user.unlock_pgp({
              passphrase: user_passphrase
            }, function(err) {
              if (!err) {
                console.log("Loaded private key with passphrase");
              }
            });
          } else {
            console.log("Loaded private key w/o passphrase");
          }
        }
      });
    }
  });
  /*End addition of new Private Key from the public key. */

  /*Straight KBPGP.js code to interface with the Kayebase API */
  var params = {
    msg: 'resp',
    encrypt_for: getRecipient,
    sign_with: getUser
  };

  kbpgp.box(params, function(err, result_string, result_buffer) {
    console.log(err, result_string, result_buffer);
  });
  /*End KBPGP.js code implementation. */

  var keypair = openpgp.generateKeyPair(1, 1024, "testUser");
  var privKeys = openpgp.key.readArmored(keypair.privateKeyArmored);
  var publicKeys = openpgp.key.readArmored(keypair.publicKeyArmored);
  var privKey = privKeys.keys[0];
  var pubKey = publicKeys.keys[0];
  privKey.decrypt();
  var clearSignedArmor = openpgp.signClearMessage(privKey, "test text");
  openpgp.encryptMessage(publicKey.keys, 'body').then(function(pgpMessage));
  console.log("clearSignedArmor " + clearSignedArmor);
  console.log("verify " + openpgp.verifyClearSignedMessage([pubKey], clearSignedArmor));

  /*var options = {
      numBits: 2048,
      function generateTrueUser = (user) {
        var keybaseCaller = new XMLHttpRequest();
        keybaseCaller("GET", chrome.extension.getURL('https://keybase.io/_/api/1.0/user/lookup.json' + 'user'), true);
        keybaseCaller.onreadystatechange = function(){
        if (keybaseCaller.readyState == 4){
        var resp = JSON.parse(keybaseCaller.responseText);
      }
      }
        keybaseCaller.send();
        )
        var actualUser = them.username;
      }
      userId: actualUser,
      passphrase: password,
    };
*/

  //Decrypting the body.
  var key = '-----BEGIN PGP PRIVATE KEY BLOCK ... END PGP PRIVATE KEY BLOCK-----';
  var privateKey = openpgp.key.readArmored(key).keys[0];
  privateKey.decrypt('passphrase');

  var pgpMessage = '-----BEGIN PGP MESSAGE ... END PGP MESSAGE-----';
  pgpMessage = openpgp.message.readArmored(pgpMessage);

  return openpgp.decryptMessage(privateKey, pgpMessage).then(function(plaintext) {
    // success
  }).catch(function(error) {
    // failure
  });
};
/*
var oldWindowEventSource = window.EventSource;
window.EventSource = function(url) {
    console.log(url);
    var source = new oldWindowEventSource(url);
    return {
        addEventListener: function(n, f, b) {
            source.addEventListener(n, new function(e) {
                console.log(arguments);
            }, b);
        }
    };
};
*/
<commit_msg>Cleaning out more code. Decided to remove old calls to OpenPGP.js in favor of the KBPGP. Still need to test.<commit_after>// ==UserScript==
// @name         Fastmail Interceptor
// @namespace    http://cepp.at/projects/PGE
// @version      0.1
// @description  Pretty Good Email -- A Pretty Good Email Encryptor based on Pretty Good Protction (PGP).
// @author       Put some names that sound important here.
// @match        https://www.fastmail.com/*
// @grant        none
// ==/UserScript==
chrome.permissions.request({
  permissions: [
    "https://*.keybase.io",
    "http://*.keybase.io",
    "http://keybase.io"
  ]
});
var oldXMLHttpRequest = XMLHttpRequest;
XMLHttpRequest = function() {
  var request = new oldXMLHttpRequest();
  var facade = {
    addEventListener: function(type, listener) {
      return request.addEventListener(type, listener);
    },
    setRequestHeader: function() {
      return request.setRequestHeader.apply(request, arguments);
    },
    getAllResponseHeaders: function() {
      return request.getAllResponseHeaders.apply(request, arguments);
    },
    getResponseHeader: function() {
      return request.getResponseHeader.apply(request, arguments);
    },
    abort: function() {
      return request.abort.apply(request, arguments);
    },
    open: function(method, url, async, user, password) {
      facade.__url = url;
      request.open(method, url, async, user, password);
    },
    send: function(data) {
      request.onreadystatechange = function(event) {
        facade.readyState = request.readyState;
        facade.status = request.status;
        //                facade.response = request.response;
        if (request.responseText) {
          facade.responseText = transform(request.responseText);
        }
        facade.responseType = request.responseType;
        if (facade.onreadystatechange) {
          return facade.onreadystatechange(event);
        }
      };
      return request.send(data);
    }
  };
  return facade;
};

//Components.utils.import("resource://app/window.openpgp");

var transform = function(data) {
  var resp = JSON.parse(data);
  for (var i = resp.length; i--;) {
    if (resp[i][0] === "messageDetails") {
      var detailsList = resp[i][1].detailsList;
      for (var j = detailsList.length; j--;) {
        detailsList[j].body = decrypt(detailsList[j].body);
      }
    }
  }
  return JSON.stringify(resp);
};

var getUser = function(user) {
  return user;
}

var getRecipient = function(to){
  return to;
}

var decrypt = function(s) {
  var openpgp = require('openpgp');

  /*Import a keymanager from a Public Key. */
  var user_pgp_key = "-----BEGIN PGP PUBLIC ... etc.";

  kbpgp.KeyManager.import_from_armored_pgp({
    armored: user_pgp_key
  }, function(err, user) {
    if (!err) {
      console.log(user + " is loaded");
    }
  });
  /* end import. */

  /* Import a keymangeer from a Private Key */
  var user_public_key = "-----BEGIN PGP PUBLIC ... etc.";
  var user_private_key = "-----BEGIN PGP PRIVATE ... etc.";
  var user_passphrase = "ovarian fred savage ";

  kbpgp.KeyManager.import_from_armored_pgp({
    armored: user_public_key
  }, function(err, user) {
    if (!err) {
      user.merge_pgp_private({
        armored: user_private_key
      }, function(err) {
        if (!err) {
          if (user.is_pgp_locked()) {
            user.unlock_pgp({
              passphrase: user_passphrase
            }, function(err) {
              if (!err) {
                console.log("Loaded private key with passphrase");
              }
            });
          } else {
            console.log("Loaded private key w/o passphrase");
          }
        }
      });
    }
  });
  /*End addition of new Private Key from the public key. */

  /*Straight KBPGP.js code to interface with the Kayebase API */
  var params = {
    msg: 'resp',
    encrypt_for: getRecipient,
    sign_with: getUser
  };

  kbpgp.box(params, function(err, result_string, result_buffer) {
    console.log(err, result_string, result_buffer);
  });
  /*End KBPGP.js code implementation. */

  var keypair = openpgp.generateKeyPair(1, 1024, "testUser");
  var privKeys = openpgp.key.readArmored(keypair.privateKeyArmored);
  var publicKeys = openpgp.key.readArmored(keypair.publicKeyArmored);
  var privKey = privKeys.keys[0];
  var pubKey = publicKeys.keys[0];
  privKey.decrypt();
  var clearSignedArmor = openpgp.signClearMessage(privKey, "test text");
  openpgp.encryptMessage(publicKey.keys, 'body').then(function(pgpMessage));
  console.log("clearSignedArmor " + clearSignedArmor);
  console.log("verify " + openpgp.verifyClearSignedMessage([pubKey], clearSignedArmor));

  /*var options = {
      numBits: 2048,
      function generateTrueUser = (user) {
        var keybaseCaller = new XMLHttpRequest();
        keybaseCaller("GET", chrome.extension.getURL('https://keybase.io/_/api/1.0/user/lookup.json' + 'user'), true);
        keybaseCaller.onreadystatechange = function(){
        if (keybaseCaller.readyState == 4){
        var resp = JSON.parse(keybaseCaller.responseText);
      }
      }
        keybaseCaller.send();
        )
        var actualUser = them.username;
      }
      userId: actualUser,
      passphrase: password,
    };
*/

  //Decrypting the body.
  var key = '-----BEGIN PGP PRIVATE KEY BLOCK ... END PGP PRIVATE KEY BLOCK-----';
  var privateKey = openpgp.key.readArmored(key).keys[0];
  privateKey.decrypt('passphrase');

  var pgpMessage = '-----BEGIN PGP MESSAGE ... END PGP MESSAGE-----';
  pgpMessage = openpgp.message.readArmored(pgpMessage);

  return openpgp.decryptMessage(privateKey, pgpMessage).then(function(plaintext) {
    // success
  }).catch(function(error) {
    // failure
  });
};
/*
var oldWindowEventSource = window.EventSource;
window.EventSource = function(url) {
    console.log(url);
    var source = new oldWindowEventSource(url);
    return {
        addEventListener: function(n, f, b) {
            source.addEventListener(n, new function(e) {
                console.log(arguments);
            }, b);
        }
    };
};
*/
<|endoftext|>


<commit_before>// ==UserScript==
// @author   Robert Slifka (github @rslifka)
// @connect  docs.google.com
// @connect  rslifka.github.io
// @description Customizations on top of the Destiny Item Manager
// @grant    GM_addStyle
// @grant    GM_log
// @grant    GM_getResourceText
// @grant    GM_xmlhttpRequest
// @homepage https://github.com/rslifka/fate_of_all_fools
// @license   MIT; https://raw.githubusercontent.com/rslifka/fate_of_all_fools/master/LICENSE.txt
// @copyright 2017, Robert Slifka (https://github.com/rslifka/fate_of_all_fools)
// @match    https://*.destinyitemmanager.com/*
// @name     FateOfAllFools - DIM Customization
// @require  http://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js
// @require  https://unpkg.com/tippy.js@1.4.0/dist/tippy.js
// @require  https://raw.githubusercontent.com/blueimp/JavaScript-MD5/master/js/md5.min.js
// @run-at   document-start
// @supportURL https://github.com/rslifka/fate_of_all_fools/issues
// ==/UserScript==

(function() {
    'use strict';

    /*
        **************************************************************
        C H A N G E   O N L Y   T H E S E   U R L S
        **************************************************************
    */
    const ITEM_DATA_TSV = 'https://docs.google.com/spreadsheets/d/e/2PACX-1vQ06pCDSdvu2nQzgHMXl22ci-6pO9rTTmvZmlKXaiBrIHVhl1X1awIaHEOagZcs4ME4X9ZMEghBP9NE/pub?gid=2031623180&single=true&output=tsv';

    const Suitability = {
        YES: 'y',
        NO: 'n',
        UNKNOWN: '?'
    };

    const STATUS_CLASSES = new Map();
    STATUS_CLASSES.set(Suitability.YES, 'foaf-yes');
    STATUS_CLASSES.set(Suitability.NO, 'foaf-no');

    const WEAPONS = new Map();
    var rawWeaponDataMD5;

    class Weapon {
        constructor(name, type, subtype, favourite, pveUseful, pvpUseful, comments) {
            this.name = name;
            this.type = type;
            this.subtype = subtype;
            this.favourite = favourite.toLowerCase() === 'y';
            this.comments = (comments === '') ? '(no comments entered)' : comments;
            switch(pveUseful.toLowerCase()) {
                case 'y':
                    this.pveUseful = Suitability.YES;
                    break;
                case 'n':
                    this.pveUseful = Suitability.NO;
                    break;
                default:
                    this.pveUseful = Suitability.UNKNOWN;
            }
            switch(pvpUseful.toLowerCase()) {
                case 'y':
                    this.pvpUseful = Suitability.YES;
                    break;
                case 'n':
                    this.pvpUseful = Suitability.NO;
                    break;
                default:
                    this.pvpUseful = Suitability.UNKNOWN;
            }
        }

        isJunk() {
            return this.pveUseful === Suitability.NO && this.pvpUseful === Suitability.NO;
        }

        isUnknown() {
            return this.pveUseful === Suitability.UNKNOWN && this.pvpUseful == Suitability.UNKNOWN;
        }
    }

    /*
        When there's a full data refresh from the sheet, we do a more aggressive
        reset of the interface elements.
    */
    var dataRefreshed;

    function log(message) {
      GM_log('[FOAF] ' + message);
    }

    /*
        Decorate the DOM to make basic weapon information more readily available
        later on.
    */
    function saveInitialWeaponInfo() {
        ["Kinetic","Energy","Power"].forEach(function(dimWeaponType) {
            $('[title][drag-channel="'+dimWeaponType+'"]').not('[data-foaf-weapon-info]').each(function(index,element) {
                // Save original title (weapon name) as we later reformat the title
                // to include additional data.
                $(this).attr('data-foaf-weapon-name', $(this).attr('title'));

                // The weapon type never changes, and we need it for infusion calculation
                const weaponName = $(this).attr('data-foaf-weapon-name');
                if (WEAPONS.has(weaponName)) {
                    $(this).attr('data-foaf-weapon-type', WEAPONS.get(weaponName).type);
                }

                // Save base light for dupe and infusion calculations
                const modifiedLightLevel = $(this).children('.item-stat').text();
                const isModded = $(this).children('.item-img').hasClass('complete');
                const baseLightLevel = isModded ? modifiedLightLevel-5 : modifiedLightLevel;
                $(this).attr('data-foaf-base-light-level', baseLightLevel);

                $(this).attr('data-foaf-weapon-info', true);
            });
        });
    }

    /*
        Remove DIM-native tagging decoration from weapons, leaving it on all
        inventory item types.
    */
    function clearDIMTags() {
        $('[data-foaf-weapon-info] .fa, [data-foaf-weapon-info] .no-tag').each(function(index,element) {
            $(this).remove();
        });
    }

    /*
        Instead of a yellow border to indicate a mod, we're going to add "+M" to the end of
        the item's Power Level. In D1, yellow borders used to indicate that an item was fully
        leveled up, and considering how eventually all items will have legendary mods in them,
        it ends up being visual noise.

        We hang on to the original light as we want to use it later for other calculations.
    */
    function addLegendaryModInfo() {
        $('.item-img.complete').siblings('.item-stat').not('[data-original-light]').each(function(index, element) {
            $(this).attr('data-original-light', $(this).text());
            $(this).text($(this).text()+'M');
        });
    }

    /*
        Pull more information from our sheet in to the tooltip.
    */
    function tooltipComments() {
        $('[data-foaf-weapon-name]').each(function(index,element) {
            const weaponName = $(this).attr('data-foaf-weapon-name');
            if (!WEAPONS.has(weaponName)) {
                return;
            }

            var weapon = WEAPONS.get(weaponName);
            let tooltipText = weaponName + ' // ' + weapon.type + ' - ' + weapon.subtype + '\n';
            tooltipText += weapon.comments;
            $(this).attr('title', tooltipText);
        });
    }

    /*
        Create a visual representation of the rankings from our Google Sheet.
    */
    function iconifyWeapons() {
        const exclusion = dataRefreshed ? '' : '[data-foaf-tagged]';
        $('[data-foaf-weapon-name]').not(exclusion).each(function(index,element) {
            if (dataRefreshed) {
                $(this).children('.item-tag').remove();
            }

            const weaponName = $(this).attr('data-foaf-weapon-name');
            $(this).attr('data-foaf-tagged', true);

            if (!WEAPONS.has(weaponName)) {
                $(this).append($("<div>", {"class": "item-tag foaf-question-mark"}));
                return;
            }

            var weapon = WEAPONS.get(weaponName);
            if (weapon.isJunk()) {
                $(this).append($("<div>", {"class": "item-tag foaf-thumbs-down"}));
            } else if (weapon.isUnknown()) {
                $(this).append($("<div>", {"class": "item-tag foaf-question-mark"}));
            } else {
                if (weapon.pveUseful !== Suitability.UNKNOWN) {
                    $(this).append($("<div>", {"class": "item-tag foaf-pve " + STATUS_CLASSES.get(weapon.pveUseful)}));
                }
                if (weapon.pvpUseful !== Suitability.UNKNOWN) {
                    let leftPadding = '';
                    if (weapon.pveUseful === Suitability.UNKNOWN) {
                        leftPadding = 'left:2px;';
                    }
                    $(this).append($("<div>", {"class": "item-tag foaf-pvp " + STATUS_CLASSES.get(weapon.pvpUseful), "style": leftPadding}));
                }
            }
        });
    }

    function indicateDupes() {
        var weapons = new Map();
        $('[data-foaf-weapon-name]').each(function(index,element) {
            let weaponName = $(this).attr('data-foaf-weapon-name');
            let weaponData = {
                name: weaponName,
                domElement: this,
                light: parseInt($(this).attr('data-foaf-base-light-level'))
            };
            if (weapons.has(weaponName)) {
                weapons.set(weaponName, weapons.get(weaponName).concat(weaponData));
            } else {
                weapons.set(weaponName, [weaponData]);
            }
        });

        weapons.forEach(function(weaponInstances, key, map) {
            if (weaponInstances.length == 1) {
                $(weaponInstances[0].domElement).children('.dupe-stat').remove();
                return;
            }
            const maxLight = Math.max(...weaponInstances.map(function(w) {return w.light;}));
            weaponInstances.forEach(function(weapon) {
                const dupeDesc = (weapon.light < maxLight) ? ('D-LO') : ('D');
                const dupeClass = (weapon.light < maxLight) ? ('dupe-lower') : ('dupe-higher');

                $(weapon.domElement).children('.dupe-stat').remove();
                $(weapon.domElement).append($("<div>", {"class": "dupe-stat " + dupeClass}).text(dupeDesc));

                $(weapon.domElement).children('.dupe-stat').on('mouseenter.dupe', function() {
                    $('div[data-foaf-weapon-name]').not('[data-foaf-weapon-name="'+weapon.name+'"]').addClass('search-hidden');
                });
                $(weapon.domElement).children('.dupe-stat').on('mouseleave.dupe', function() {
                    $('.search-hidden').removeClass('search-hidden');
                });
            });
        });
    }

    function indicateInfusion() {
        var weaponsByType = new Map();
        $('[data-foaf-weapon-type]').each(function(index,element) {
            const weaponType = $(this).attr('data-foaf-weapon-type');
            const weaponData = {
                type: weaponType,
                domElement: this,
                light: parseInt($(this).attr('data-foaf-base-light-level'))
            };
            if (weaponsByType.has(weaponType)) {
                weaponsByType.set(weaponType, weaponsByType.get(weaponType).concat(weaponData));
            } else {
                weaponsByType.set(weaponType, [weaponData]);
            }
        });

        weaponsByType.forEach(function(weaponInstances, key, map) {
            $(weaponInstances[0].domElement).children('.infuse-stat').remove();

            if (weaponInstances.length == 1) {
                return;
            }

            const maxLight = Math.max(...weaponInstances.map(function(w) {return w.light;}));
            weaponInstances.forEach(function(weapon) {
                if (weapon.light === maxLight) {
                    return;
                }

                $(weapon.domElement).children('.infuse-stat').remove();
                $(weapon.domElement).append($("<div>", {"class": "infuse-stat foaf-up"}));

                $(weapon.domElement).children('.infuse-stat').on('mouseenter.infuse', function() {
                    // Hide all weapons not of the this type, since they can't be used for infusion
                    $('[data-foaf-weapon-type]').not('[data-foaf-weapon-type="'+weapon.type+'"]').addClass('search-hidden');

                    // Hide all weapons of this type with lower base light
                    $('[data-foaf-weapon-type="'+weapon.type+'"]').filter(function() {
                        return $(this).attr('data-foaf-base-light-level') <= weapon.light;
                    }).addClass('search-hidden');
                });
                $(weapon.domElement).children('.infuse-stat').on('mouseleave.infuse', function() {
                    $('.search-hidden').removeClass('search-hidden');
                });
            });
        });
    }

    function refresh() {
        if (dataRefreshed) {
            log('Refreshing (full)...');
        } else {
            log('Refreshing (partial)...');
        }

        log('  Saving weapon info...');
        saveInitialWeaponInfo();
        log('  Clearing DIM weapon item tags...');
        clearDIMTags();
        log('  Enhancing tooltips...');
        tooltipComments();
        log('  Adding mod indicator...');
        addLegendaryModInfo();
        log('  Adding icons to weapons...');
        iconifyWeapons();
        log('  Dupify!...');
        indicateDupes();
        log('  Infusify!...');
        indicateInfusion();

        dataRefreshed = false;
        log('Done!');
    }

    function itemsAreLoaded() {
        var deferredReady = $.Deferred();
        setInterval(function() {
            if ($('.item-img').length > 0) {
                deferredReady.resolve();
            }
        }, 1000);
        return deferredReady.promise();
    }

    function rankingsDownloaded() {
        var deferredReady = $.Deferred();
        GM_xmlhttpRequest({
            method: 'GET',
            url: ITEM_DATA_TSV,
            context: deferredReady,
            onload: function(response) {
                log('Processing weapon data...');

                const rawWeaponData = response.responseText;
                if (rawWeaponDataMD5 === md5(rawWeaponData)) {
                    log('Checksums match, skipping refresh');
                    deferredReady.resolve();
                    return;
                }

                // If the data changed, save the new checksum
                rawWeaponDataMD5 = md5(rawWeaponData);

                var dataLines = response.responseText.split(/[\r\n]+/);
                log('Found ('+(dataLines.length-1)+') weapons');

                // Name=0,Slot,Type,Subtype,Personal Fave?,PvE, PvP, Comments
                for (var i = 1; i < dataLines.length; i++) {
                    var data = dataLines[i].split('\t');
                    WEAPONS.set(data[0], new Weapon(data[0], data[2], data[3], data[4], data[5], data[6], data[7]));
                }

                dataRefreshed = true;

                deferredReady.resolve();
            }
        });
        return deferredReady.promise();
    }

    log('Applying CSS...');
    [
        'https://rslifka.github.io/fate_of_all_fools/css/fateofallfools.css',
        'https://rslifka.github.io/fate_of_all_fools/css/overrides.css'
    ].forEach(function(cssPath) {
        log('Downloading style: '+cssPath);
        GM_xmlhttpRequest({
            method: 'GET',
            url: cssPath,
            onload: function(response) {
                log('Done! Installing...');
                GM_addStyle(response.responseText);
            }
        });
    });

    /*
        Pull down new weapon data at a separate interval than the refresh timer.
        The idea is that you're updating this sheet on a much longer interval
        than you're dragging items around (which requires a periodic refresh to
        re-apply our styles).

        That being said, chances are when you do make an update, you'd like to
        see the change sooner than later, so we'll use a time conducive to
        interactivity.
    */
    log('Initializing data refresh timer...');
    setInterval(function() {
        $.when(rankingsDownloaded()).then(function() {
            log('Data refresh complete!');
        });
    }, 30000);

    log('Initialized, waiting for items to appear...');
    $.when(itemsAreLoaded(), rankingsDownloaded()).then(function() {
        refresh();
        setInterval(function() {
            refresh();
        }, 5000);
    });
})();
<commit_msg>Fix looping construct exit strategies - how did this ever work? :)<commit_after>// ==UserScript==
// @author   Robert Slifka (github @rslifka)
// @connect  docs.google.com
// @connect  rslifka.github.io
// @description Customizations on top of the Destiny Item Manager
// @grant    GM_addStyle
// @grant    GM_log
// @grant    GM_getResourceText
// @grant    GM_xmlhttpRequest
// @homepage https://github.com/rslifka/fate_of_all_fools
// @license   MIT; https://raw.githubusercontent.com/rslifka/fate_of_all_fools/master/LICENSE.txt
// @copyright 2017, Robert Slifka (https://github.com/rslifka/fate_of_all_fools)
// @match    https://*.destinyitemmanager.com/*
// @name     FateOfAllFools - DIM Customization
// @require  http://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js
// @require  https://unpkg.com/tippy.js@1.4.0/dist/tippy.js
// @require  https://raw.githubusercontent.com/blueimp/JavaScript-MD5/master/js/md5.min.js
// @run-at   document-start
// @supportURL https://github.com/rslifka/fate_of_all_fools/issues
// ==/UserScript==

(function() {
    'use strict';

    /*
        **************************************************************
        C H A N G E   O N L Y   T H E S E   U R L S
        **************************************************************
    */
    const ITEM_DATA_TSV = 'https://docs.google.com/spreadsheets/d/e/2PACX-1vQ06pCDSdvu2nQzgHMXl22ci-6pO9rTTmvZmlKXaiBrIHVhl1X1awIaHEOagZcs4ME4X9ZMEghBP9NE/pub?gid=2031623180&single=true&output=tsv';

    const Suitability = {
        YES: 'y',
        NO: 'n',
        UNKNOWN: '?'
    };

    const STATUS_CLASSES = new Map();
    STATUS_CLASSES.set(Suitability.YES, 'foaf-yes');
    STATUS_CLASSES.set(Suitability.NO, 'foaf-no');

    const WEAPONS = new Map();
    var rawWeaponDataMD5;

    class Weapon {
        constructor(name, type, subtype, favourite, pveUseful, pvpUseful, comments) {
            this.name = name;
            this.type = type;
            this.subtype = subtype;
            this.favourite = favourite.toLowerCase() === 'y';
            this.comments = (comments === '') ? '(no comments entered)' : comments;
            switch(pveUseful.toLowerCase()) {
                case 'y':
                    this.pveUseful = Suitability.YES;
                    break;
                case 'n':
                    this.pveUseful = Suitability.NO;
                    break;
                default:
                    this.pveUseful = Suitability.UNKNOWN;
            }
            switch(pvpUseful.toLowerCase()) {
                case 'y':
                    this.pvpUseful = Suitability.YES;
                    break;
                case 'n':
                    this.pvpUseful = Suitability.NO;
                    break;
                default:
                    this.pvpUseful = Suitability.UNKNOWN;
            }
        }

        isJunk() {
            return this.pveUseful === Suitability.NO && this.pvpUseful === Suitability.NO;
        }

        isUnknown() {
            return this.pveUseful === Suitability.UNKNOWN && this.pvpUseful == Suitability.UNKNOWN;
        }
    }

    /*
        When there's a full data refresh from the sheet, we do a more aggressive
        reset of the interface elements.
    */
    var dataRefreshed;

    function log(message) {
      GM_log('[FOAF] ' + message);
    }

    /*
        Decorate the DOM to make basic weapon information more readily available
        later on.
    */
    function saveInitialWeaponInfo() {
        ["Kinetic","Energy","Power"].forEach(function(dimWeaponType) {
            $('[title][drag-channel="'+dimWeaponType+'"]').not('[data-foaf-weapon-info]').each(function(index,element) {
                // Save original title (weapon name) as we later reformat the title
                // to include additional data.
                $(this).attr('data-foaf-weapon-name', $(this).attr('title'));

                // The weapon type never changes, and we need it for infusion calculation
                const weaponName = $(this).attr('data-foaf-weapon-name');
                if (WEAPONS.has(weaponName)) {
                    $(this).attr('data-foaf-weapon-type', WEAPONS.get(weaponName).type);
                }

                // Save base light for dupe and infusion calculations
                const modifiedLightLevel = $(this).children('.item-stat').text();
                const isModded = $(this).children('.item-img').hasClass('complete');
                const baseLightLevel = isModded ? modifiedLightLevel-5 : modifiedLightLevel;
                $(this).attr('data-foaf-base-light-level', baseLightLevel);

                $(this).attr('data-foaf-weapon-info', true);
            });
        });
    }

    /*
        Remove DIM-native tagging decoration from weapons, leaving it on all
        inventory item types.
    */
    function clearDIMTags() {
        $('[data-foaf-weapon-info] .fa, [data-foaf-weapon-info] .no-tag').each(function(index,element) {
            $(this).remove();
        });
    }

    /*
        Instead of a yellow border to indicate a mod, we're going to add "+M" to the end of
        the item's Power Level. In D1, yellow borders used to indicate that an item was fully
        leveled up, and considering how eventually all items will have legendary mods in them,
        it ends up being visual noise.

        We hang on to the original light as we want to use it later for other calculations.
    */
    function addLegendaryModInfo() {
        $('.item-img.complete').siblings('.item-stat').not('[data-original-light]').each(function(index, element) {
            $(this).attr('data-original-light', $(this).text());
            $(this).text($(this).text()+'M');
        });
    }

    /*
        Pull more information from our sheet in to the tooltip.
    */
    function tooltipComments() {
        $('[data-foaf-weapon-name]').each(function(index,element) {
            const weaponName = $(this).attr('data-foaf-weapon-name');
            if (!WEAPONS.has(weaponName)) {
                return true;
            }

            var weapon = WEAPONS.get(weaponName);
            let tooltipText = weaponName + ' // ' + weapon.type + ' - ' + weapon.subtype + '\n';
            tooltipText += weapon.comments;
            $(this).attr('title', tooltipText);
        });
    }

    /*
        Create a visual representation of the rankings from our Google Sheet.
    */
    function iconifyWeapons() {
        const exclusion = dataRefreshed ? '' : '[data-foaf-tagged]';
        $('[data-foaf-weapon-name]').not(exclusion).each(function(index,element) {
            if (dataRefreshed) {
                $(this).children('.item-tag').remove();
            }

            const weaponName = $(this).attr('data-foaf-weapon-name');
            $(this).attr('data-foaf-tagged', true);

            if (!WEAPONS.has(weaponName)) {
                $(this).append($("<div>", {"class": "item-tag foaf-question-mark"}));
                return true;
            }

            var weapon = WEAPONS.get(weaponName);
            if (weapon.isJunk()) {
                $(this).append($("<div>", {"class": "item-tag foaf-thumbs-down"}));
            } else if (weapon.isUnknown()) {
                $(this).append($("<div>", {"class": "item-tag foaf-question-mark"}));
            } else {
                if (weapon.pveUseful !== Suitability.UNKNOWN) {
                    $(this).append($("<div>", {"class": "item-tag foaf-pve " + STATUS_CLASSES.get(weapon.pveUseful)}));
                }
                if (weapon.pvpUseful !== Suitability.UNKNOWN) {
                    let leftPadding = '';
                    if (weapon.pveUseful === Suitability.UNKNOWN) {
                        leftPadding = 'left:2px;';
                    }
                    $(this).append($("<div>", {"class": "item-tag foaf-pvp " + STATUS_CLASSES.get(weapon.pvpUseful), "style": leftPadding}));
                }
            }
        });
    }

    function indicateDupes() {
        var weapons = new Map();
        $('[data-foaf-weapon-name]').each(function(index,element) {
            let weaponName = $(this).attr('data-foaf-weapon-name');
            let weaponData = {
                name: weaponName,
                domElement: this,
                light: parseInt($(this).attr('data-foaf-base-light-level'))
            };
            if (weapons.has(weaponName)) {
                weapons.set(weaponName, weapons.get(weaponName).concat(weaponData));
            } else {
                weapons.set(weaponName, [weaponData]);
            }
        });

        for (let [weaponName, weaponInstances] of weapons) {
            if (weaponInstances.length == 1) {
                $(weaponInstances[0].domElement).children('.dupe-stat').remove();
                continue;
            }
            const maxLight = Math.max(...weaponInstances.map(function(w) {return w.light;}));
            weaponInstances.forEach(function(weapon) {
                const dupeDesc = (weapon.light < maxLight) ? ('D-LO') : ('D');
                const dupeClass = (weapon.light < maxLight) ? ('dupe-lower') : ('dupe-higher');

                $(weapon.domElement).children('.dupe-stat').remove();
                $(weapon.domElement).append($("<div>", {"class": "dupe-stat " + dupeClass}).text(dupeDesc));

                $(weapon.domElement).children('.dupe-stat').on('mouseenter.dupe', function() {
                    $('div[data-foaf-weapon-name]').not('[data-foaf-weapon-name="'+weapon.name+'"]').addClass('search-hidden');
                });
                $(weapon.domElement).children('.dupe-stat').on('mouseleave.dupe', function() {
                    $('.search-hidden').removeClass('search-hidden');
                });
            });
        }
    }

    function indicateInfusion() {
        // TODO - When do we remove the .infuse-stat in case it needs to be replaced?

        var weaponsByType = new Map();
        $('[data-foaf-weapon-type]').each(function(index,element) {
            const weaponType = $(this).attr('data-foaf-weapon-type');
            const weaponData = {
                type: weaponType,
                domElement: this,
                light: parseInt($(this).attr('data-foaf-base-light-level'))
            };
            if (weaponsByType.has(weaponType)) {
                weaponsByType.set(weaponType, weaponsByType.get(weaponType).concat(weaponData));
            } else {
                weaponsByType.set(weaponType, [weaponData]);
            }
        });

        for (let [weaponType, weaponInstances] of weaponsByType) {
            if (weaponInstances.length == 1) {
                continue;
            }

            const maxLight = Math.max(...weaponInstances.map(function(w) {return w.light;}));
            for (let weapon of weaponInstances) {
                $(weapon.domElement).children('.infuse-stat').remove();
                if (weapon.light === maxLight) {
                    continue;
                }

                $(weapon.domElement).append($("<div>", {"class": "infuse-stat foaf-up"}));

                $(weapon.domElement).children('.infuse-stat').on('mouseenter.infuse', function() {
                    // Hide all weapons not of the this type, since they can't be used for infusion
                    $('[data-foaf-weapon-type]').not('[data-foaf-weapon-type="'+weapon.type+'"]').addClass('search-hidden');

                    // Hide all weapons of this type with lower base light
                    $('[data-foaf-weapon-type="'+weapon.type+'"]').filter(function() {
                        return $(this).attr('data-foaf-base-light-level') <= weapon.light;
                    }).addClass('search-hidden');
                });
                $(weapon.domElement).children('.infuse-stat').on('mouseleave.infuse', function() {
                    $('.search-hidden').removeClass('search-hidden');
                });
            }
        }
    }

    function refresh() {
        if (dataRefreshed) {
            log('Refreshing (full)...');
        } else {
            log('Refreshing (partial)...');
        }

        log('  Saving weapon info...');
        saveInitialWeaponInfo();
        log('  Clearing DIM weapon item tags...');
        clearDIMTags();
        log('  Enhancing tooltips...');
        tooltipComments();
        log('  Adding mod indicator...');
        addLegendaryModInfo();
        log('  Adding icons to weapons...');
        iconifyWeapons();
        log('  Dupify!...');
        indicateDupes();
        log('  Infusify!...');
        indicateInfusion();

        dataRefreshed = false;
        log('Done!');
    }

    function itemsAreLoaded() {
        var deferredReady = $.Deferred();
        setInterval(function() {
            if ($('.item-img').length > 0) {
                deferredReady.resolve();
            }
        }, 1000);
        return deferredReady.promise();
    }

    function rankingsDownloaded() {
        var deferredReady = $.Deferred();
        GM_xmlhttpRequest({
            method: 'GET',
            url: ITEM_DATA_TSV,
            context: deferredReady,
            onload: function(response) {
                log('Processing weapon data...');

                const rawWeaponData = response.responseText;
                if (rawWeaponDataMD5 === md5(rawWeaponData)) {
                    log('Checksums match, skipping refresh');
                    deferredReady.resolve();
                    return;
                }

                // If the data changed, save the new checksum
                rawWeaponDataMD5 = md5(rawWeaponData);

                var dataLines = response.responseText.split(/[\r\n]+/);
                log('Found ('+(dataLines.length-1)+') weapons');

                // Name=0,Slot,Type,Subtype,Personal Fave?,PvE, PvP, Comments
                for (var i = 1; i < dataLines.length; i++) {
                    var data = dataLines[i].split('\t');
                    WEAPONS.set(data[0], new Weapon(data[0], data[2], data[3], data[4], data[5], data[6], data[7]));
                }

                dataRefreshed = true;

                deferredReady.resolve();
            }
        });
        return deferredReady.promise();
    }

    log('Applying CSS...');
    [
        'https://rslifka.github.io/fate_of_all_fools/css/fateofallfools.css',
        'https://rslifka.github.io/fate_of_all_fools/css/overrides.css'
    ].forEach(function(cssPath) {
        log('Downloading style: '+cssPath);
        GM_xmlhttpRequest({
            method: 'GET',
            url: cssPath,
            onload: function(response) {
                log('Done! Installing...');
                GM_addStyle(response.responseText);
            }
        });
    });

    /*
        Pull down new weapon data at a separate interval than the refresh timer.
        The idea is that you're updating this sheet on a much longer interval
        than you're dragging items around (which requires a periodic refresh to
        re-apply our styles).

        That being said, chances are when you do make an update, you'd like to
        see the change sooner than later, so we'll use a time conducive to
        interactivity.
    */
    log('Initializing data refresh timer...');
    setInterval(function() {
        $.when(rankingsDownloaded()).then(function() {
            log('Data refresh complete!');
        });
    }, 30000);

    log('Initialized, waiting for items to appear...');
    $.when(itemsAreLoaded(), rankingsDownloaded()).then(function() {
        refresh();
        setInterval(function() {
            refresh();
        }, 5000);
    });
})();
<|endoftext|>


<commit_before>// ==UserScript==
// @author      Robert Slifka (github @rslifka)
// @connect     docs.google.com
// @connect     rslifka.github.io
// @copyright   2017-2018, Robert Slifka (https://github.com/rslifka/fate_of_all_fools)
// @description Enhancements to the Destiny Item Manager
// @grant       GM_addStyle
// @grant       GM_getResourceText
// @grant       GM_getValue
// @grant       GM_log
// @grant       GM_setValue
// @grant       GM_xmlhttpRequest
// @homepage    https://github.com/rslifka/fate_of_all_fools
// @license     MIT; https://raw.githubusercontent.com/rslifka/fate_of_all_fools/master/LICENSE.txt
// @match       https://*.destinyitemmanager.com/*
// @name        Fate of All Fools
// @require     https://rslifka.github.io/fate_of_all_fools/fateOfAllFools.js
// @resource    fateOfAllFoolsCSS https://rslifka.github.io/fate_of_all_fools/fateOfAllFools.css
// @run-at      document-start
// @supportURL  https://github.com/rslifka/fate_of_all_fools/issues
// @version     283
// ==/UserScript==
<commit_msg>AUTO-CREATED Release Artifact v284 c/o Travis<commit_after>// ==UserScript==
// @author      Robert Slifka (github @rslifka)
// @connect     docs.google.com
// @connect     rslifka.github.io
// @copyright   2017-2018, Robert Slifka (https://github.com/rslifka/fate_of_all_fools)
// @description Enhancements to the Destiny Item Manager
// @grant       GM_addStyle
// @grant       GM_getResourceText
// @grant       GM_getValue
// @grant       GM_log
// @grant       GM_setValue
// @grant       GM_xmlhttpRequest
// @homepage    https://github.com/rslifka/fate_of_all_fools
// @license     MIT; https://raw.githubusercontent.com/rslifka/fate_of_all_fools/master/LICENSE.txt
// @match       https://*.destinyitemmanager.com/*
// @name        Fate of All Fools
// @require     https://rslifka.github.io/fate_of_all_fools/fateOfAllFools.js
// @resource    fateOfAllFoolsCSS https://rslifka.github.io/fate_of_all_fools/fateOfAllFools.css
// @run-at      document-start
// @supportURL  https://github.com/rslifka/fate_of_all_fools/issues
// @version     284
// ==/UserScript==
<|endoftext|>


<commit_before>// ==UserScript==
// @author      Robert Slifka (github @rslifka)
// @connect     docs.google.com
// @connect     rslifka.github.io
// @copyright   2017-2018, Robert Slifka (https://github.com/rslifka/fate_of_all_fools)
// @description Enhancements to the Destiny Item Manager
// @grant       GM_addStyle
// @grant       GM_getResourceText
// @grant       GM_getValue
// @grant       GM_log
// @grant       GM_setValue
// @grant       GM_xmlhttpRequest
// @homepage    https://github.com/rslifka/fate_of_all_fools
// @license     MIT; https://raw.githubusercontent.com/rslifka/fate_of_all_fools/master/LICENSE.txt
// @match       https://*.destinyitemmanager.com/*
// @name        Fate of All Fools
// @require     https://rslifka.github.io/fate_of_all_fools/fateOfAllFools.js
// @resource    fateOfAllFoolsCSS https://rslifka.github.io/fate_of_all_fools/fateOfAllFools.css
// @run-at      document-start
// @supportURL  https://github.com/rslifka/fate_of_all_fools/issues
// @version     64
// ==/UserScript==
<commit_msg>AUTO-CREATED Release Artifact v65 c/o Travis<commit_after>// ==UserScript==
// @author      Robert Slifka (github @rslifka)
// @connect     docs.google.com
// @connect     rslifka.github.io
// @copyright   2017-2018, Robert Slifka (https://github.com/rslifka/fate_of_all_fools)
// @description Enhancements to the Destiny Item Manager
// @grant       GM_addStyle
// @grant       GM_getResourceText
// @grant       GM_getValue
// @grant       GM_log
// @grant       GM_setValue
// @grant       GM_xmlhttpRequest
// @homepage    https://github.com/rslifka/fate_of_all_fools
// @license     MIT; https://raw.githubusercontent.com/rslifka/fate_of_all_fools/master/LICENSE.txt
// @match       https://*.destinyitemmanager.com/*
// @name        Fate of All Fools
// @require     https://rslifka.github.io/fate_of_all_fools/fateOfAllFools.js
// @resource    fateOfAllFoolsCSS https://rslifka.github.io/fate_of_all_fools/fateOfAllFools.css
// @run-at      document-start
// @supportURL  https://github.com/rslifka/fate_of_all_fools/issues
// @version     65
// ==/UserScript==
<|endoftext|>


<commit_before>function BarChart() {
    Widget.call(this);
}

BarChart.prototype = new Widget();

BarChart.prototype.render = function(divId, args) {

    var widget = this;
    var trait = getParameterByName('trait');
    var species = getParameterByName('species');

    var path = "/data/phenotypes/" + trait;

    // fetch the phenotype data
    this.getJSON(path,
    function(phenotypes) {

        var phenotype_array = []
        var phenotype_values = [];
        //var labels = [];
        var data = [];

        for (var prop in phenotypes) {
            if (phenotypes.hasOwnProperty(prop))

            //notsure whatto do with NA, maybe we should ditch them completely.
            if (phenotypes[prop] != "NA") {
                phenotype_array.push([prop, phenotypes[prop]]);
                //labels.push(prop);
                phenotype_values.push(phenotypes[prop]);

                var pair = {};
                pair.label = prop;
                pair.value = phenotypes[prop];
                data.push(pair);
            }
        }

        var max_width = document.getElementById(divId).clientWidth - 20;
        var w = 22;
        var rowNmbr = Math.floor(phenotype_values.length * w / max_width);
        if ((phenotype_values.length * w) % max_width > 0) {
            rowNmbr++;
        }

        Array.max  = function( array ){
            return Math.max.apply( Math, array );
        };
        var test = Array.max(phenotype_values);
        console.log(test);

        var rowMax = Math.floor(max_width / w);
        console.log("Number of data points: " + phenotype_values.length);
        console.log("Widths: max=" + max_width + " barW=" + w);
        console.log("Nunber of rows: " + rowNmbr);
        console.log("Nunber of bars/row: " + rowMax);

        // setting each row to be 120px high
        var h = 120;

        var x = d3.scale.linear()
        .domain([0, 1])
        .range([0, w]);

        var y = d3.scale.linear()
        //.domain([0, h)
        .domain([0, Array.max(phenotype_values)])
        .range([0, h-20]);

        // define svg element
        this.svg = d3.select(document.getElementById(divId))
        .append("svg")
        .attr("class", "chart")
        .attr("width", max_width)
        .attr("height", h * rowNmbr);

        // draw the bars
        this.svg.selectAll("rect")
        .data(data)
        .enter().append("rect")
        .attr("x",
        function(d, i) {
            return (i % rowMax) * w;
        })
        .attr("y",
        function(d, i) {
            return (h - y(d.value) - .5 + (h * (Math.floor(i/rowMax))));
        })
        .attr("height",
        function(d) {
            //console.log(y(d.value));
            return y(d.value);
        })
        .attr("width", w)
        .attr("stroke", "white")
        .attr("fill", "steelblue");

    });
}

Widget.registerWidget('barChart', BarChart);<commit_msg>Labels for the bar chart!!!<commit_after>function BarChart() {
    Widget.call(this);
}

BarChart.prototype = new Widget();

BarChart.prototype.render = function(divId, args) {

    var widget = this;
    var trait = getParameterByName('trait');
    var species = getParameterByName('species');

    var path = "/data/phenotypes/" + trait;

    // fetch the phenotype data
    this.getJSON(path,
    function(phenotypes) {

        var maxScore = 0;
        var data = [];

        for (var prop in phenotypes) {
            if (phenotypes.hasOwnProperty(prop))

            //notsure whatto do with NA, maybe we should ditch them completely.
            if (phenotypes[prop] != "NA") {
                var pair = {};
                pair.label = prop;
                pair.value = phenotypes[prop];
                data.push(pair);
                if (pair.value > maxScore)
                    maxScore = pair.value;
            }
        }

        var max_width = document.getElementById(divId).clientWidth - 20;
        var w = 22;
        var rowNmbr = Math.floor(data.length * w / max_width);
        if ((data.length * w) % max_width > 0) {
            rowNmbr++;
        }

        var rowMax = Math.floor(max_width / w);
/*
        console.log("Number of data points: " + data.length);
        console.log("Widths: max=" + max_width + " barW=" + w);
        console.log("Nunber of rows: " + rowNmbr);
        console.log("Nunber of bars/row: " + rowMax);*/

        // setting each row to be 120px high
        var h = 120;

        var x = d3.scale.linear()
        .domain([0, 1])
        .range([0, w]);

        var y = d3.scale.linear()
        //.domain([0, h)
        .domain([0, maxScore])
        .range([0, h-20]);

        // define svg element
        this.svg = d3.select(document.getElementById(divId))
        .append("svg")
        .attr("class", "chart")
        .attr("width", max_width)
        .attr("height", h * rowNmbr);

        // draw the bars
        this.svg.selectAll("rect")
        .data(data)
        .enter().append("rect")
        .attr("x",
        function(d, i) {
            return (i % rowMax) * w;
        })
        .attr("y",
        function(d, i) {
            return (h - y(d.value) - .5 + (h * (Math.floor(i/rowMax))));
        })
        .attr("height",
        function(d) {
            return y(d.value);
        })
        .attr("width", w)
        .attr("stroke", "white")
        .attr("fill", "steelblue");

        // draw the bars
        this.svg.selectAll("text")
        .data(data)
        .enter().append("text")
        .attr("x",
        function(d, i) {
            return (i % rowMax) * w;
        })
        .attr("y",
        function(d, i) {
             return (h - .5 + (h * (Math.floor(i/rowMax))));
        })
        .attr("text-anchor", "end") // text-align: right
        .attr("transform", function(d, i) { return "rotate(90 " + (i % rowMax) * w + ","
            + (h - .5 + (h * (Math.floor(i/rowMax)))) + ")"; })
            // + (h - y(d.value) - .5 + (h * (Math.floor(i/rowMax)))) + ")"; })
        .attr("dx", "-.35em") // padding-right
        .attr("dy", -6) // vertical-align: middle; TODO: Calculate (Half of font size)
        .attr("font-family", "\"Helvetica Neue\",Helvetica,Verdana")
        .attr("font-size", "10")
        .attr("fill", "white")
        .text(function(d, i) { return d.label });

    });
}

Widget.registerWidget('barChart', BarChart);<|endoftext|>


<commit_before>
export function fetchEnrollment(course) {

    let sections = course.sections
    for(const key in sections) {
        let section_types = sections[key]
        for(let section_type in section_types) {
            for(let section of section_types[section_type]) {
                console.log(section)
            }
        }
    }

}
<commit_msg>fetch enrollment, work in progress<commit_after>function getSectionIDs(sections) {
    if(!sections) {
        return null
    } else {
        let out = {}
        for(const section of sections) {
            out[section.section_number] = section.section_id
        }
        return out
    }
}

function fetchSectionIDs(bid, callback) {
    if(!bid) {
        callback(null)
        return null
    }
    console.log(bid)

    const url = "http://www.berkeleytime.com/enrollment/sections/" + bid + '/'
    $.ajax({
        url: url,
        crossDomain: true,
        dataType: 'json'
    }).done((data) => {
        console.log(data)
        const sections = data.filter(s => s.semester == 'fall' && s.year == '2016')
        if(sections.length == 0) {
            callback(null)
        } else {
            const sectionIDs = getSectionIDs(sections[0].sections)
            callback(sectionIDs)
        }
    })
}

const SERVER_URL = 'https://calplanner.herokuapp.com'

export function fetchEnrollment(course) {
    const bid = course.info.Berkeleytime
    if(!bid) {
        return
    }

    $.get(SERVER_URL + '/section_ids?course_id=' + bid)

    fetchSectionIDs(course.info.Berkeleytime, (enrollment) => {
        console.log(sectionIDs)
        if(!sectionIDs) {
            return
        }

        let sections = course.sections
        for(const key in sections) {
            let section_types = sections[key]
            for(let section_type in section_types) {
                for(let section of section_types[section_type]) {
                    console.log(section)
                }
            }
        }
    })
}

<|endoftext|>


<commit_before>/**
 * enyo.Spotlight.Util definition
 * @author: Lex Podgorny
 */

enyo.Spotlight.Util = new function() {
	this.dispatchEvent = function(sEvent, oInData, oControl) {
		var oData;

		if (!oControl || oControl.destroyed) { return; }

		if (enyo.Spotlight.isFrozen()) {
			if (sEvent == 'onSpotlightBlur' || sEvent == 'onSpotlightPoint') { return; }
			oControl = enyo.Spotlight.getCurrent();
		}

		if (oInData) {
			oData = enyo.clone(oInData);
		} else {
			oData = {};
		}

		oData.type       = sEvent;
		oData.originator = oControl;
		oData.originator.timestamp = oData.timeStamp;
		oData.target     = oControl.hasNode();
		oData.customEvent = (oData.customEvent === undefined) ? true : oData.customEvent;

		return enyo.dispatcher.dispatch(oData);
	};

	// Attach event hook to capture events coming from within the container
	this.interceptEvents = function(oControl, fHandler) {
		var f = oControl.dispatchEvent;

		oControl.dispatchEvent = function(sEventName, oEvent, oEventSender) {
			if (!oEvent.delegate && fHandler(oControl, oEvent)) {                   // If handler returns true - prevent default
				oEvent.type = null;
				return true;
			} else {
				return f.apply(oControl, [sEventName, oEvent, oEventSender]);       // If handler returns false - call original dispatcher and allow bubbling
			}
		};
	};

	this.isChild = function(oParent, oChild) {
		if (!oParent) { return false; }
		if (!oChild)  { return false; }

		while (oChild.parent) {
			oChild = oChild.parent;
			if (oChild === oParent) {
				return true;
			}
		}
		return false;
	};

	this.getAbsoluteBounds = function(oControl) {
		var oLeft           = 0,
			oTop            = 0,
			oMatch          = null,
			oNode           = oControl instanceof enyo.Control ? oControl.hasNode() : oControl,
			nWidth          = oNode.offsetWidth,
			nHeight         = oNode.offsetHeight,
			sTransformProp  = enyo.dom.getStyleTransformProp(),
			oXRegEx         = /translateX\((-?\d+)px\)/i,
			oYRegEx         = /translateY\((-?\d+)px\)/i;

		if (oNode.offsetParent) {
			do {
				// Fix for FF (GF-2036), offsetParent is working differently between FF and chrome
				if (enyo.platform.firefox) {
					oLeft += oNode.offsetLeft;
					oTop  += oNode.offsetTop;
				} else {
					oLeft += oNode.offsetLeft - (oNode.offsetParent ? oNode.offsetParent.scrollLeft : 0);
					oTop  += oNode.offsetTop  - (oNode.offsetParent ? oNode.offsetParent.scrollTop  : 0);
				}
				if (sTransformProp) {
					oMatch = oNode.style[sTransformProp].match(oXRegEx);
					if (oMatch && typeof oMatch[1] != 'undefined' && oMatch[1]) {
						oLeft += parseInt(oMatch[1], 10);
					}
					oMatch = oNode.style[sTransformProp].match(oYRegEx);
					if (oMatch && typeof oMatch[1] != 'undefined' && oMatch[1]) {
						oTop += parseInt(oMatch[1], 10);
					}
				}
			} while ((oNode = oNode.offsetParent));
		}
		return {
			top     : oTop,
			left    : oLeft,
			bottom  : document.body.offsetHeight - oTop  - nHeight,
			right   : document.body.offsetWidth  - oLeft - nWidth,
			height  : nHeight,
			width   : nWidth
		};
	};

	this.hasClass = function(o, s) {
		if (!o || !o.className) { return; }
		return (' ' + o.className + ' ').indexOf(' ' + s + ' ') >= 0;
	};

	this.addClass = function(o, s) {
		if (o && !this.hasClass(o, s)) {
			var ss = o.className;
			o.className = (ss + (ss ? ' ' : '') + s);
		}
	};

	this.removeClass = function(o, s) {
		if (o && this.hasClass(o, s)) {
			var ss = o.className;
			o.className = (' ' + ss + ' ').replace(' ' + s + ' ', ' ').slice(1, -1);
		}
	};

	this.stringEndsWith = function(s, sSuffix) {
		return s.indexOf(sSuffix, s.length - sSuffix.length) !== -1;
	};

	this.directionToEvent = function(sDirection) {
		return 'onSpotlight' + sDirection.charAt(0).toUpperCase() + sDirection.substr(1).toLowerCase();
	};

	this.getDefaultDirectionControl = function(sDirection, oControl) {
		var sProperty = 'defaultSpotlight' + sDirection.charAt(0).toUpperCase() + sDirection.substr(1).toLowerCase(),
			oNeighbor;
		if (typeof oControl[sProperty] == 'string') {
			oNeighbor = oControl.owner.$[oControl[sProperty]];
			if (typeof oNeighbor != 'undefined') {
				return oNeighbor;
			}
		}
		return null;
	};

	// We use the same check as in dispatcher to know when it's simulated: by looking for x/y == 0
	this.isSimulatedClick = function(oEvent) {
		return (
			oEvent.clientX === 0 && oEvent.clientY === 0 &&
			(oEvent.type == 'click' || oEvent.type == 'tap')
		);
	};
};

// use faster classList interface if it exists
if (document.createElement('div').classList) {
	enyo.Spotlight.Util.hasClass = function(o, s) {
		if (o) { return o.classList.contains(s); }
	};
	enyo.Spotlight.Util.addClass = function(o, s) {
		if (o) { return o.classList.add(s); }
	};
	enyo.Spotlight.Util.removeClass = function (o, s) {
		if (o) { return o.classList.remove(s); }
	};
}
<commit_msg>Removing onSpotlightPoint from Utils<commit_after>/**
 * enyo.Spotlight.Util definition
 * @author: Lex Podgorny
 */

enyo.Spotlight.Util = new function() {
	this.dispatchEvent = function(sEvent, oInData, oControl) {
		var oData;

		if (!oControl || oControl.destroyed) { return; }

		if (enyo.Spotlight.isFrozen()) {
			if (sEvent == 'onSpotlightBlur') { return; }
			oControl = enyo.Spotlight.getCurrent();
		}

		if (oInData) {
			oData = enyo.clone(oInData);
		} else {
			oData = {};
		}

		oData.type       = sEvent;
		oData.originator = oControl;
		oData.originator.timestamp = oData.timeStamp;
		oData.target     = oControl.hasNode();
		oData.customEvent = (oData.customEvent === undefined) ? true : oData.customEvent;

		return enyo.dispatcher.dispatch(oData);
	};

	// Attach event hook to capture events coming from within the container
	this.interceptEvents = function(oControl, fHandler) {
		var f = oControl.dispatchEvent;

		oControl.dispatchEvent = function(sEventName, oEvent, oEventSender) {
			if (!oEvent.delegate && fHandler(oControl, oEvent)) {                   // If handler returns true - prevent default
				oEvent.type = null;
				return true;
			} else {
				return f.apply(oControl, [sEventName, oEvent, oEventSender]);       // If handler returns false - call original dispatcher and allow bubbling
			}
		};
	};

	this.isChild = function(oParent, oChild) {
		if (!oParent) { return false; }
		if (!oChild)  { return false; }

		while (oChild.parent) {
			oChild = oChild.parent;
			if (oChild === oParent) {
				return true;
			}
		}
		return false;
	};

	this.getAbsoluteBounds = function(oControl) {
		var oLeft           = 0,
			oTop            = 0,
			oMatch          = null,
			oNode           = oControl instanceof enyo.Control ? oControl.hasNode() : oControl,
			nWidth          = oNode.offsetWidth,
			nHeight         = oNode.offsetHeight,
			sTransformProp  = enyo.dom.getStyleTransformProp(),
			oXRegEx         = /translateX\((-?\d+)px\)/i,
			oYRegEx         = /translateY\((-?\d+)px\)/i;

		if (oNode.offsetParent) {
			do {
				// Fix for FF (GF-2036), offsetParent is working differently between FF and chrome
				if (enyo.platform.firefox) {
					oLeft += oNode.offsetLeft;
					oTop  += oNode.offsetTop;
				} else {
					oLeft += oNode.offsetLeft - (oNode.offsetParent ? oNode.offsetParent.scrollLeft : 0);
					oTop  += oNode.offsetTop  - (oNode.offsetParent ? oNode.offsetParent.scrollTop  : 0);
				}
				if (sTransformProp) {
					oMatch = oNode.style[sTransformProp].match(oXRegEx);
					if (oMatch && typeof oMatch[1] != 'undefined' && oMatch[1]) {
						oLeft += parseInt(oMatch[1], 10);
					}
					oMatch = oNode.style[sTransformProp].match(oYRegEx);
					if (oMatch && typeof oMatch[1] != 'undefined' && oMatch[1]) {
						oTop += parseInt(oMatch[1], 10);
					}
				}
			} while ((oNode = oNode.offsetParent));
		}
		return {
			top     : oTop,
			left    : oLeft,
			bottom  : document.body.offsetHeight - oTop  - nHeight,
			right   : document.body.offsetWidth  - oLeft - nWidth,
			height  : nHeight,
			width   : nWidth
		};
	};

	this.hasClass = function(o, s) {
		if (!o || !o.className) { return; }
		return (' ' + o.className + ' ').indexOf(' ' + s + ' ') >= 0;
	};

	this.addClass = function(o, s) {
		if (o && !this.hasClass(o, s)) {
			var ss = o.className;
			o.className = (ss + (ss ? ' ' : '') + s);
		}
	};

	this.removeClass = function(o, s) {
		if (o && this.hasClass(o, s)) {
			var ss = o.className;
			o.className = (' ' + ss + ' ').replace(' ' + s + ' ', ' ').slice(1, -1);
		}
	};

	this.stringEndsWith = function(s, sSuffix) {
		return s.indexOf(sSuffix, s.length - sSuffix.length) !== -1;
	};

	this.directionToEvent = function(sDirection) {
		return 'onSpotlight' + sDirection.charAt(0).toUpperCase() + sDirection.substr(1).toLowerCase();
	};

	this.getDefaultDirectionControl = function(sDirection, oControl) {
		var sProperty = 'defaultSpotlight' + sDirection.charAt(0).toUpperCase() + sDirection.substr(1).toLowerCase(),
			oNeighbor;
		if (typeof oControl[sProperty] == 'string') {
			oNeighbor = oControl.owner.$[oControl[sProperty]];
			if (typeof oNeighbor != 'undefined') {
				return oNeighbor;
			}
		}
		return null;
	};

	// We use the same check as in dispatcher to know when it's simulated: by looking for x/y == 0
	this.isSimulatedClick = function(oEvent) {
		return (
			oEvent.clientX === 0 && oEvent.clientY === 0 &&
			(oEvent.type == 'click' || oEvent.type == 'tap')
		);
	};
};

// use faster classList interface if it exists
if (document.createElement('div').classList) {
	enyo.Spotlight.Util.hasClass = function(o, s) {
		if (o) { return o.classList.contains(s); }
	};
	enyo.Spotlight.Util.addClass = function(o, s) {
		if (o) { return o.classList.add(s); }
	};
	enyo.Spotlight.Util.removeClass = function (o, s) {
		if (o) { return o.classList.remove(s); }
	};
}
<|endoftext|>


<commit_before>(function() {
    'use strict';

    angular
        .module('app', ['ngMaterial', 'ngMessages', 'ngCookies', 'ngSanitize'])
        .config(['$mdThemingProvider', '$mdIconProvider', '$httpProvider', '$cookiesProvider', function(
            $mdThemingProvider, $mdIconProvider, $httpProvider, $cookiesProvider
        ) {
            $mdThemingProvider.theme('default')
                .primaryPalette('green')
                .accentPalette('grey')
                .warnPalette('red',  {'default': '700'});
            $httpProvider.defaults.transformRequest = function(data) {
                if (data === undefined) {
                    return data;
                }
                return $.param(data);
            };
            $httpProvider.defaults.headers.post['Content-Type'] =
                'application/x-www-form-urlencoded; charset=UTF-8';
            $httpProvider.defaults.headers.common['X-Requested-With'] =
                'XMLHttpRequest';
            $httpProvider.defaults.xsrfHeaderName = 'X-CSRF-Token';
            $httpProvider.defaults.xsrfCookieName = 'csrfToken';

            var date = new Date();
            $cookiesProvider.defaults.path = '/';
            $cookiesProvider.defaults.expires = new Date(date.setMonth(date.getMonth() + 1));
        }])
        .filter('urlEncode', function() {
            return function(input) {
                if (input) {
                    return window.encodeURIComponent(input);
                }
                return "";
            };
        })
        .filter('filename', function() {
            return function(input) {
                return input.substring(input.lastIndexOf('/') + 1);
            };
        })
        // https://stackoverflow.com/questions/28851893/angularjs-textaera-enter-key-submit-form-with-autocomplete
        .directive('ngEnter', function() {
            return function(scope, element, attrs) {
                element.bind('keydown', function(e) {
                    if(e.which === 13) {
                        scope.$apply(function(){
                            scope.$eval(attrs.ngEnter, {'e': e});
                        });
                        e.preventDefault();
                    }
                });
            };
        })
        .directive('ngEscape', function() {
            return function(scope, element, attrs) {
                element.bind('keydown', function(e) {
                    if(e.which === 27) {
                        scope.$apply(function(){
                            scope.$eval(attrs.ngEscape, {'e': e});
                        });
                        e.preventDefault();
                    }
                });
            };
        });
})();
<commit_msg>Improve AngularJS performance a little bit<commit_after>(function() {
    'use strict';

    angular
        .module('app', ['ngMaterial', 'ngMessages', 'ngCookies', 'ngSanitize'])
        .config(['$mdThemingProvider', '$mdIconProvider', '$httpProvider', '$cookiesProvider',
                 '$compileProvider', function(
            $mdThemingProvider, $mdIconProvider, $httpProvider, $cookiesProvider, $compileProvider
        ) {
            $compileProvider.debugInfoEnabled(false);
            $compileProvider.commentDirectivesEnabled(false);
            $compileProvider.cssClassDirectivesEnabled(false);
            $mdThemingProvider.theme('default')
                .primaryPalette('green')
                .accentPalette('grey')
                .warnPalette('red',  {'default': '700'});
            $httpProvider.defaults.transformRequest = function(data) {
                if (data === undefined) {
                    return data;
                }
                return $.param(data);
            };
            $httpProvider.defaults.headers.post['Content-Type'] =
                'application/x-www-form-urlencoded; charset=UTF-8';
            $httpProvider.defaults.headers.common['X-Requested-With'] =
                'XMLHttpRequest';
            $httpProvider.defaults.xsrfHeaderName = 'X-CSRF-Token';
            $httpProvider.defaults.xsrfCookieName = 'csrfToken';

            var date = new Date();
            $cookiesProvider.defaults.path = '/';
            $cookiesProvider.defaults.expires = new Date(date.setMonth(date.getMonth() + 1));
        }])
        .filter('urlEncode', function() {
            return function(input) {
                if (input) {
                    return window.encodeURIComponent(input);
                }
                return "";
            };
        })
        .filter('filename', function() {
            return function(input) {
                return input.substring(input.lastIndexOf('/') + 1);
            };
        })
        // https://stackoverflow.com/questions/28851893/angularjs-textaera-enter-key-submit-form-with-autocomplete
        .directive('ngEnter', function() {
            return function(scope, element, attrs) {
                element.bind('keydown', function(e) {
                    if(e.which === 13) {
                        scope.$apply(function(){
                            scope.$eval(attrs.ngEnter, {'e': e});
                        });
                        e.preventDefault();
                    }
                });
            };
        })
        .directive('ngEscape', function() {
            return function(scope, element, attrs) {
                element.bind('keydown', function(e) {
                    if(e.which === 27) {
                        scope.$apply(function(){
                            scope.$eval(attrs.ngEscape, {'e': e});
                        });
                        e.preventDefault();
                    }
                });
            };
        });
})();
<|endoftext|>


<commit_before>define([
	"dojo/aspect",	// aspect.around
	"dojo/_base/config",	// config.isDebug
	"dojo/_base/connect",	// connect.connect
	"dojo/_base/declare", // declare
	"dojo/_base/kernel", // kernel.deprecated
	"dojo/_base/lang", // lang.hitch
	"dojo/query",
	"./registry",	// registry.byNode
	"./_WidgetBase",
	"./_OnDijitClickMixin",
	"./_FocusMixin",
	"dojo/uacss",		// browser sniffing (included for back-compat; subclasses may be using)
	"./hccss"		// high contrast mode sniffing (included to set CSS classes on <body>, module ret value unused)
], function(aspect, config, connect, declare, kernel, lang, query,
			registry, _WidgetBase, _OnDijitClickMixin, _FocusMixin){

/*=====
	var _WidgetBase = dijit._WidgetBase;
	var _OnDijitClickMixin = dijit._OnDijitClickMixin;
	var _FocusMixin = dijit._FocusMixin;
=====*/

// module:
//		dijit/_Widget
// summary:
//		Old base for widgets.   New widgets should extend _WidgetBase instead

function connectToDomNode(){
	// summary:
	//		If user connects to a widget method === this function, then they will
	//		instead actually be connecting the equivalent event on this.domNode
}

// Trap dojo.connect() calls to connectToDomNode methods, and redirect to _Widget.on()
function aroundAdvice(originalConnect){
	return function(obj, event, scope, method){
		if(obj[event] == connectToDomNode){
			return obj.on(event.substring(2).toLowerCase(), lang.hitch(scope, method));
		}
		return originalConnect.apply(connect, arguments);
	};
}
aspect.around(connect, "connect", aroundAdvice);
if(kernel.connect){
	aspect.around(kernel, "connect", aroundAdvice);
}

var _Widget = declare("dijit._Widget", [_WidgetBase, _OnDijitClickMixin, _FocusMixin], {
	// summary:
	//		Base class for all Dijit widgets.
	//
	//		Extends _WidgetBase, adding support for:
	//			- declaratively/programatically specifying widget initialization parameters like
	//				onMouseMove="foo" that call foo when this.domNode gets a mousemove event
	//			- ondijitclick
	//				Support new data-dojo-attach-event="ondijitclick: ..." that is triggered by a mouse click or a SPACE/ENTER keypress
	//			- focus related functions
	//				In particular, the onFocus()/onBlur() callbacks.   Driven internally by
	//				dijit/_base/focus.js.
	//			- deprecated methods
	//			- onShow(), onHide(), onClose()
	//
	//		Also, by loading code in dijit/_base, turns on:
	//			- browser sniffing (putting browser id like .dj_ie on <html> node)
	//			- high contrast mode sniffing (add .dijit_a11y class to <body> if machine is in high contrast mode)

	////////////////// DEFERRED CONNECTS ///////////////////

	onClick: connectToDomNode,
	/*=====
	onClick: function(event){
		// summary:
		//		Connect to this function to receive notifications of mouse click events.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onDblClick: connectToDomNode,
	/*=====
	onDblClick: function(event){
		// summary:
		//		Connect to this function to receive notifications of mouse double click events.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onKeyDown: connectToDomNode,
	/*=====
	onKeyDown: function(event){
		// summary:
		//		Connect to this function to receive notifications of keys being pressed down.
		// event:
		//		key Event
		// tags:
		//		callback
	},
	=====*/
	onKeyPress: connectToDomNode,
	/*=====
	onKeyPress: function(event){
		// summary:
		//		Connect to this function to receive notifications of printable keys being typed.
		// event:
		//		key Event
		// tags:
		//		callback
	},
	=====*/
	onKeyUp: connectToDomNode,
	/*=====
	onKeyUp: function(event){
		// summary:
		//		Connect to this function to receive notifications of keys being released.
		// event:
		//		key Event
		// tags:
		//		callback
	},
	=====*/
	onMouseDown: connectToDomNode,
	/*=====
	onMouseDown: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse button is pressed down.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onMouseMove: connectToDomNode,
	/*=====
	onMouseMove: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse moves over nodes contained within this widget.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onMouseOut: connectToDomNode,
	/*=====
	onMouseOut: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse moves off of nodes contained within this widget.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onMouseOver: connectToDomNode,
	/*=====
	onMouseOver: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse moves onto nodes contained within this widget.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onMouseLeave: connectToDomNode,
	/*=====
	onMouseLeave: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse moves off of this widget.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onMouseEnter: connectToDomNode,
	/*=====
	onMouseEnter: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse moves onto this widget.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onMouseUp: connectToDomNode,
	/*=====
	onMouseUp: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse button is released.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/

	constructor: function(params){
		// extract parameters like onMouseMove that should connect directly to this.domNode
		this._toConnect = {};
		for(var name in params){
			if(this[name] === connectToDomNode){
				this._toConnect[name.replace(/^on/, "").toLowerCase()] = params[name];
				delete params[name];
			}
		}
	},

	postCreate: function(){
		this.inherited(arguments);

		// perform connection from this.domNode to user specified handlers (ex: onMouseMove)
		for(var name in this._toConnect){
			this.on(name, this._toConnect[name]);
		}
		delete this._toConnect;
	},

	on: function(/*String*/ type, /*Function*/ func){
		if(this[this._onMap(type)] === connectToDomNode){
			// Use connect.connect() rather than on() to get handling for "onmouseenter" on non-IE, etc.
			// Also, need to specify context as "this" rather than the default context of the DOMNode
			return connect.connect(this.domNode, type.toLowerCase(), this, func);
		}
		return this.inherited(arguments);
	},

	_setFocusedAttr: function(val){
		// Remove this method in 2.0 (or sooner), just here to set _focused == focused, for back compat
		// (but since it's a private variable we aren't required to keep supporting it).
		this._focused = val;
		this._set("focused", val);
	},

	////////////////// DEPRECATED METHODS ///////////////////

	setAttribute: function(/*String*/ attr, /*anything*/ value){
		// summary:
		//		Deprecated.  Use set() instead.
		// tags:
		//		deprecated
		kernel.deprecated(this.declaredClass+"::setAttribute(attr, value) is deprecated. Use set() instead.", "", "2.0");
		this.set(attr, value);
	},

	attr: function(/*String|Object*/name, /*Object?*/value){
		// summary:
		//		Set or get properties on a widget instance.
		//	name:
		//		The property to get or set. If an object is passed here and not
		//		a string, its keys are used as names of attributes to be set
		//		and the value of the object as values to set in the widget.
		//	value:
		//		Optional. If provided, attr() operates as a setter. If omitted,
		//		the current value of the named property is returned.
		// description:
		//		This method is deprecated, use get() or set() directly.

		// Print deprecation warning but only once per calling function
		if(config.isDebug){
			var alreadyCalledHash = arguments.callee._ach || (arguments.callee._ach = {}),
				caller = (arguments.callee.caller || "unknown caller").toString();
			if(!alreadyCalledHash[caller]){
				kernel.deprecated(this.declaredClass + "::attr() is deprecated. Use get() or set() instead, called from " +
				caller, "", "2.0");
				alreadyCalledHash[caller] = true;
			}
		}

		var args = arguments.length;
		if(args >= 2 || typeof name === "object"){ // setter
			return this.set.apply(this, arguments);
		}else{ // getter
			return this.get(name);
		}
	},

	getDescendants: function(){
		// summary:
		//		Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode.
		//		This method should generally be avoided as it returns widgets declared in templates, which are
		//		supposed to be internal/hidden, but it's left here for back-compat reasons.

		kernel.deprecated(this.declaredClass+"::getDescendants() is deprecated. Use getChildren() instead.", "", "2.0");
		return this.containerNode ? query('[widgetId]', this.containerNode).map(registry.byNode) : []; // dijit._Widget[]
	},

	////////////////// MISCELLANEOUS METHODS ///////////////////

	_onShow: function(){
		// summary:
		//		Internal method called when this widget is made visible.
		//		See `onShow` for details.
		this.onShow();
	},

	onShow: function(){
		// summary:
		//		Called when this widget becomes the selected pane in a
		//		`dijit.layout.TabContainer`, `dijit.layout.StackContainer`,
		//		`dijit.layout.AccordionContainer`, etc.
		//
		//		Also called to indicate display of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
		// tags:
		//		callback
	},

	onHide: function(){
		// summary:
			//		Called when another widget becomes the selected pane in a
			//		`dijit.layout.TabContainer`, `dijit.layout.StackContainer`,
			//		`dijit.layout.AccordionContainer`, etc.
			//
			//		Also called to indicate hide of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
			// tags:
			//		callback
	},

	onClose: function(){
		// summary:
		//		Called when this widget is being displayed as a popup (ex: a Calendar popped
		//		up from a DateTextBox), and it is hidden.
		//		This is called from the dijit.popup code, and should not be called directly.
		//
		//		Also used as a parameter for children of `dijit.layout.StackContainer` or subclasses.
		//		Callback if a user tries to close the child.   Child will be closed if this function returns true.
		// tags:
		//		extension

		return true;		// Boolean
	}
});

// For back-compat, remove in 2.0.
if(dojo.ready && !dojo.isAsync){
	dojo.ready(0, function(){
		var requires = ["dijit/_base/focus", "dijit/_base/place", "dijit/_base/popup", "dijit/_base/scroll",
			"dijit/_base/typematic", "dijit/_base/wai", "dijit/_base/window", "dijit/WidgetSet"];
		require(requires);	// use indirection so modules not rolled into a build
	})
}
return _Widget;
});
<commit_msg>Safeguard against null first argument to dojo.connect(), refs #5417, #12451 !strict.<commit_after>define([
	"dojo/aspect",	// aspect.around
	"dojo/_base/config",	// config.isDebug
	"dojo/_base/connect",	// connect.connect
	"dojo/_base/declare", // declare
	"dojo/_base/kernel", // kernel.deprecated
	"dojo/_base/lang", // lang.hitch
	"dojo/query",
	"./registry",	// registry.byNode
	"./_WidgetBase",
	"./_OnDijitClickMixin",
	"./_FocusMixin",
	"dojo/uacss",		// browser sniffing (included for back-compat; subclasses may be using)
	"./hccss"		// high contrast mode sniffing (included to set CSS classes on <body>, module ret value unused)
], function(aspect, config, connect, declare, kernel, lang, query,
			registry, _WidgetBase, _OnDijitClickMixin, _FocusMixin){

/*=====
	var _WidgetBase = dijit._WidgetBase;
	var _OnDijitClickMixin = dijit._OnDijitClickMixin;
	var _FocusMixin = dijit._FocusMixin;
=====*/

// module:
//		dijit/_Widget
// summary:
//		Old base for widgets.   New widgets should extend _WidgetBase instead

function connectToDomNode(){
	// summary:
	//		If user connects to a widget method === this function, then they will
	//		instead actually be connecting the equivalent event on this.domNode
}

// Trap dojo.connect() calls to connectToDomNode methods, and redirect to _Widget.on()
function aroundAdvice(originalConnect){
	return function(obj, event, scope, method){
		if(obj && typeof event == "string" && obj[event] == connectToDomNode){
			return obj.on(event.substring(2).toLowerCase(), lang.hitch(scope, method));
		}
		return originalConnect.apply(connect, arguments);
	};
}
aspect.around(connect, "connect", aroundAdvice);
if(kernel.connect){
	aspect.around(kernel, "connect", aroundAdvice);
}

var _Widget = declare("dijit._Widget", [_WidgetBase, _OnDijitClickMixin, _FocusMixin], {
	// summary:
	//		Base class for all Dijit widgets.
	//
	//		Extends _WidgetBase, adding support for:
	//			- declaratively/programatically specifying widget initialization parameters like
	//				onMouseMove="foo" that call foo when this.domNode gets a mousemove event
	//			- ondijitclick
	//				Support new data-dojo-attach-event="ondijitclick: ..." that is triggered by a mouse click or a SPACE/ENTER keypress
	//			- focus related functions
	//				In particular, the onFocus()/onBlur() callbacks.   Driven internally by
	//				dijit/_base/focus.js.
	//			- deprecated methods
	//			- onShow(), onHide(), onClose()
	//
	//		Also, by loading code in dijit/_base, turns on:
	//			- browser sniffing (putting browser id like .dj_ie on <html> node)
	//			- high contrast mode sniffing (add .dijit_a11y class to <body> if machine is in high contrast mode)

	////////////////// DEFERRED CONNECTS ///////////////////

	onClick: connectToDomNode,
	/*=====
	onClick: function(event){
		// summary:
		//		Connect to this function to receive notifications of mouse click events.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onDblClick: connectToDomNode,
	/*=====
	onDblClick: function(event){
		// summary:
		//		Connect to this function to receive notifications of mouse double click events.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onKeyDown: connectToDomNode,
	/*=====
	onKeyDown: function(event){
		// summary:
		//		Connect to this function to receive notifications of keys being pressed down.
		// event:
		//		key Event
		// tags:
		//		callback
	},
	=====*/
	onKeyPress: connectToDomNode,
	/*=====
	onKeyPress: function(event){
		// summary:
		//		Connect to this function to receive notifications of printable keys being typed.
		// event:
		//		key Event
		// tags:
		//		callback
	},
	=====*/
	onKeyUp: connectToDomNode,
	/*=====
	onKeyUp: function(event){
		// summary:
		//		Connect to this function to receive notifications of keys being released.
		// event:
		//		key Event
		// tags:
		//		callback
	},
	=====*/
	onMouseDown: connectToDomNode,
	/*=====
	onMouseDown: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse button is pressed down.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onMouseMove: connectToDomNode,
	/*=====
	onMouseMove: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse moves over nodes contained within this widget.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onMouseOut: connectToDomNode,
	/*=====
	onMouseOut: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse moves off of nodes contained within this widget.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onMouseOver: connectToDomNode,
	/*=====
	onMouseOver: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse moves onto nodes contained within this widget.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onMouseLeave: connectToDomNode,
	/*=====
	onMouseLeave: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse moves off of this widget.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onMouseEnter: connectToDomNode,
	/*=====
	onMouseEnter: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse moves onto this widget.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onMouseUp: connectToDomNode,
	/*=====
	onMouseUp: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse button is released.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/

	constructor: function(params){
		// extract parameters like onMouseMove that should connect directly to this.domNode
		this._toConnect = {};
		for(var name in params){
			if(this[name] === connectToDomNode){
				this._toConnect[name.replace(/^on/, "").toLowerCase()] = params[name];
				delete params[name];
			}
		}
	},

	postCreate: function(){
		this.inherited(arguments);

		// perform connection from this.domNode to user specified handlers (ex: onMouseMove)
		for(var name in this._toConnect){
			this.on(name, this._toConnect[name]);
		}
		delete this._toConnect;
	},

	on: function(/*String*/ type, /*Function*/ func){
		if(this[this._onMap(type)] === connectToDomNode){
			// Use connect.connect() rather than on() to get handling for "onmouseenter" on non-IE, etc.
			// Also, need to specify context as "this" rather than the default context of the DOMNode
			return connect.connect(this.domNode, type.toLowerCase(), this, func);
		}
		return this.inherited(arguments);
	},

	_setFocusedAttr: function(val){
		// Remove this method in 2.0 (or sooner), just here to set _focused == focused, for back compat
		// (but since it's a private variable we aren't required to keep supporting it).
		this._focused = val;
		this._set("focused", val);
	},

	////////////////// DEPRECATED METHODS ///////////////////

	setAttribute: function(/*String*/ attr, /*anything*/ value){
		// summary:
		//		Deprecated.  Use set() instead.
		// tags:
		//		deprecated
		kernel.deprecated(this.declaredClass+"::setAttribute(attr, value) is deprecated. Use set() instead.", "", "2.0");
		this.set(attr, value);
	},

	attr: function(/*String|Object*/name, /*Object?*/value){
		// summary:
		//		Set or get properties on a widget instance.
		//	name:
		//		The property to get or set. If an object is passed here and not
		//		a string, its keys are used as names of attributes to be set
		//		and the value of the object as values to set in the widget.
		//	value:
		//		Optional. If provided, attr() operates as a setter. If omitted,
		//		the current value of the named property is returned.
		// description:
		//		This method is deprecated, use get() or set() directly.

		// Print deprecation warning but only once per calling function
		if(config.isDebug){
			var alreadyCalledHash = arguments.callee._ach || (arguments.callee._ach = {}),
				caller = (arguments.callee.caller || "unknown caller").toString();
			if(!alreadyCalledHash[caller]){
				kernel.deprecated(this.declaredClass + "::attr() is deprecated. Use get() or set() instead, called from " +
				caller, "", "2.0");
				alreadyCalledHash[caller] = true;
			}
		}

		var args = arguments.length;
		if(args >= 2 || typeof name === "object"){ // setter
			return this.set.apply(this, arguments);
		}else{ // getter
			return this.get(name);
		}
	},

	getDescendants: function(){
		// summary:
		//		Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode.
		//		This method should generally be avoided as it returns widgets declared in templates, which are
		//		supposed to be internal/hidden, but it's left here for back-compat reasons.

		kernel.deprecated(this.declaredClass+"::getDescendants() is deprecated. Use getChildren() instead.", "", "2.0");
		return this.containerNode ? query('[widgetId]', this.containerNode).map(registry.byNode) : []; // dijit._Widget[]
	},

	////////////////// MISCELLANEOUS METHODS ///////////////////

	_onShow: function(){
		// summary:
		//		Internal method called when this widget is made visible.
		//		See `onShow` for details.
		this.onShow();
	},

	onShow: function(){
		// summary:
		//		Called when this widget becomes the selected pane in a
		//		`dijit.layout.TabContainer`, `dijit.layout.StackContainer`,
		//		`dijit.layout.AccordionContainer`, etc.
		//
		//		Also called to indicate display of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
		// tags:
		//		callback
	},

	onHide: function(){
		// summary:
			//		Called when another widget becomes the selected pane in a
			//		`dijit.layout.TabContainer`, `dijit.layout.StackContainer`,
			//		`dijit.layout.AccordionContainer`, etc.
			//
			//		Also called to indicate hide of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
			// tags:
			//		callback
	},

	onClose: function(){
		// summary:
		//		Called when this widget is being displayed as a popup (ex: a Calendar popped
		//		up from a DateTextBox), and it is hidden.
		//		This is called from the dijit.popup code, and should not be called directly.
		//
		//		Also used as a parameter for children of `dijit.layout.StackContainer` or subclasses.
		//		Callback if a user tries to close the child.   Child will be closed if this function returns true.
		// tags:
		//		extension

		return true;		// Boolean
	}
});

// For back-compat, remove in 2.0.
if(dojo.ready && !dojo.isAsync){
	dojo.ready(0, function(){
		var requires = ["dijit/_base/focus", "dijit/_base/place", "dijit/_base/popup", "dijit/_base/scroll",
			"dijit/_base/typematic", "dijit/_base/wai", "dijit/_base/window", "dijit/WidgetSet"];
		require(requires);	// use indirection so modules not rolled into a build
	})
}
return _Widget;
});
<|endoftext|>


<commit_before>var textFrame = null;
var topFrame = null;

function beginUsingTextFrame() {
    textFrame = $("#divText");
    topFrame = $("#divFrame");
    setFrameHeight();
    disableMainScrollbar();

    $(window).resize(function () {
        setFrameHeight();
    });

    window.addText = function (text) {
        textFrame.append(text);
        scrollToEnd();
    }

    window.scrollToEnd = function () {
        textFrame.scrollTop(textFrame.attr("scrollHeight"));
    }

    window.clearScreen = function () {
        textFrame.html("");
    }
}

function setFrameHeight() {
    if (webPlayer) {
        setTimeout(function () {
            if (topFrame.is(":visible")) {
                textFrame.height($("#divOutput").height() - topFrame.height() - 6);
            }
            else {
                textFrame.height($("#divOutput").height() - 6);
            }
            scrollToEnd();
        }, 100);
    }
    else {
        if (topFrame.is(":visible")) {
            textFrame.height($(window).height() - topFrame.position().top - topFrame.height() - 6);
        }
        else {
            textFrame.height($(window).height() - topFrame.position().top - 22);
        }
        scrollToEnd();
    }
}

function setFramePicture(filename) {
    topFrame.show();
    topFrame.html("");
}

function clearFramePicture(filename) {
    topFrame.hide();
    setFrameHeight();
}<commit_msg>#739 - fix resizing issue when "static frame" is turned on and no picture is displayed<commit_after>var textFrame = null;
var topFrame = null;

function beginUsingTextFrame() {
    textFrame = $("#divText");
    topFrame = $("#divFrame");
    clearFramePicture();
    setFrameHeight();
    disableMainScrollbar();

    $(window).resize(function () {
        setFrameHeight();
    });

    window.addText = function (text) {
        textFrame.append(text);
        scrollToEnd();
    }

    window.scrollToEnd = function () {
        textFrame.scrollTop(textFrame.attr("scrollHeight"));
    }

    window.clearScreen = function () {
        textFrame.html("");
    }
}

function setFrameHeight() {
    if (webPlayer) {
        setTimeout(function () {
            if (topFrame.is(":visible")) {
                textFrame.height($("#divOutput").height() - topFrame.height() - 6);
            }
            else {
                textFrame.height($("#divOutput").height() - 6);
            }
            scrollToEnd();
        }, 100);
    }
    else {
        if (topFrame.is(":visible")) {
            textFrame.height($(window).height() - topFrame.position().top - topFrame.height() - 6);
        }
        else {
            textFrame.height($(window).height() - topFrame.position().top - 22);
        }
        scrollToEnd();
    }
}

function setFramePicture(filename) {
    topFrame.show();
    topFrame.html("");
}

function clearFramePicture(filename) {
    topFrame.hide();
    setFrameHeight();
}<|endoftext|>