<commit_before>"use strict";

var getPackageName = function(globalName, global){
  for (var packageName in Package) {
    if(Package[packageName] && Package[packageName].globalName === global) {
      return packageName;
    }
  }
};

var getPackage = function(packageName){
  return Package[packageName];
};

Meteor.setTimeout(function(){

  // var inspectKeys = function(name, obj){
  //   for (var key in obj) {
  //     console.log(name+'[',key,']', '(', typeof(obj[key]), ')' );
  //   }
  // };

  var isPackageGlobal = function(obj){

    for (var packageName in Package) {
      for (var key in Package[packageName]) {
        if ( Package[packageName][key] === obj ) {
          return packageName;
        }
      }
    }

    return false;
  };

  var eachFrom = function(obj, fn) {

    for (var key in obj) {
      var value = obj[key];
      var packageName;

      if (value === global) {}
      else if (value === lodash) {}
      else if (value === process) {}
      else if (value === console) {}
      else if (value === ArrayBuffer) {}
      else if (value === Int8Array) {}
      else if (value === Uint8Array) {}
      else if (value === Uint8ClampedArray) {}
      else if (value === Int16Array) {}
      else if (value === Uint16Array) {}
      else if (value === Int32Array ) {}
      else if (value === Uint32Array ) {}
      else if (value === Float32Array ) {}
      else if (value === Float64Array ) {}
      // else if (typeof(value) === 'function') {}
      else if (value instanceof Meteor.Collection) {
        fn(value, key, "Collection");
        // console.log(lodash.padRight(typeof(value), 10), ':', lodash.padRight('Collection', 28), ':', key);
      }
      else if ( ( packageName = isPackageGlobal(value) ) !== false) {
        fn(value, key, packageName);
        // console.log(lodash.padRight(typeof(value), 10), ':', lodash.padRight(packageName, 28), ':', key);
      }
      else {
        fn(value, key);
        // console.log(lodash.padRight(typeof(value), 41), ':', key);
      }
    }

  };

  var listSomeGlobals = function(){

    eachFrom(global, function(gVal, gKey, gContext){
      console.log(lodash.padRight(typeof(gVal), 10), ':', lodash.padRight(gContext, 30), ':', gKey);
      eachFrom(gVal, function(ggVal, ggKey, ggContext){
        console.log(lodash.padRight(typeof(ggVal), 10), ':', lodash.padRight(ggContext, 30), ':', gKey+'.'+ggKey);
      });
    });

    // for (var key in global) {
    //   var g = global[key];
    //   var packageName;

    //   if (g === global) {}
    //   // else if (g === process) {}
    //   // else if (g === console) {}
    //   // else if (typeof(g) === 'function') {}
    //   else if (g instanceof Meteor.Collection) {
    //     console.log(lodash.padRight(typeof(g), 10), ':', lodash.padRight('Collection', 28), ':', key);
    //   }
    //   else if ( ( packageName = isPackageGlobal(g) ) !== false) {
    //     console.log(lodash.padRight(typeof(g), 10), ':', lodash.padRight(packageName, 28), ':', key);
    //   }
    //   else {
    //     console.log(lodash.padRight(typeof(g), 41), ':', key);
    //   }
    // }
  };

  listSomeGlobals();

  // _ ( function )
  // __meteor_bootstrap__ ( object )
  // __meteor_bootstrap__[ configJson ] ( object )
  // __meteor_bootstrap__[ serverDir ] ( string )
  // __meteor_bootstrap__[ startupHooks ] ( object )
  // __meteor_runtime_config__ ( object )
  // __meteor_runtime_config__[ appId ] ( string )
  // __meteor_runtime_config__[ meteorRelease ] ( string )
  // __meteor_runtime_config__[ ROOT_URL ] ( string )
  // __meteor_runtime_config__[ ROOT_URL_PATH_PREFIX ] ( string )
  // Accounts ( object )
  // ActionItems ( object )
  // ActionItemsDoc ( function )
  // ActiveRoute ( object )
  // ArrayBuffer ( function )
  // Blaze ( object )
  // Buffer ( function )
  // check ( function )
  // clearImmediate ( function )
  // clearInterval ( function )
  // clearTimeout ( function )
  // CollectionBehaviours ( object )
  // CollectionHooks ( object )
  // console ( object )
  // DataView ( function )
  // DDP ( object )
  // DDP[ _allSubscriptionsReady ] ( function )
  // DDP[ _CurrentInvocation ] ( object )
  // DDP[ connect ] ( function )
  // DDP[ ConnectionError ] ( function )
  // DDP[ ForcedReconnectError ] ( function )
  // DDP[ randomStream ] ( function )
  // DDPServer ( object )
  // DDPServer[ _Crossbar ] ( function )
  // DDPServer[ _CurrentWriteFence ] ( object )
  // DDPServer[ _InvalidationCrossbar ] ( object )
  // DDPServer[ _WriteFence ] ( function )
  // Deps ( object )
  // Deps[ _computations ] ( object )
  // Deps[ _runFlush ] ( function )
  // Deps[ active ] ( boolean )
  // Deps[ afterFlush ] ( function )
  // Deps[ autorun ] ( function )
  // Deps[ Computation ] ( function )
  // Deps[ currentComputation ] ( object )
  // Deps[ depend ] ( function )
  // Deps[ Dependency ] ( function )
  // Deps[ flush ] ( function )
  // Deps[ nonreactive ] ( function )
  // Deps[ onInvalidate ] ( function )
  // DTRACE_HTTP_CLIENT_REQUEST ( function )
  // DTRACE_HTTP_CLIENT_RESPONSE ( function )
  // DTRACE_HTTP_SERVER_REQUEST ( function )
  // DTRACE_HTTP_SERVER_RESPONSE ( function )
  // DTRACE_NET_SERVER_CONNECTION ( function )
  // DTRACE_NET_SOCKET_READ ( function )
  // DTRACE_NET_SOCKET_WRITE ( function )
  // DTRACE_NET_STREAM_END ( function )
  // EJSON ( object )
  // environment ( string )
  // Files ( object )
  // Files[ _collection ] ( object )
  // Files[ _connection ] ( object )
  // Files[ _createCappedCollection ] ( function )
  // Files[ _defineMutationMethods ] ( function )
  // Files[ _driver ] ( object )
  // Files[ _dropCollection ] ( function )
  // Files[ _dropIndex ] ( function )
  // Files[ _ensureIndex ] ( function )
  // Files[ _getFindOptions ] ( function )
  // Files[ _getFindSelector ] ( function )
  // Files[ _hookAspects ] ( object )
  // Files[ _insecure ] ( undefined )
  // Files[ _isInsecure ] ( function )
  // Files[ _makeNewID ] ( function )
  // Files[ _name ] ( string )
  // Files[ _prefix ] ( string )
  // Files[ _restricted ] ( boolean )
  // Files[ _transform ] ( object )
  // Files[ _updateFetch ] ( function )
  // Files[ _validatedInsert ] ( function )
  // Files[ _validatedRemove ] ( function )
  // Files[ _validatedUpdate ] ( function )
  // Files[ _validators ] ( object )
  // Files[ after ] ( object )
  // Files[ allow ] ( function )
  // Files[ attachBehaviour ] ( function )
  // Files[ before ] ( object )
  // Files[ deny ] ( function )
  // Files[ direct ] ( object )
  // Files[ find ] ( function )
  // Files[ findOne ] ( function )
  // Files[ helpers ] ( function )
  // Files[ hookOptions ] ( object )
  // Files[ insert ] ( function )
  // Files[ rawCollection ] ( function )
  // Files[ rawDatabase ] ( function )
  // Files[ remove ] ( function )
  // Files[ restore ] ( function )
  // Files[ softRemove ] ( function )
  // Files[ update ] ( function )
  // Files[ upsert ] ( function )
  // Float32Array ( function )
  // Float64Array ( function )
  // global ( object )
  // GLOBAL ( object )
  // Handlebars ( object )
  // HTML ( object )
  // Int16Array ( function )
  // Int32Array ( function )
  // Int8Array ( function )
  // Iron ( object )
  // lodash ( function )
  // Log ( function )
  // Logger ( function )
  // main ( function )
  // Mandrill ( object )
  // Match ( object )
  // Meteor ( object )
  // Meteor[ _debug ] ( function )
  // Meteor[ _delete ] ( function )
  // Meteor[ _DoubleEndedQueue ] ( function )
  // Meteor[ _ensure ] ( function )
  // Meteor[ _get ] ( function )
  // Meteor[ _inherits ] ( function )
  // Meteor[ _nodeCodeMustBeInFiber ] ( function )
  // Meteor[ _noYieldsAllowed ] ( function )
  // Meteor[ _relativeToSiteRootUrl ] ( function )
  // Meteor[ _setImmediate ] ( function )
  // Meteor[ _sleepForMs ] ( function )
  // Meteor[ _suppress_log ] ( function )
  // Meteor[ _supressed_log_expected ] ( function )
  // Meteor[ _SynchronousQueue ] ( function )
  // Meteor[ _wrapAsync ] ( function )
  // Meteor[ absoluteUrl ] ( function )
  // Meteor[ apply ] ( function )
  // Meteor[ autorun ] ( function )
  // Meteor[ autosubscribe ] ( function )
  // Meteor[ bindEnvironment ] ( function )
  // Meteor[ call ] ( function )
  // Meteor[ clearInterval ] ( function )
  // Meteor[ clearTimeout ] ( function )
  // Meteor[ Collection ] ( function )
  // Meteor[ default_server ] ( object )
  // Meteor[ defer ] ( function )
  // Meteor[ enviornment ] ( string )
  // Meteor[ EnvironmentVariable ] ( function )
  // Meteor[ Error ] ( function )
  // Meteor[ flush ] ( function )
  // Meteor[ http ] ( object )
  // Meteor[ isClient ] ( boolean )
  // Meteor[ isCordova ] ( boolean )
  // Meteor[ isDemo ] ( boolean )
  // Meteor[ isDevelopment ] ( boolean )
  // Meteor[ isProduction ] ( boolean )
  // Meteor[ isServer ] ( boolean )
  // Meteor[ isStaging ] ( boolean )
  // Meteor[ makeErrorType ] ( function )
  // Meteor[ methods ] ( function )
  // Meteor[ onConnection ] ( function )
  // Meteor[ publish ] ( function )
  // Meteor[ refresh ] ( function )
  // Meteor[ release ] ( string )
  // Meteor[ server ] ( object )
  // Meteor[ setInterval ] ( function )
  // Meteor[ setTimeout ] ( function )
  // Meteor[ settings ] ( object )
  // Meteor[ startup ] ( function )
  // Meteor[ tags ] ( object )
  // Meteor[ user ] ( function )
  // Meteor[ userId ] ( function )
  // Meteor[ users ] ( object )
  // Meteor[ uuid ] ( function )
  // Meteor[ wrapAsync ] ( function )
  // Migrations ( object )
  // moment ( function )
  // Mongo ( object )
  // MongoInternals ( object )
  // Notepads ( object )
  // Package ( object )
  // Package[ accounts-base ] ( object )
  // Package[ accounts-facebook ] ( object )
  // Package[ accounts-oauth ] ( object )
  // Package[ accounts-password ] ( object )
  // Package[ aldeed:template-extension ] ( object )
  // Package[ autopublish ] ( object )
  // Package[ autoupdate ] ( object )
  // Package[ base64 ] ( object )
  // Package[ binary-heap ] ( object )
  // Package[ blaze ] ( object )
  // Package[ blaze-tools ] ( object )
  // Package[ boilerplate-generator ] ( object )
  // Package[ callback-hook ] ( object )
  // Package[ check ] ( object )
  // Package[ coffeescript ] ( object )
  // Package[ dburles:collection-helpers ] ( object )
  // Package[ ddp ] ( object )
  // Package[ deps ] ( object )
  // Package[ ejson ] ( object )
  // Package[ email ] ( object )
  // Package[ facebook ] ( object )
  // Package[ fortawesome:fontawesome ] ( object )
  // Package[ froala:editor-reactive ] ( object )
  // Package[ geojson-utils ] ( object )
  // Package[ html-tools ] ( object )
  // Package[ htmljs ] ( object )
  // Package[ http ] ( object )
  // Package[ id-map ] ( object )
  // Package[ insecure ] ( object )
  // Package[ iron:controller ] ( object )
  // Package[ iron:core ] ( object )
  // Package[ iron:dynamic-template ] ( object )
  // Package[ iron:layout ] ( object )
  // Package[ iron:location ] ( object )
  // Package[ iron:middleware-stack ] ( object )
  // Package[ iron:router ] ( object )
  // Package[ iron:url ] ( object )
  // Package[ jparker:crypto-core ] ( object )
  // Package[ jparker:crypto-hmac ] ( object )
  // Package[ jparker:crypto-sha256 ] ( object )
  // Package[ jquery ] ( object )
  // Package[ json ] ( object )
  // Package[ livedata ] ( object )
  // Package[ localstorage ] ( object )
  // Package[ logging ] ( object )
  // Package[ matb33:collection-hooks ] ( object )
  // Package[ meteor ] ( object )
  // Package[ meteor-platform ] ( object )
  // Package[ minimongo ] ( object )
  // Package[ momentjs:moment ] ( object )
  // Package[ mongo ] ( object )
  // Package[ mongo-livedata ] ( object )
  // Package[ npm-bcrypt ] ( object )
  // Package[ oauth ] ( object )
  // Package[ oauth2 ] ( object )
  // Package[ observe-sequence ] ( object )
  // Package[ ordered-dict ] ( object )
  // Package[ patrickleet:tags ] ( object )
  // Package[ percolate:migrations ] ( object )
  // Package[ random ] ( object )
  // Package[ reactive-dict ] ( object )
  // Package[ reactive-var ] ( object )
  // Package[ reload ] ( object )
  // Package[ retry ] ( object )
  // Package[ routepolicy ] ( object )
  // Package[ service-configuration ] ( object )
  // Package[ session ] ( object )
  // Package[ sha ] ( object )
  // Package[ spacebars ] ( object )
  // Package[ spacebars-compiler ] ( object )
  // Package[ srp ] ( object )
  // Package[ stevezhu:lodash ] ( object )
  // Package[ stylus ] ( object )
  // Package[ templating ] ( object )
  // Package[ tracker ] ( object )
  // Package[ ui ] ( object )
  // Package[ underscore ] ( object )
  // Package[ url ] ( object )
  // Package[ webapp ] ( object )
  // Package[ webapp-hashing ] ( object )
  // Package[ wylio:mandrill ] ( object )
  // Package[ ziftr:ziftr-api ] ( object )
  // Package[ zimme:active-route ] ( object )
  // Package[ zimme:collection-behaviours ] ( object )
  // Package[ zimme:collection-softremovable ] ( object )
  // Package[ zimme:collection-timestampable ] ( object )
  // People ( object )
  // process ( object )
  // process[ _channel ] ( object )
  // process[ _currentTickHandler ] ( function )
  // process[ _debugEnd ] ( function )
  // process[ _debugPause ] ( function )
  // process[ _debugProcess ] ( function )
  // process[ _emittingTopLevelDomainError ] ( boolean )
  // process[ _events ] ( object )
  // process[ _exiting ] ( boolean )
  // process[ _fatalException ] ( function )
  // process[ _getActiveHandles ] ( function )
  // process[ _getActiveRequests ] ( function )
  // process[ _handleQueue ] ( object )
  // process[ _immediateCallback ] ( function )
  // process[ _kill ] ( function )
  // process[ _maxListeners ] ( number )
  // process[ _needImmediateCallback ] ( boolean )
  // process[ _needTickCallback ] ( function )
  // process[ _nextDomainTick ] ( function )
  // process[ _tickCallback ] ( function )
  // process[ _tickDomainCallback ] ( function )
  // process[ _tickFromSpinner ] ( function )
  // process[ _tickInfoBox ] ( object )
  // process[ _usingDomains ] ( function )
  // process[ abort ] ( function )
  // process[ addListener ] ( function )
  // process[ arch ] ( string )
  // process[ argv ] ( object )
  // process[ assert ] ( function )
  // process[ binding ] ( function )
  // process[ chdir ] ( function )
  // process[ config ] ( object )
  // process.config[ target_defaults ] ( object )
  // process.config.target_defaults[ cflags ] ( object )
  // process.config.target_defaults[ default_configuration ] ( string )
  // process.config.target_defaults[ defines ] ( object )
  // process.config.target_defaults[ include_dirs ] ( object )
  // process.config.target_defaults[ libraries ] ( object )
  // process.config[ variables ] ( object )
  // process.config.variables[ clang ] ( number )
  // process.config.variables[ host_arch ] ( string )
  // process.config.variables[ node_install_npm ] ( boolean )
  // process.config.variables[ node_prefix ] ( string )
  // process.config.variables[ node_shared_cares ] ( boolean )
  // process.config.variables[ node_shared_http_parser ] ( boolean )
  // process.config.variables[ node_shared_libuv ] ( boolean )
  // process.config.variables[ node_shared_openssl ] ( boolean )
  // process.config.variables[ node_shared_v8 ] ( boolean )
  // process.config.variables[ node_shared_zlib ] ( boolean )
  // process.config.variables[ node_tag ] ( string )
  // process.config.variables[ node_unsafe_optimizations ] ( number )
  // process.config.variables[ node_use_dtrace ] ( boolean )
  // process.config.variables[ node_use_etw ] ( boolean )
  // process.config.variables[ node_use_openssl ] ( boolean )
  // process.config.variables[ node_use_perfctr ] ( boolean )
  // process.config.variables[ openssl_no_asm ] ( number )
  // process.config.variables[ python ] ( string )
  // process.config.variables[ target_arch ] ( string )
  // process.config.variables[ v8_enable_gdbjit ] ( number )
  // process.config.variables[ v8_no_strict_aliasing ] ( number )
  // process.config.variables[ v8_use_snapshot ] ( boolean )
  // process.config.variables[ want_separate_host_toolset ] ( number )
  // process[ connected ] ( boolean )
  // process[ cwd ] ( function )
  // process[ debugPort ] ( number )
  // process[ disconnect ] ( function )
  // process[ dlopen ] ( function )
  // process[ domain ] ( object )
  // process[ emit ] ( function )
  // process[ env ] ( object )
  // process[ EventEmitter ] ( function )
  // process[ execArgv ] ( object )
  // process[ execPath ] ( string )
  // process[ exit ] ( function )
  // process[ features ] ( object )
  // process[ fiberLib ] ( function )
  // process[ getgid ] ( function )
  // process[ getgroups ] ( function )
  // process[ getuid ] ( function )
  // process[ hrtime ] ( function )
  // process[ initgroups ] ( function )
  // process[ kill ] ( function )
  // process[ listeners ] ( function )
  // process[ mainModule ] ( object )
  // process[ maxTickDepth ] ( number )
  // process[ memoryUsage ] ( function )
  // process[ moduleLoadList ] ( object )
  // process[ nextTick ] ( function )
  // process[ on ] ( function )
  // process[ once ] ( function )
  // process[ openStdin ] ( function )
  // process[ pid ] ( number )
  // process[ platform ] ( string )
  // process[ reallyExit ] ( function )
  // process[ removeAllListeners ] ( function )
  // process[ removeListener ] ( function )
  // process[ send ] ( function )
  // process[ setgid ] ( function )
  // process[ setgroups ] ( function )
  // process[ setMaxListeners ] ( function )
  // process[ setuid ] ( function )
  // process[ stderr ] ( object )
  // process[ stdin ] ( object )
  // process[ stdout ] ( object )
  // process[ title ] ( string )
  // process[ umask ] ( function )
  // process[ uptime ] ( function )
  // process[ version ] ( string )
  // process[ versions ] ( object )
  // Projects ( object )
  // Random ( object )
  // root ( object ) === global
  // RouteController ( function )
  // Router ( function )
  // ServiceConfiguration ( object )
  // ServiceConfiguration[ ConfigError ] ( function )
  // ServiceConfiguration[ configurations ] ( object )
  // ServiceConfiguration.configurations[ _collection ] ( object )
  // ServiceConfiguration.configurations[ _connection ] ( object )
  // ServiceConfiguration.configurations[ _createCappedCollection ] ( function )
  // ServiceConfiguration.configurations[ _defineMutationMethods ] ( function )
  // ServiceConfiguration.configurations[ _driver ] ( object )
  // ServiceConfiguration.configurations[ _dropCollection ] ( function )
  // ServiceConfiguration.configurations[ _dropIndex ] ( function )
  // ServiceConfiguration.configurations[ _ensureIndex ] ( function )
  // ServiceConfiguration.configurations[ _getFindOptions ] ( function )
  // ServiceConfiguration.configurations[ _getFindSelector ] ( function )
  // ServiceConfiguration.configurations[ _hookAspects ] ( object )
  // ServiceConfiguration.configurations[ _insecure ] ( undefined )
  // ServiceConfiguration.configurations[ _isInsecure ] ( function )
  // ServiceConfiguration.configurations[ _makeNewID ] ( function )
  // ServiceConfiguration.configurations[ _name ] ( string ) "meteor_accounts_loginServiceConfiguration"
  // ServiceConfiguration.configurations[ _prefix ] ( string ) "/meteor_accounts_loginServiceConfiguration/"
  // ServiceConfiguration.configurations[ _restricted ] ( boolean )
  // ServiceConfiguration.configurations[ _transform ] ( object )
  // ServiceConfiguration.configurations[ _updateFetch ] ( function )
  // ServiceConfiguration.configurations[ _validatedInsert ] ( function )
  // ServiceConfiguration.configurations[ _validatedRemove ] ( function )
  // ServiceConfiguration.configurations[ _validatedUpdate ] ( function )
  // ServiceConfiguration.configurations[ _validators ] ( object )
  // ServiceConfiguration.configurations[ after ] ( object )
  // ServiceConfiguration.configurations[ allow ] ( function )
  // ServiceConfiguration.configurations[ attachBehaviour ] ( function )
  // ServiceConfiguration.configurations[ before ] ( object )
  // ServiceConfiguration.configurations[ deny ] ( function )
  // ServiceConfiguration.configurations[ direct ] ( object )
  // ServiceConfiguration.configurations[ find ] ( function )
  // ServiceConfiguration.configurations[ findOne ] ( function )
  // ServiceConfiguration.configurations[ helpers ] ( function )
  // ServiceConfiguration.configurations[ hookOptions ] ( object )
  // ServiceConfiguration.configurations[ insert ] ( function )
  // ServiceConfiguration.configurations[ rawCollection ] ( function )
  // ServiceConfiguration.configurations[ rawDatabase ] ( function )
  // ServiceConfiguration.configurations[ remove ] ( function )
  // ServiceConfiguration.configurations[ update ] ( function )
  // ServiceConfiguration.configurations[ upsert ] ( function )
  // setImmediate ( function )
  // setInterval ( function )
  // setTimeout ( function )
  // Spacebars ( object )
  // Tags ( object )
  // Tracker ( object )
  // Tracker[ _computations ] ( object )
  // Tracker[ _runFlush ] ( function )
  // Tracker[ active ] ( boolean )
  // Tracker[ afterFlush ] ( function )
  // Tracker[ autorun ] ( function )
  // Tracker[ Computation ] ( function )
  // Tracker[ currentComputation ] ( object )
  // Tracker[ depend ] ( function )
  // Tracker[ Dependency ] ( function )
  // Tracker[ flush ] ( function )
  // Tracker[ nonreactive ] ( function )
  // Tracker[ onInvalidate ] ( function )
  // UI ( object )
  // UI[ _addEventMap ] ( function )
  // UI[ _calculateCondition ] ( function )
  // UI[ _createView ] ( function )
  // UI[ _destroyNode ] ( function )
  // UI[ _destroyView ] ( function )
  // UI[ _escape ] ( function )
  // UI[ _expand ] ( function )
  // UI[ _expandAttributes ] ( function )
  // UI[ _expandView ] ( function )
  // UI[ _fireCallbacks ] ( function )
  // UI[ _getCurrentView ] ( function )
  // UI[ _getElementView ] ( function )
  // UI[ _getParentView ] ( function )
  // UI[ _getTemplateHelper ] ( function )
  // UI[ _globalHelpers ] ( object )
  // UI[ _HTMLJSExpander ] ( function )
  // UI[ _InOuterTemplateScope ] ( function )
  // UI[ _isContentEqual ] ( function )
  // UI[ _materializeView ] ( function )
  // UI[ _OLDSTYLE_HELPER ] ( object )
  // UI[ _parentData ] ( function )
  // UI[ _reportException ] ( function )
  // UI[ _templateInstance ] ( function )
  // UI[ _TemplateWith ] ( function )
  // UI[ _throwNextException ] ( boolean )
  // UI[ _toText ] ( function )
  // UI[ _warn ] ( function )
  // UI[ _withCurrentView ] ( function )
  // UI[ _wrapCatchingExceptions ] ( function )
  // UI[ currentView ] ( object )
  // UI[ Each ] ( function )
  // UI[ getData ] ( function )
  // UI[ getElementData ] ( function )
  // UI[ getView ] ( function )
  // UI[ If ] ( function )
  // UI[ InOuterTemplateScope ] ( function )
  // UI[ insert ] ( function )
  // UI[ isTemplate ] ( function )
  // UI[ ReactiveVar ] ( function )
  // UI[ registerHelper ] ( function )
  // UI[ remove ] ( function )
  // UI[ render ] ( function )
  // UI[ renderWithData ] ( function )
  // UI[ Template ] ( function )
  // UI[ TemplateInstance ] ( function )
  // UI[ toHTML ] ( function )
  // UI[ toHTMLWithData ] ( function )
  // UI[ Unless ] ( function )
  // UI[ View ] ( function )
  // UI[ With ] ( function )
  // Uint16Array ( function )
  // Uint32Array ( function )
  // Uint8Array ( function )
  // Uint8ClampedArray ( function )
  // Users ( object )
  // WebApp ( object )
  // WebApp[ _timeoutAdjustmentRequestCallback ] ( function )
  // WebApp[ addHtmlAttributeHook ] ( function )
  // WebApp[ categorizeRequest ] ( function )
  // WebApp[ clientPrograms ] ( object )
  // WebApp[ connectHandlers ] ( function )
  // WebApp[ defaultArch ] ( string )
  // WebApp[ httpServer ] ( object )
  // WebApp[ onListening ] ( function )
  // WebApp[ rawConnectHandlers ] ( function )
  // WebApp[ suppressConnectErrors ] ( function )
  // WebAppInternals ( object )
  // ZiftrApi ( object )

  // inspectKeys('WebApp', WebApp);
  // console.log('ServiceConfiguration.configurations._prefix', ServiceConfiguration.configurations._prefix);

  // inspectKeys('Npm', Npm);
  // inspectKeys('Package', Package);
  // inspectKeys('Meteor', Meteor);
  // inspectKeys('Meteor.server', Meteor.server);

  // console.log('Package', Package);
  // console.log('Package["ziftr"]', Package["ziftr"]);
  // console.log('Package["ziftr:ziftr-api"]', Package["ziftr:ziftr-api"]);
  // console.log('Package["ziftr-api"]', Package["ziftr-api"]);
  // console.log('getPackageName() =>', getPackageName('ZiftrApi', ZiftrApi));
  // console.log('getPackage(...) =>', getPackage(getPackageName('ZiftrApi', ZiftrApi)));

}, 10);
<commit_msg>Deleted src/ZiftrApi.ignore.js<commit_after><|endoftext|>


<commit_before>import "./integration"
import "./disableRanges-test"
import "./disableRanges-integration-test"
<commit_msg>Add plugin tests to index<commit_after>import "./integration"
import "./disableRanges-test"
import "./disableRanges-integration-test"
import "./plugins-test"
<|endoftext|>


<commit_before>(function(window, factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD
    define(factory);
  } else if (typeof exports === 'object') {
    // CommonJS
    module.exports = factory();
  } else {
    // Browser Global (constants is your global library identifier)
    window.constants = factory();
  }
}(this, function() {
  <%= contents %>
  return require('constants');
}));
<commit_msg>Update bundleTemplate path<commit_after>(function(window, factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD
    define(factory);
  } else if (typeof exports === 'object') {
    // CommonJS
    module.exports = factory();
  } else {
    // Browser Global (constants is your global library identifier)
    window.constants = factory();
  }
}(this, function() {
  <%= contents %>
  return require('./constants');
}));
<|endoftext|>


<commit_before>import Rx from 'rx'
import request from 'axios'
import moment from 'moment'
import { decode } from 'base-64'

const INDEX_URL = "https://api.github.com/repos/tldr-pages/tldr-pages.github.io/contents/assets/index.json"

// in-memory storage
let _commands

let search = (name) => {
  return getIndex()
    .filter( byName(name) )
    .last( fallbackCommand()  )
}

let byName = (name) => {
  return (cmd) => cmd.name === name
}

let fallbackCommand = () =>  ({ platform: ["client"],
                                name: "not-found" })

let requestIndex = function *() {
  let response =  yield request(requestOptions())
  return response
}

let requestOptions = () => {
  let opts = {
    method: 'GET',
    url: INDEX_URL,
    withCredentials: false
  }

  let modifiedSince = hasModifiedSince()
  if(modifiedSince) {
    opts.headers = {
      'If-Modified-Since': modifiedSince
    }
  }

  return opts
}

let hasModifiedSince = () => {
  let expiry = localStorage.getItem("tldr/index.expiry")
  // Yes, that's right, it's the string "undefined"...gee.
  // So if it's truthy (not null, not undefined) it works
  if( expiry && expiry !== "undefined" ) {
    return expiry
  } else {
    return false
  }
}

let getIndex = () => {
  return Rx.Observable.spawn(requestIndex)
    .tap(cache)
    .flatMap(toCommands)
    .catch(localIndex)
}

let localIndex = (e) => {
  if( ! _commands ) {
    let raw = localStorage.getItem('tldr/index')
    _commands = JSON.parse(raw)
  }
  return Rx.Observable.fromArray(_commands)
}

let toCommands = (res) => {
  let hasData = res.status === 200
  if (hasData) {
    return parse(res.data.content).commands
  } else {
    throw new Error("Fallback to cache")
  }
}

let parse = (raw) => JSON.parse(decode(raw))

let cache = (res) => {
  let shouldCache = res.status === 200
  if(shouldCache) {
    let ifModifiedSince = res.headers["last-modified"]
    localStorage.setItem("tldr/index.expiry", ifModifiedSince)
    let commands = JSON.stringify(parse(res.data.content).commands)
    localStorage.setItem("tldr/index", commands)
  }
}

// Public API
let Command = {
  search,
  getIndex
}

export { Command }

<commit_msg>Close #31.<commit_after>import Rx from 'rx'
import request from 'axios'
import moment from 'moment'
import { decode } from 'base-64'
import QS from 'query-string'

const INDEX_URL = "https://api.github.com/repos/tldr-pages/tldr-pages.github.io/contents/assets/index.json"

// in-memory storage
let _commands

let search = (name) => {
  let { platform } = QS.parse(location.search)
  if (platform)  {
    let command = buildCommand(name, [platform])
    return Rx.Observable.fromArray([command])
  } else {
    return getIndex()
      .filter( byName(name) )
      .last( fallbackCommand()  )
  }
}

let byName = (name) => {
  return (cmd) => cmd.name === name
}

let buildCommand    = (name, platforms) =>  ({ platform: platforms, name })
let fallbackCommand = () =>  buildCommand("not-found", ["client"])

let requestIndex = function *() {
  let response =  yield request(requestOptions())
  return response
}

let requestOptions = () => {
  let opts = {
    method: 'GET',
    url: INDEX_URL,
    withCredentials: false
  }

  let modifiedSince = hasModifiedSince()
  if(modifiedSince) {
    opts.headers = {
      'If-Modified-Since': modifiedSince
    }
  }

  return opts
}

let hasModifiedSince = () => {
  let expiry = localStorage.getItem("tldr/index.expiry")
  // Yes, that's right, it's the string "undefined"...gee.
  // So if it's truthy (not null, not undefined) it works
  if( expiry && expiry !== "undefined" ) {
    return expiry
  } else {
    return false
  }
}

let getIndex = () => {
  return Rx.Observable.spawn(requestIndex)
    .tap(cache)
    .flatMap(toCommands)
    .catch(localIndex)
}

let localIndex = (e) => {
  if( ! _commands ) {
    let raw = localStorage.getItem('tldr/index')
    _commands = JSON.parse(raw)
  }
  return Rx.Observable.fromArray(_commands)
}

let toCommands = (res) => {
  let hasData = res.status === 200
  if (hasData) {
    return parse(res.data.content).commands
  } else {
    throw new Error("Fallback to cache")
  }
}

let parse = (raw) => JSON.parse(decode(raw))

let cache = (res) => {
  let shouldCache = res.status === 200
  if(shouldCache) {
    let ifModifiedSince = res.headers["last-modified"]
    localStorage.setItem("tldr/index.expiry", ifModifiedSince)
    let commands = JSON.stringify(parse(res.data.content).commands)
    localStorage.setItem("tldr/index", commands)
  }
}

// Public API
let Command = {
  search,
  getIndex
}

export { Command }

<|endoftext|>


<commit_before>import fetch from 'isomorphic-fetch'

// determine where to send requests
const www = window.location.href.indexOf('www.') !== -1 ? 'www.' : ''
const url = process.env.NODE_ENV === 'production'
  ? `https://${www}amodahl.no/api/public` : `http://${www}local.amodahl.no:3000`

const scopes = [
  'user.all',
  'user.list',
  'chess-game.all',
  'chess-game.list',
  'chess-game.create',
  'chess-move.all',
  'chess-move.list',
  'chess-move.create',
  'update.all',
  'update.list'
]

export const requestTokenFromLocalStorage = () => {
  return {
    type: 'REQUEST_TOKEN_FROM_LOCAL_STORAGE'
  }
}

export const requestTokenFromServer = () => {
  return {
    type: 'REQUEST_TOKEN_FROM_SERVER'
  }
}

export const requestTokenFailed = (message, displayMessage) => {
  return {
    message: message,
    displayMessage: displayMessage,
    jwToken: null,
    type: 'REQUEST_TOKEN_FAILED'
  }
}

export const deleteToken = (dispatcher) => {
  return {
    jwToken: null,
    type: '@@' + dispatcher + '/DELETE_TOKEN'
  }
}

export const receiveTokenFromServer = (jwToken, name, email) => {
  return {
    jwToken: jwToken,
    name: name,
    email: email,
    type: 'RECEIVE_TOKEN_FROM_SERVER'
  }
}

export const receiveTokenFromLocalStorage = (jwToken, name, email) => {
  return {
    jwToken: jwToken,
    name: name,
    email: email,
    type: 'RECEIVE_TOKEN_FROM_LOCAL_STORAGE'
  }
}

function fetchTestUserJwtToken (dispatch, name, email, mockFacebookId, scopes, onSuccess) {
  var form = new FormData()
  form.append('requested_scopes', JSON.stringify(scopes))
  form.append('name', name)
  form.append('email', email)
  form.append('mock_facebook_id', mockFacebookId)

  fetch(url + '/token', {
    method: 'POST',
    body: form
  })
  .then(response => { // ensure its json
    let contentType = response.headers.get('content-type')
    let gotJson = contentType && contentType.indexOf('application/json') !== -1
    console.log(response)
    if (!gotJson) {
      throw new Error('Oops, we haven\'t got JSON: ' + JSON.stringify(response))
    }
    return response.json()
  })
  .then(response => { // ensure query was accepted
    let ok = response.status === 'ok'

    if (!ok) {
      throw new Error('Received: ' + JSON.stringify(response))
    }

    // finally got token
    onSuccess(response)
  })
  .catch(error => { // all errors are handled here
    dispatch(requestTokenFailed('Something went wrong: ' + error, true))
  })
}

function fetchJwtToken (dispatch, fbAccessToken, scopes, onSuccess) {
  var form = new FormData()
  form.append('fb_access_token', fbAccessToken)
  form.append('requested_scopes', JSON.stringify(scopes))

  fetch(url + '/token', {
    method: 'POST',
    body: form
  })
  .then(response => { // ensure its json
    let contentType = response.headers.get('content-type')
    let gotJson = contentType && contentType.indexOf('application/json') !== -1

    if (!gotJson) {
      throw new Error('Oops, we haven\'t got JSON: ' + JSON.stringify(response))
    }
    return response.json()
  })
  .then(response => { // ensure query was accepted
    let ok = response.status === 'ok'

    if (!ok) {
      throw new Error('Received: ' + JSON.stringify(response))
    }

    // finally got token
    onSuccess(response)
  })
  .catch(error => { // all errors are handled here
    dispatch(requestTokenFailed('Something went wrong: ' + error, true))
  })
}

function fbLoginCallback (dispatch, response, callback) {
  let fbAccessToken = response.authResponse.accessToken

  if (!response.authResponse) { // ensure facebook login was successful
    dispatch(requestTokenFailed('Failed to log in to facebook: ' + JSON.stringify(response), true))
    return
  }

  fetchJwtToken(dispatch, fbAccessToken, scopes,
    response => {
      let action = receiveTokenFromServer(response.token,
        response.user.name, response.user.email)
      dispatch(action)

      // store user info in browser
      localStorage.setItem('jwToken', response.token)
      localStorage.setItem('name', response.user.name)
      localStorage.setItem('email', response.user.email)
      localStorage.setItem('mockFacebookId', null)
      localStorage.setItem('isTestUser', false)

      if (callback) {
        callback(response.token)
      }
    })
}

export const testUserLogin = (local, server, name, email, mockFacebookId, callback) => dispatch => {
    // try to get from local
  if (local) {
    dispatch(requestTokenFromLocalStorage())
    if (localStorage.jwToken && localStorage.name && localStorage.email) {
      let action = receiveTokenFromLocalStorage(localStorage.jwToken,
          localStorage.name, localStorage.email)
      dispatch(action)
      return
    }
    let action = requestTokenFailed('No login info found in local storage', false)
    dispatch(action)
  }

  if (!server) {
    return
  }
    // try to get from server
  dispatch(requestTokenFromServer())
  console.log(name, email, mockFacebookId)
  fetchTestUserJwtToken(dispatch, name, email, mockFacebookId, scopes,
      response => {
        let action = receiveTokenFromServer(response.token,
          response.user.name, response.user.email)
        dispatch(action)

        // store user info in browser
        localStorage.setItem('jwToken', response.token)
        localStorage.setItem('name', response.user.name)
        localStorage.setItem('email', response.user.email)
        localStorage.setItem('mockFacebookId', mockFacebookId)
        localStorage.setItem('isTestUser', true)

        if (callback) {
          callback(response.token)
        }
      })
}

export const login = (local, server, callback) => dispatch => {
    // try to get from local
  if (local) {
    dispatch(requestTokenFromLocalStorage())
    if (localStorage.jwToken && localStorage.name && localStorage.email) {
      let action = receiveTokenFromLocalStorage(localStorage.jwToken,
        localStorage.name, localStorage.email)
      dispatch(action)
      if (callback) {
        callback()
      }
      return
    }
    dispatch(requestTokenFailed('No login info found in local storage', false))
  }

  if (!server) {
    return
  }

  // try to get from server
  dispatch(requestTokenFromServer())
  // log in with facebook
  console.info('Begin facebook login')
  window.FB.login(response => {
    fbLoginCallback(dispatch, response, callback)
    console.info('Successfully logged in to facebook', response)
  }, {
    scope: 'public_profile,email'
  })
}

/*
  Uses callback instead of .then because
  I could not figure out how to
  pass both body and headers otherwise.
*/
export const myFetch = dispatch => (jwToken, attempts = 0) => (what, props, callback) => {
  var headers = props.headers ? props.headers : new Headers()
  headers.append('Authorization', 'Bearer ' + jwToken)
  headers.append('pragma', 'no-cache')
  headers.append('cache-control', 'no-store')

  const properties = {
    headers: headers,
    ...props
  }

  return fetch(url + what, properties)
    .then(response => {
      // ensure its json
      let contentType = response.headers.get('content-type')
      let gotJson = contentType && contentType.indexOf('application/json') !== -1
      if (!gotJson) {
        throw new Error('Oops, we haven\'t got JSON: ' + JSON.stringify(response))
      }
      return response.json().then(json => {
        // check if token has expired
        if (json.status === 'error' && json.message === 'Expired token') {
          // try to get new token and then repeat fetch
          if (attempts < 2) {
            let callback = (newToken) =>
              myFetch(newToken, dispatch, attempts + 1)(what, props, callback)

            if (localStorage.isTestUser === true && localStorage.jwToken &&
              localStorage.name && localStorage.email) {
              dispatch(testUserLogin(false, true, localStorage.name,
                localStorage.email, localStorage.name, callback))
            } else {
              dispatch(login(false, true, callback))
            }
            throw new Error('Token expired')
          }
          throw new Error('Token expired, no more relog attempts')
        }

        // ensure query was accepted
        if (json.status && json.status !== 'ok') {
          throw new Error('Received: ' + JSON.stringify(json))
        }
        if (callback) {
          callback(json, response.headers)
        }
      })
    })
}

export const logout = (dispatcher) => dispatch => {
  dispatch(deleteToken(dispatcher))
  localStorage.clear()

    // log out of facebook
  console.info('Begin facebook logout')
  try {
    window.FB.logout((response) => {
      console.info('Successfully logged out of facebook', response)
    })
  } catch (err) {
    console.info('There was a problem logging out of facebook', err)
  }
}
<commit_msg>Added comments<commit_after>import fetch from 'isomorphic-fetch'

// determine where to send requests
const www = window.location.href.indexOf('www.') !== -1 ? 'www.' : ''
const url = process.env.NODE_ENV === 'production'
  ? `https://${www}amodahl.no/api/public` : `http://${www}local.amodahl.no:3000`

/* The scopes to request for the token */
const scopes = [
  'user.all',
  'user.list',
  'chess-game.all',
  'chess-game.list',
  'chess-game.create',
  'chess-move.all',
  'chess-move.list',
  'chess-move.create',
  'update.all',
  'update.list'
]

export const requestTokenFromLocalStorage = () => {
  return {
    type: 'REQUEST_TOKEN_FROM_LOCAL_STORAGE'
  }
}

export const requestTokenFromServer = () => {
  return {
    type: 'REQUEST_TOKEN_FROM_SERVER'
  }
}

export const requestTokenFailed = (message, displayMessage) => {
  return {
    message: message,
    displayMessage: displayMessage,
    jwToken: null,
    type: 'REQUEST_TOKEN_FAILED'
  }
}

export const deleteToken = (dispatcher) => {
  return {
    jwToken: null,
    type: '@@' + dispatcher + '/DELETE_TOKEN'
  }
}

export const receiveTokenFromServer = (jwToken, name, email) => {
  return {
    jwToken: jwToken,
    name: name,
    email: email,
    type: 'RECEIVE_TOKEN_FROM_SERVER'
  }
}

export const receiveTokenFromLocalStorage = (jwToken, name, email) => {
  return {
    jwToken: jwToken,
    name: name,
    email: email,
    type: 'RECEIVE_TOKEN_FROM_LOCAL_STORAGE'
  }
}

/**
 * @callback onSuccess
 * @param {object} response The response body from the API
 */

/**
 * Ask the API for a token for one of the test users.
 * Valid test users are defined by the API.
 * @param dispatch The redux dispatch function
 * @param {string} name The test user name
 * @param {string} email The test user email
 * @param {string} mockFacebookId The test user (fake) facebook id
 * @param {string[]} scopes The scopes to request for the token
 * @param {onSuccess} onSuccess Callback when token is received
 */
function fetchTestUserJwtToken (dispatch, name, email, mockFacebookId, scopes, onSuccess) {
  var form = new FormData()
  form.append('requested_scopes', JSON.stringify(scopes))
  form.append('name', name)
  form.append('email', email)
  form.append('mock_facebook_id', mockFacebookId)

  fetch(url + '/token', {
    method: 'POST',
    body: form
  })
  .then(response => { // ensure its json
    let contentType = response.headers.get('content-type')
    let gotJson = contentType && contentType.indexOf('application/json') !== -1
    console.log(response)
    if (!gotJson) {
      throw new Error('Oops, we haven\'t got JSON: ' + JSON.stringify(response))
    }
    return response.json()
  })
  .then(response => { // ensure query was accepted
    let ok = response.status === 'ok'

    if (!ok) {
      throw new Error('Received: ' + JSON.stringify(response))
    }

    // finally got token
    onSuccess(response)
  })
  .catch(error => { // all errors are handled here
    dispatch(requestTokenFailed('Something went wrong: ' + error, true))
  })
}

/**
 * Ask the API for a token using a facebook token.
 * The API will checks that the facebook token is valid
 * and then responds with a JSON web token.
 *
 * @param dispatch The redux dispatch function
 * @param {string} fbAccessToken A valid facebook token
 * @param {string[]} scopes The scopes to request for the token
 * @param {onSuccess} onSuccess Callback when token is received
 */
function fetchJwToken (dispatch, fbAccessToken, scopes, onSuccess) {
  var form = new FormData()
  form.append('fb_access_token', fbAccessToken)
  form.append('requested_scopes', JSON.stringify(scopes))

  fetch(url + '/token', {
    method: 'POST',
    body: form
  })
  .then(response => { // ensure its json
    let contentType = response.headers.get('content-type')
    let gotJson = contentType && contentType.indexOf('application/json') !== -1

    if (!gotJson) {
      throw new Error('Oops, we haven\'t got JSON: ' + JSON.stringify(response))
    }
    return response.json()
  })
  .then(response => { // ensure query was accepted
    let ok = response.status === 'ok'

    if (!ok) {
      throw new Error('Received: ' + JSON.stringify(response))
    }

    // finally got token
    onSuccess(response)
  })
  .catch(error => { // all errors are handled here
    dispatch(requestTokenFailed('Something went wrong: ' + error, true))
  })
}

/**
 * @callback tokenCallback
 * @param {string} token A JSON web token
 */

/**
 * Is ment to be called after logging in to facebook.
 * Will request a JSON web token from amodahl.no API, using the
 * facebook token as authorization
 *
 * @param dispatch The redux dispatch function
 * @param {Object} fbResponse The login response from the facebook API
 * @param {tokenCallback} tokenCallback Called when JSON web token is received from amodahl.no API
 */
function fbLoginCallback (dispatch, fbResponse, tokenCallback) {
  let fbAccessToken = fbResponse.authResponse.accessToken

  if (!fbResponse.authResponse) { // ensure facebook login was successful
    dispatch(requestTokenFailed('Failed to log in to facebook: ' + JSON.stringify(response), true))
    return
  }

  // fetch token from amodahl.no using facebook token
  fetchJwToken(dispatch, fbAccessToken, scopes,
    response => {
      // store user info in redux store
      dispatch(receiveTokenFromServer(fbResponse.token,
        fbResponse.user.name, fbResponse.user.email))

      // store user info in browser (for next time app is started)
      localStorage.setItem('jwToken', fbResponse.token)
      localStorage.setItem('name', fbResponse.user.name)
      localStorage.setItem('email', fbResponse.user.email)
      localStorage.setItem('mockFacebookId', null)
      localStorage.setItem('isTestUser', false)

      if (tokenCallback) {
        tokenCallback(fbResponse.token)
      }
    })
}

/**
 * @callback tokenCallback
 * @param {string} token A JSON web token
 */

// TODO refactor, this method is confusing
/**
 *
 * Try to log in as a test user, logging in means just getting a token, name and email.
 * Later, if the token is invalid (it often is when its from local storage)
 * this method can be called again with the server parameter true to get a new token
 * from the server.
 * @param {boolean} local Whether to try to get login info from local storage
 * @param {boolean} server Whether to try to get login info from server
 * @param {string} name Test user name, only used getting login info from server
 * @param {string} email Test user email, only used getting login info from server
 * @param {string} mockFacebookId Test user mockFacebookId, only used getting login info from server
 * @param {tokenCallback} tokenCallback Called when JSON web token is received from amodahl.no API
 */
export const testUserLogin = (local, server, name, email, mockFacebookId, tokenCallback) => dispatch => {
    // try to get info from local
  if (local) {
    dispatch(requestTokenFromLocalStorage())
    if (localStorage.jwToken && localStorage.name && localStorage.email) {
      let action = receiveTokenFromLocalStorage(localStorage.jwToken,
          localStorage.name, localStorage.email)
      dispatch(action)
      return
    }
    let action = requestTokenFailed('No login info found in local storage', false)
    dispatch(action)
  }

  if (!server) {
    return
  }
    // try to get info from server
  dispatch(requestTokenFromServer())
  console.log(name, email, mockFacebookId)
  fetchTestUserJwtToken(dispatch, name, email, mockFacebookId, scopes,
      response => {
        let action = receiveTokenFromServer(response.token,
          response.user.name, response.user.email)
        dispatch(action)

        // store user info in browser
        localStorage.setItem('jwToken', response.token)
        localStorage.setItem('name', response.user.name)
        localStorage.setItem('email', response.user.email)
        localStorage.setItem('mockFacebookId', mockFacebookId)
        localStorage.setItem('isTestUser', true)

        if (tokenCallback) {
          tokenCallback(response.token)
        }
      })
}

/**
 *
 * Try to log in as a test user, logging in means just getting a token, name and email.
 * Later, if the token is invalid (it often is when its from local storage)
 * this method can be called again with the server parameter true to get a new token
 * from the server.
 * @param {boolean} local Whether to try to get login info from local storage
 * @param {boolean} server Whether to try to get login info from server
 * @param {tokenCallback} tokenCallback Called when JSON web token is received from amodahl.no API
 */
export const login = (local, server, callback) => dispatch => {
    // try to get from local
  if (local) {
    dispatch(requestTokenFromLocalStorage())
    if (localStorage.jwToken && localStorage.name && localStorage.email) {
      let action = receiveTokenFromLocalStorage(localStorage.jwToken,
        localStorage.name, localStorage.email)
      dispatch(action)
      if (callback) {
        callback()
      }
      return
    }
    dispatch(requestTokenFailed('No login info found in local storage', false))
  }

  if (!server) {
    return
  }

  // try to get from server
  dispatch(requestTokenFromServer())
  // log in with facebook
  console.info('Begin facebook login')
  window.FB.login(fbResponse => {
    fbLoginCallback(dispatch, fbResponse, callback)
    console.info('Successfully logged in to facebook', response)
  }, {
    scope: 'public_profile,email'
  })
}

/**
 * @callback myFetchCallback
 * @param {Object} body The response body
 * @param {Object} header The response header
 */

/**
 * Method for fetching resources from amodahl.no API.
 * If request fails because of expired token a login action is dispatched and
 * the fetch request is repeated after successfull login.
 * @param dispatch The redux dispatch function
 * @param {string} jwToken a token for amodahl.no API
 * @param {number} attempts Number fetch attempts for 'what'
 * @param {string} what The url describing what to fetch
 * @param {Object} what Additional fetch properties
 * @param {myFetchCallback} what Called after successfully fetching resource
 *
 * Uses callback instead of .then because
 * I could not figure out how to
 * pass both body and headers otherwise.
 */
export const myFetch = dispatch => (jwToken, attempts = 0) => (what, props, myFetchCallback) => {
  var headers = props.headers ? props.headers : new Headers()
  headers.append('Authorization', 'Bearer ' + jwToken)
  headers.append('pragma', 'no-cache')
  headers.append('cache-control', 'no-store')

  const properties = {
    headers: headers,
    ...props
  }

  return fetch(url + what, properties)
    .then(response => {
      // ensure its json
      let contentType = response.headers.get('content-type')
      let gotJson = contentType && contentType.indexOf('application/json') !== -1
      if (!gotJson) {
        throw new Error('Oops, we haven\'t got JSON: ' + JSON.stringify(response))
      }
      return response.json().then(json => {
        // check if token has expired
        if (json.status === 'error' && json.message === 'Expired token') {
          // try to get new token and then repeat fetch
          if (attempts < 2) {
            let callback = (newToken) =>
              myFetch(newToken, dispatch, attempts + 1)(what, props, callback)

            if (localStorage.isTestUser === true && localStorage.jwToken &&
              localStorage.name && localStorage.email) {
              dispatch(testUserLogin(false, true, localStorage.name,
                localStorage.email, localStorage.name, callback))
            } else {
              dispatch(login(false, true, callback))
            }
            throw new Error('Token expired')
          }
          throw new Error('Token expired, no more relog attempts')
        }

        // ensure query was accepted
        if (json.status && json.status !== 'ok') {
          throw new Error('Received: ' + JSON.stringify(json))
        }
        if (myFetchCallback) {
          myFetchCallback(json, response.headers)
        }
      })
    })
}

/**
 * Clears local storage and dispatches an action to tell
 * other components that user has logged out.
 * @param {string} dispatcher Identifies from which page user clicked log out
 */
export const logout = (dispatcher) => dispatch => {
  dispatch(deleteToken(dispatcher))
  localStorage.clear()

    // log out of facebook
  console.info('Begin facebook logout')
  try {
    window.FB.logout((response) => {
      console.info('Successfully logged out of facebook', response)
    })
  } catch (err) {
    console.info('There was a problem logging out of facebook', err)
  }
}
<|endoftext|>


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

    var
    main = smiliesConfig.main,
    smilies = smiliesConfig.smilies,
    shorts = smiliesConfig.shorts,

    regex = new RegExp(':(' + smilies.join('|') + '):', 'g'),
    template = '',

    escapeRegExp = function(str) {
        return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
    },

    regExpForShort = function(str) {
        if (/^[a-z]+$/i.test(str)) { // use word boundaries if emoji is letters only
            return new RegExp('\\b'+ str +'\\b', 'gi');
        }
        else {
            return new RegExp(escapeRegExp(str), 'gi');
        }
    },

    templateForSmiley = function(str) {
        return template.replace('$1', str);
    },

    apply = function(input) {
        if (!input) return '';

        var output = input.replace(regex, template);

        for (var sm in shorts) {
            if (shorts.hasOwnProperty(sm)) {
                output = output.replace(regExpForShort(sm), templateForSmiley(shorts[sm]));
            }
        }

        return output;
    };

    angular.module('angular-smilies', [])
    /* smilies parser filter */
    .filter('smilies', function() {
        return apply;
    })
    /* smilies parser attribute */
    .directive('smilies', function() {
        return {
            restrict: 'A',
            scope: {
                source: '=smilies'
            },
            link: function($scope, el, attrs) {
                el.html(apply($scope.source));
            }
        };
    })
    /* smilies selector directive */
    .directive('smiliesSelector', ['$timeout', function($timeout) {
        var templateUrl;
        try {
            angular.module('ui.bootstrap.popover');
            templateUrl = 'template/smilies/button-a.html';
        }
        catch(e) {
            try {
                angular.module('mgcrea.ngStrap.popover');
                templateUrl = 'template/smilies/button-b.html';
            }
            catch(e) {
                console.error('No Popover module found');
                return {};
            }
        }

        return {
            restrict: 'A',
            templateUrl: templateUrl,
            scope: {
                source: '=smiliesSelector',
                placement: '@smiliesPlacement',
                title: '@smiliesTitle'
            },
            link: function($scope, el) {
                $scope.smilies = smilies;

                $scope.append = function(smiley) {
                    $scope.source+= ' :'+smiley+': ';

                    $timeout(function() {
                        el.children('i').triggerHandler('click'); // close the popover
                    });
                };
            }
        };

    }])
    /* helper directive for input focusing */
    .directive('focusOnChange', function() {
        return {
            restrict: 'A',
            link: function($scope, el, attrs) {
                $scope.$watch(attrs.focusOnChange, function() {
                    el[0].focus();
                });
            }
        };
    })
    /* popover template */
    .run(['$templateCache', function($templateCache) {
        // use ng-init because popover-template only accept a variable
        $templateCache.put('template/smilies/button-a.html',
            ''
        );
        $templateCache.put('template/smilies/button-b.html',
            ''
        );
        $templateCache.put('template/smilies/popover-a.html',
            '
'+
              ''+
            '
'
        );
        $templateCache.put('template/smilies/popover-b.html',
            '
'+
                '

'+
                '

'+
                '
'+
                    $templateCache.get('template/smilies/popover-a.html') +
                '
'+
            '
'
        );
    }]);

}());
<commit_msg>Update angular-smilies.js<commit_after>(function(){
    'use strict';

    var
    main = smiliesConfig.main,
    smilies = smiliesConfig.smilies,
    shorts = smiliesConfig.shorts,

    regex = new RegExp(':(' + smilies.join('|') + '):', 'g'),
    template = '',

    escapeRegExp = function(str) {
        return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
    },

    regExpForShort = function(str) {
        if (/^[a-z]+$/i.test(str)) { // use word boundaries if emoji is letters only
            return new RegExp('\\b'+ str +'\\b', 'gi');
        }
        else {
            return new RegExp(escapeRegExp(str), 'gi');
        }
    },

    templateForSmiley = function(str) {
        return template.replace('$1', str);
    },

    apply = function(input) {
        if (!input) return '';

        var output = input.replace(regex, template);

        for (var sm in shorts) {
            if (shorts.hasOwnProperty(sm)) {
                output = output.replace(regExpForShort(sm), templateForSmiley(shorts[sm]));
            }
        }

        return output;
    };

    angular.module('angular-smilies', [])
    /* smilies parser filter */
    .filter('smilies', function() {
        return apply;
    })
    /* smilies parser attribute */
    .directive('smilies', function() {
        return {
            restrict: 'A',
            scope: {
                source: '=smilies'
            },
            link: function($scope, el, attrs) {
                el.html(apply($scope.source));
            }
        };
    })
    /* smilies selector directive */
    .directive('smiliesSelector', ['$timeout', function($timeout) {
        var templateUrl;
        try {
            angular.module('ui.bootstrap.popover');
            templateUrl = 'template/smilies/button-a.html';
        }
        catch(e) {
            try {
                angular.module('mgcrea.ngStrap.popover');
                templateUrl = 'template/smilies/button-b.html';
            }
            catch(e) {
                console.error('No Popover module found');
                return {};
            }
        }

        return {
            restrict: 'A',
            templateUrl: templateUrl,
            scope: {
                source: '=smiliesSelector',
                placement: '@smiliesPlacement',
                title: '@smiliesTitle'
            },
            link: function($scope, el) {
                $scope.smilies = smilies;

                $scope.append = function(smiley) {
                    $scope.source+= ' :'+smiley+': ';

                    $timeout(function() {
                        el.children('i').triggerHandler('click'); // close the popover
                    });
                };
            }
        };

    }])
    /* helper directive for input focusing */
    .directive('focusOnChange', function() {
        return {
            restrict: 'A',
            link: function($scope, el, attrs) {
                $scope.$watch(attrs.focusOnChange, function() {
                    el[0].focus();
                });
            }
        };
    })
    /* popover template */
    .run(['$templateCache', function($templateCache) {
        // use ng-init because popover-template only accept a variable
        $templateCache.put('template/smilies/button-a.html',
            ''
        );
        $templateCache.put('template/smilies/button-b.html',
            ''
        );
        $templateCache.put('template/smilies/popover-a.html',
            '
'+
              ''+
            '
'
        );
        $templateCache.put('template/smilies/popover-b.html',
            '
'+
                '

'+
                '

'+
                '
'+
                    $templateCache.get('template/smilies/popover-a.html') +
                '
'+
            '
'
        );
    }]);

}());
<|endoftext|>


<commit_before>angular.module('app.about', [
  'ui.router'
]);

angular.module('app.about').config(function config($stateProvider) {
  $stateProvider.state('about', {
    url: '^/about',
    views: {
      "main": {
        templateUrl: 'views/about/about.tpl.html',
        controller: 'AboutCtrl'
      }
    },
    data: {
      meta: {
        title: 'Angularplate - About',
        description: 'An Angular template/boilerplate implementing best practices and modern technologies',
        keywords: 'Angularplate, About, AngularJS, Boilerplate, Best-practice',
        canonical: 'About',
        url: 'about/'
      }
    }
  });
});
<commit_msg>fix(about): Change template url to be based off root directory<commit_after>angular.module('app.about', [
  'ui.router'
]);

angular.module('app.about').config(function config($stateProvider) {
  $stateProvider.state('about', {
    url: '^/about',
    views: {
      "main": {
        templateUrl: '/views/about/about.tpl.html',
        controller: 'AboutCtrl'
      }
    },
    data: {
      meta: {
        title: 'Angularplate - About',
        description: 'An Angular template/boilerplate implementing best practices and modern technologies',
        keywords: 'Angularplate, About, AngularJS, Boilerplate, Best-practice',
        canonical: 'About',
        url: 'about/'
      }
    }
  });
});
<|endoftext|>


<commit_before>/**
 @ngdoc overview
 @name  octoboard.login

 @description
 stuff about login
 */

// Add our module to the namespace
angular.module( 'GitKan.login', [
  'ui.state'
])

// Add our routes so that we can get into the section of the app
.config(function config( $stateProvider ) {
  $stateProvider.state( 'login', {
     url: '/login',
     views: {
        body: {
           controller:  'LoginCtrl',
           templateUrl: 'login/login.tpl.html'
        }
     }
  });
})

.controller( 'LoginCtrl', function HomeController($scope, $http, $location, gh) {
   // Always start with a blonk username/password and remember turned off
   $scope.username   = '';
   $scope.passsword  = '';
   $scope.rememberMe = false;

   $scope.attemptLogin = function() {
      var user = $scope.username, pass = $scope.password;

      // Make sure the user entered something
      if (!user || !pass) {
         console.log('Dude you must enter something');
         // XXX look at basic field validation
         return;
      }

      // Try and login into GitHub.
      var auth = user + ':' + pass;
      var p = $http({
         method: 'GET',
         url:    'https://api.github.com/authorizations',
         headers: {
            Authorization: 'Basic ' + window.btoa(auth)
         }
      });

      // If these are valid creds then create a token we can use from now.
      // - Be smart and tag it so that we can use the same one latter for our app
      p.success(function(auths) {
         // See if any of the authorizations are for our app
         var auth = _.find(auths, function(auth) {
            return auth.note == "gitkan";
         });

         // If none of the authorizations are for gitkan then add one for the user
         // XXX

         // Cache the token so that we can use it later
         if (auth) {
            // Pass the token off to the `gh` service
            // - Tell the github service how long to hold the authentication
            gh.setAccessToken(auth.token,
                              $scope.rememberMe ? 'local' : 'session');

            // Yeah, for successful auth so bounce the user to next page
            // - If the next page was supplied then use that otherwise
            //   bounce the user to the repo list by default
            $location.path('/repos');
         }
      });

      // XXX handle bad cred case (some kind of error screen

   };
})

;
<commit_msg>Redirect to /board after login.<commit_after>/**
 @ngdoc overview
 @name  octoboard.login

 @description
 stuff about login
 */

// Add our module to the namespace
angular.module( 'GitKan.login', [
  'ui.state'
])

// Add our routes so that we can get into the section of the app
.config(function config( $stateProvider ) {
  $stateProvider.state( 'login', {
     url: '/login',
     views: {
        body: {
           controller:  'LoginCtrl',
           templateUrl: 'login/login.tpl.html'
        }
     }
  });
})

.controller( 'LoginCtrl', function HomeController($scope, $http, $location, gh) {
   // Always start with a blonk username/password and remember turned off
   $scope.username   = '';
   $scope.passsword  = '';
   $scope.rememberMe = false;

   $scope.attemptLogin = function() {
      var user = $scope.username, pass = $scope.password;

      // Make sure the user entered something
      if (!user || !pass) {
         console.log('Dude you must enter something');
         // XXX look at basic field validation
         return;
      }

      // Try and login into GitHub.
      var auth = user + ':' + pass;
      var p = $http({
         method: 'GET',
         url:    'https://api.github.com/authorizations',
         headers: {
            Authorization: 'Basic ' + window.btoa(auth)
         }
      });

      // If these are valid creds then create a token we can use from now.
      // - Be smart and tag it so that we can use the same one latter for our app
      p.success(function(auths) {
         // See if any of the authorizations are for our app
         var auth = _.find(auths, function(auth) {
            return auth.note == "gitkan";
         });

         // If none of the authorizations are for gitkan then add one for the user
         // XXX

         // Cache the token so that we can use it later
         if (auth) {
            // Pass the token off to the `gh` service
            // - Tell the github service how long to hold the authentication
            gh.setAccessToken(auth.token,
                              $scope.rememberMe ? 'local' : 'session');

            // Yeah, for successful auth so bounce the user to next page
            // - If the next page was supplied then use that otherwise
            //   bounce the user to the repo list by default
            $location.path('/board');
         }
      });

      // XXX handle bad cred case (some kind of error screen

   };
})

;
<|endoftext|>


<commit_before>"use strict";

var CityTour = CityTour || {};

/*
  Converts an X/Y screen pixel coordinate to a 3D point in the world, based on the current camera position.
  The calculated point takes the terrain into account. That is, you can use this to calculate where on the
  terrain the mouse pointer/finger is touching.
*/
CityTour.WorldTouch = function(el, camera, screenPixelX, screenPixelY, terrain) {
  // Adapted from https://stackoverflow.com/questions/13055214/mouse-canvas-x-y-to-three-js-world-x-y-z#13091694
  var screenCoordinateToWorldPosition = function(camera) {
    var MAX_ITERATIONS = 1000;

    var direction, worldPosition;
    var ray, movementTowardXZPlaneAmount;
    var mapX, mapZ;
    var loopCount;

    normalizedScreenVector.unproject(camera);
    direction = normalizedScreenVector.sub(camera.position).normalize();
    ray = camera.position.clone();
    movementTowardXZPlaneAmount = direction.clone().multiplyScalar(0.3333333333333333);

    loopCount = 0;
    while (ray.y > 0.0 && worldPosition === undefined && loopCount < MAX_ITERATIONS) {
      ray = ray.add(movementTowardXZPlaneAmount);

      if (ray.x >= terrain.minX() &&
          ray.x <= terrain.maxX() &&
          ray.z >= terrain.minZ() &&
          ray.z <= terrain.maxZ()) {
        if (ray.y <= terrain.heightAtCoordinates(ray.x, ray.z)) {
          worldPosition = ray;
        }
      }

      loopCount += 1;
    }

    // Ray doesn't intersect the terrain
    if (worldPosition === undefined) {
      worldPosition = ray;
    }

    return worldPosition;
  };

  var normalizedScreenVector = new THREE.Vector3(((screenPixelX / (el.width / window.devicePixelRatio)) * 2) - 1,
                                                 (-(screenPixelY / (el.height / window.devicePixelRatio)) * 2) + 1,
                                                 0.5);
  var normalizedScreenX = normalizedScreenVector.x;
  var normalizedScreenY = normalizedScreenVector.y;

  var worldPosition = screenCoordinateToWorldPosition(camera);
  var worldX = worldPosition.x;
  var worldY = worldPosition.y;
  var worldZ = worldPosition.z;

  return {
    screenPixelX: function() { return screenPixelX; },
    screenPixelY: function() { return screenPixelY; },
    normalizedScreenX: function() { return normalizedScreenX; },
    normalizedScreenY: function() { return normalizedScreenY; },
    worldX: function() { return worldX; },
    worldY: function() { return worldY; },
    worldZ: function() { return worldZ; },
  };
};
<commit_msg>Removing unused variables<commit_after>"use strict";

var CityTour = CityTour || {};

/*
  Converts an X/Y screen pixel coordinate to a 3D point in the world, based on the current camera position.
  The calculated point takes the terrain into account. That is, you can use this to calculate where on the
  terrain the mouse pointer/finger is touching.
*/
CityTour.WorldTouch = function(el, camera, screenPixelX, screenPixelY, terrain) {
  // Adapted from https://stackoverflow.com/questions/13055214/mouse-canvas-x-y-to-three-js-world-x-y-z#13091694
  var screenCoordinateToWorldPosition = function(camera) {
    var MAX_ITERATIONS = 1000;

    var direction, worldPosition;
    var ray, movementTowardXZPlaneAmount;
    var loopCount;

    normalizedScreenVector.unproject(camera);
    direction = normalizedScreenVector.sub(camera.position).normalize();
    ray = camera.position.clone();
    movementTowardXZPlaneAmount = direction.clone().multiplyScalar(0.3333333333333333);

    loopCount = 0;
    while (ray.y > 0.0 && worldPosition === undefined && loopCount < MAX_ITERATIONS) {
      ray = ray.add(movementTowardXZPlaneAmount);

      if (ray.x >= terrain.minX() &&
          ray.x <= terrain.maxX() &&
          ray.z >= terrain.minZ() &&
          ray.z <= terrain.maxZ()) {
        if (ray.y <= terrain.heightAtCoordinates(ray.x, ray.z)) {
          worldPosition = ray;
        }
      }

      loopCount += 1;
    }

    // Ray doesn't intersect the terrain
    if (worldPosition === undefined) {
      worldPosition = ray;
    }

    return worldPosition;
  };

  var normalizedScreenVector = new THREE.Vector3(((screenPixelX / (el.width / window.devicePixelRatio)) * 2) - 1,
                                                 (-(screenPixelY / (el.height / window.devicePixelRatio)) * 2) + 1,
                                                 0.5);
  var normalizedScreenX = normalizedScreenVector.x;
  var normalizedScreenY = normalizedScreenVector.y;

  var worldPosition = screenCoordinateToWorldPosition(camera);
  var worldX = worldPosition.x;
  var worldY = worldPosition.y;
  var worldZ = worldPosition.z;

  return {
    screenPixelX: function() { return screenPixelX; },
    screenPixelY: function() { return screenPixelY; },
    normalizedScreenX: function() { return normalizedScreenX; },
    normalizedScreenY: function() { return normalizedScreenY; },
    worldX: function() { return worldX; },
    worldY: function() { return worldY; },
    worldZ: function() { return worldZ; },
  };
};
<|endoftext|>


<commit_before>import WrapperBuilder from '../wrapper';
import assertKeyValue from '../assert-key-value';
import should from 'should';

const Assertion = should.Assertion;

Assertion.add('prop', function(expectedKey, expectedValue){
  const wrapper = WrapperBuilder(this.obj),
  wrapperProp = wrapper.prop(expectedKey);

  if(arguments.length > 1 && typeof wrapperProp !== 'undefined') {
    this.params = {
      actual: wrapper.name(),
      operator: `prop '${expectedKey}' to have value ${should.format(expectedValue)}, instead found ${should.format(wrapperProp)}`
    };
    should(wrapperProp).be.eql(expectedValue, ' ');
  }else{
    this.params = {
      actual: wrapper.name(),
      operator: `to have prop '${expectedKey}'`
    };
    should(assertKeyValue(wrapperProp)).be.true(' ');
  }
}); <commit_msg>remove the assertion-key-value from prop assertion.<commit_after>import WrapperBuilder from '../wrapper';
import should from 'should';

const Assertion = should.Assertion;

Assertion.add('prop', function(expectedKey, expectedValue){
  const wrapper = WrapperBuilder(this.obj),
  wrapperProp = wrapper.prop(expectedKey);

  if(arguments.length > 1 && typeof wrapperProp !== 'undefined') {
    this.params = {
      actual: wrapper.name(),
      operator: `prop '${expectedKey}' to have value ${should.format(expectedValue)}, instead found ${should.format(wrapperProp)}`
    };
    should(wrapperProp).be.eql(expectedValue, ' ');
  }else{
    this.params = {
      actual: wrapper.name(),
      operator: `to have prop '${expectedKey}'`
    };
    should(wrapperProp).not.be.undefined(' ');
  }
}); <|endoftext|>


<commit_before>import Layout from "@financial-times/o-layout";
Layout.init(document.body);

import HeaderServices from "@financial-times/o-header-services";
HeaderServices.init();

// const polyfillAliases = require("polyfill-library/polyfills/__dist/aliases.json");

import Tabs from "@financial-times/o-tabs";

Tabs.init(document.body, {
	disablefocus: false
});

import Tooltip from "@financial-times/o-tooltip";
const createTooltips = () => {
	const tooltipObjects = Tooltip.init(document.body);

	tooltipObjects.forEach(tooltip => {
		tooltip.tooltipEl.addEventListener("oTooltip.show", event => {
			tooltipObjects.forEach(tooltip => {
				if (tooltip.tooltipEl !== event.target) {
					tooltip.close();
				}
			});
		});
	});
};

const copyToClipboard = str => {
	// Create a <textarea> element
	const el = document.createElement("textarea");
	// Set its value to the string that you want copied
	el.value = str;
	// Make it readonly to be tamper-proof
	el.setAttribute("readonly", "");
	el.style.position = "absolute";
	// Move outside the screen to make it invisible
	el.style.left = "-9999px";
	// Append the <textarea> element to the HTML document
	document.body.appendChild(el);
	// Mark as false to know no selection existed before
	let selected = false;
	// Check if there is any content selected previously
	if (document.getSelection().rangeCount > 0) {
		// Store selection if found
		selected = document.getSelection().getRangeAt(0);
	}
	// Select the <textarea> content
	el.select();
	// Copy - only works as a result of a user action (e.g. click events)
	document.execCommand("copy");
	// Remove the <textarea> element
	document.body.removeChild(el);
	if (selected) {
		// If a selection existed before copying
		// Unselect everything on the HTML document
		document.getSelection().removeAllRanges();
		// Restore the original selection
		document.getSelection().addRange(selected);
	}
};

const createPolyfillBundleURL = options => {
	const parameters = {};

	const gated = options.gated ? true : false;
	const always = options.always ? true : false;

	if (gated && always) {
		parameters.flags = "gated,always";
	} else if (gated) {
		parameters.flags = "gated";
	} else if (always) {
		parameters.flags = "always";
	}

	if (options.version) {
		parameters.version = options.version;
	}

	if (options.callback) {
		parameters.callback = options.callback;
	}

	if (options.rum) {
		parameters.rum = options.rum;
	}

	if (options.features.length > 0) {
		parameters.features = options.features.join(",");
	}

	const minified = options.minified ? true : false;
	const extension = minified ? ".min.js" : ".js";

	if (Object.keys(parameters).length > 0) {
		return `https://polyfill.io/v3/polyfill${extension}?${new URLSearchParams(parameters).toString()}`;
	} else {
		return `https://polyfill.io/v3/polyfill${extension}`;
	}
};

const createPolyfillBundleHTML = options => {
	return `<script crossorigin="anonymous" src="${createPolyfillBundleURL(options)}"></script>`;
};

const updatePolyfillBundle = options => {
	const bundleURLNode = document.getElementById("polyfill-bundle-url");
	const bundleHTMLNode = document.getElementById("polyfill-bundle-html");
	bundleURLNode.innerText = createPolyfillBundleURL(options);
	bundleHTMLNode.innerText = createPolyfillBundleHTML(options);
};

const defaultPolyfillBundleOptions = {
	minified: true,
	gated: false,
	always: false,
	rum: 0,
	features: [],
	callback: "",
	version: ""
};

const polyfillBundleOptions = Object.assign({}, defaultPolyfillBundleOptions);

const featuresCache = {};
const featuresRows = Array.from(document.querySelectorAll("#features-list [data-feature-name]"));

featuresRows.forEach(node => {
	featuresCache[node.dataset.featureName.toLowerCase()] = node;
});

const renderFeatureList = ({ polyfillAliases, polyfills }) => {
	let html = "";
	for (const item of polyfillAliases) {
		html += `

					<input type="checkbox" name="${item.name}">
					${item.name}

				<button class="tooltip-button o-buttons o-buttons--secondary o-buttons--mono o-buttons-icon o-buttons-icon--more o-buttons-icon--icon-only" id="${item.name}-tooltip-target">
					More about ${item.name} (opens tooltip).
				</button>

						${item.polyfills.map(polyfill => `<li>${polyfill}</li>`)}

`;
	}
	for (const item of polyfills) {
		html += `

					<input type="checkbox" name="${item.name}">
					${item.name}

				<button class="tooltip-button o-buttons o-buttons--secondary o-buttons--mono o-buttons-icon o-buttons-icon--more o-buttons-icon--icon-only" id="${item.name}-tooltip-target">
					More about ${item.name} (opens tooltip).
				</button>

							${item.aliasFor ? `<li class='alias'>Alias for <code>${item.aliasFor}</code></li>` : ""}
							${item.license ? `<li class='license'><a title='This polyfill has a specific licence' href='https://choosealicense.com/licenses/${item.licenseLowerCase}'>License: ${item.license}</a></li>` : ""}
							${item.spec ? `<li><a href='${item.spec}'>Specification</a></li>` : ""}
							${item.docs ? `<li><a href='${item.docs}'>Documentation</a></li>` : ""}
							${item.baseDir ? `<li><a href='https://github.com/Financial-Times/polyfill-library/tree/master/polyfills/${item.baseDir}'>Polyfill source</a></li>` : ""}

`;
	}
	return html;
};

const libraryVersionInput = document.getElementById("library-version");
if (libraryVersionInput) {
	libraryVersionInput.addEventListener("change", async event => {
		const version = event.target.value;
		// if version is not selected, get the data for the latest version
		const res = await fetch(`/v3/json/library-${version || event.target.options[1].value}.json`);
		if (res.ok) {
			const data = await res.json();
			featuresList.innerHTML = renderFeatureList(data);
			Array.from(featuresList.getElementsByTagName("input")).forEach(node => {
				node.addEventListener("change", updateFeaturesListFromEvent);
			});
			polyfillBundleOptions.features = [];
			polyfillBundleOptions.version = version;
			updatePolyfillBundle(polyfillBundleOptions);
			createTooltips();
		}
	});
}

const filterInput = document.getElementById("filter");
if (filterInput) {
	filterInput.addEventListener("keyup", filterFeatures);
	filterInput.addEventListener("paste", filterFeatures);

	function resetFeaturesList() {
		return Array.from(featuresRows).forEach(function(el) {
			el.removeAttribute("aria-hidden");
		});
	}

	function filterFeatures(e) {
		let inputVal = e.target.value;

		if (e.type === "paste") {
			const clipboardData = e.clipboardData || window.clipboardData;
			inputVal = clipboardData.getData("Text");
		}

		if (inputVal === "") {
			resetFeaturesList();
		} else {
			Object.entries(featuresCache).forEach(function([key, node]) {
				if (key.toLowerCase().includes(inputVal.toLowerCase())) {
					node.removeAttribute("aria-hidden");
				} else {
					node.setAttribute("aria-hidden", true);
				}
			});
		}
	}
}

const callbackInput = document.getElementById("callback");

if (callbackInput) {
	callbackInput.addEventListener("keyup", updateCallback);
	callbackInput.addEventListener("paste", updateCallback);

	function updateCallback(e) {
		let inputVal = e.target.value;

		if (e.type === "paste") {
			const clipboardData = e.clipboardData || window.clipboardData;
			inputVal = clipboardData.getData("Text");
		}

		polyfillBundleOptions.callback = inputVal;
		updatePolyfillBundle(polyfillBundleOptions);
	}
}

const minified1 = document.getElementById("minified1");
if (minified1) {
	minified1.addEventListener("change", function minifiedCallback() {
		polyfillBundleOptions.minified = true;
		updatePolyfillBundle(polyfillBundleOptions);
	});
}

const minified2 = document.getElementById("minified2");
if (minified2) {
	minified2.addEventListener("change", function() {
		polyfillBundleOptions.minified = false;
		updatePolyfillBundle(polyfillBundleOptions);
	});
}

const gated1 = document.getElementById("gated1");
if (gated1) {
	gated1.addEventListener("change", function gatedCallback() {
		polyfillBundleOptions.gated = true;
		updatePolyfillBundle(polyfillBundleOptions);
	});
}

const gated2 = document.getElementById("gated2");
if (gated2) {
	gated2.addEventListener("change", function() {
		polyfillBundleOptions.gated = false;
		updatePolyfillBundle(polyfillBundleOptions);
	});
}

const rum1 = document.getElementById("rum1");
if (rum1) {
	rum1.addEventListener("change", function rumCallback() {
		polyfillBundleOptions.rum = true;
		updatePolyfillBundle(polyfillBundleOptions);
	});
}

const rum2 = document.getElementById("rum2");
if (rum2) {
	rum2.addEventListener("change", function() {
		polyfillBundleOptions.rum = false;
		updatePolyfillBundle(polyfillBundleOptions);
	});
}

const always1 = document.getElementById("always1");
if (always1) {
	always1.addEventListener("change", function alwaysCallback() {
		polyfillBundleOptions.always = true;
		updatePolyfillBundle(polyfillBundleOptions);
	});
}

const always2 = document.getElementById("always2");
if (always2) {
	always2.addEventListener("change", function() {
		polyfillBundleOptions.always = false;
		updatePolyfillBundle(polyfillBundleOptions);
	});
}

const addFeatureToList = (list, feature) => {
	if (!list.includes(feature)) {
		list.push(feature);
	}
};

const removeFeatureToList = (list, feature) => {
	const index = list.indexOf(feature);

	if (index !== -1) {
		list.splice(index, 1);
	}
};

const updateFeaturesListFromEvent = e => {
	if (e.target.checked) {
		addFeatureToList(polyfillBundleOptions.features, e.target.name);
	} else {
		removeFeatureToList(polyfillBundleOptions.features, e.target.name);
	}
	updatePolyfillBundle(polyfillBundleOptions);
};

const featuresList = document.getElementById("features-list");
if (featuresList) {
	Array.from(featuresList.getElementsByTagName("input")).forEach(node => {
		node.addEventListener("change", updateFeaturesListFromEvent);
	});
}

const copyURLButton = document.getElementById("copy-url");
if (copyURLButton) {
	let copyURLButtonTimeout;
	const copyURLButtonOriginalText = copyURLButton.innerText;
	copyURLButton.addEventListener("click", () => {
		copyToClipboard(createPolyfillBundleURL(polyfillBundleOptions));
		copyURLButton.innerText = "Copied to clipboard";
		clearTimeout(copyURLButtonTimeout);
		copyURLButtonTimeout = setTimeout(() => {
			copyURLButton.innerHTML = copyURLButtonOriginalText;
		}, 5000);
	});
}

const copyHTMLButton = document.getElementById("copy-html");
if (copyHTMLButton) {
	let copyHTMLButtonTimeout;
	const copyHTMLButtonOriginalText = copyHTMLButton.innerText;
	copyHTMLButton.addEventListener("click", () => {
		copyToClipboard(createPolyfillBundleHTML(polyfillBundleOptions));
		copyHTMLButton.innerText = "Copied to clipboard";
		clearTimeout(copyHTMLButtonTimeout);
		copyHTMLButtonTimeout = setTimeout(() => {
			copyHTMLButton.innerHTML = copyHTMLButtonOriginalText;
		}, 5000);
	});
}
<commit_msg>make tooltips work on initial load<commit_after>import Layout from "@financial-times/o-layout";
Layout.init(document.body);

import HeaderServices from "@financial-times/o-header-services";
HeaderServices.init();

// const polyfillAliases = require("polyfill-library/polyfills/__dist/aliases.json");

import Tabs from "@financial-times/o-tabs";

Tabs.init(document.body, {
	disablefocus: false
});

import Tooltip from "@financial-times/o-tooltip";
const createTooltips = () => {
	const tooltipObjects = Tooltip.init(document.body);

	tooltipObjects.forEach(tooltip => {
		tooltip.tooltipEl.addEventListener("oTooltip.show", event => {
			tooltipObjects.forEach(tooltip => {
				if (tooltip.tooltipEl !== event.target) {
					tooltip.close();
				}
			});
		});
	});
};

createTooltips();

const copyToClipboard = str => {
	// Create a <textarea> element
	const el = document.createElement("textarea");
	// Set its value to the string that you want copied
	el.value = str;
	// Make it readonly to be tamper-proof
	el.setAttribute("readonly", "");
	el.style.position = "absolute";
	// Move outside the screen to make it invisible
	el.style.left = "-9999px";
	// Append the <textarea> element to the HTML document
	document.body.appendChild(el);
	// Mark as false to know no selection existed before
	let selected = false;
	// Check if there is any content selected previously
	if (document.getSelection().rangeCount > 0) {
		// Store selection if found
		selected = document.getSelection().getRangeAt(0);
	}
	// Select the <textarea> content
	el.select();
	// Copy - only works as a result of a user action (e.g. click events)
	document.execCommand("copy");
	// Remove the <textarea> element
	document.body.removeChild(el);
	if (selected) {
		// If a selection existed before copying
		// Unselect everything on the HTML document
		document.getSelection().removeAllRanges();
		// Restore the original selection
		document.getSelection().addRange(selected);
	}
};

const createPolyfillBundleURL = options => {
	const parameters = {};

	const gated = options.gated ? true : false;
	const always = options.always ? true : false;

	if (gated && always) {
		parameters.flags = "gated,always";
	} else if (gated) {
		parameters.flags = "gated";
	} else if (always) {
		parameters.flags = "always";
	}

	if (options.version) {
		parameters.version = options.version;
	}

	if (options.callback) {
		parameters.callback = options.callback;
	}

	if (options.rum) {
		parameters.rum = options.rum;
	}

	if (options.features.length > 0) {
		parameters.features = options.features.join(",");
	}

	const minified = options.minified ? true : false;
	const extension = minified ? ".min.js" : ".js";

	if (Object.keys(parameters).length > 0) {
		return `https://polyfill.io/v3/polyfill${extension}?${new URLSearchParams(parameters).toString()}`;
	} else {
		return `https://polyfill.io/v3/polyfill${extension}`;
	}
};

const createPolyfillBundleHTML = options => {
	return `<script crossorigin="anonymous" src="${createPolyfillBundleURL(options)}"></script>`;
};

const updatePolyfillBundle = options => {
	const bundleURLNode = document.getElementById("polyfill-bundle-url");
	const bundleHTMLNode = document.getElementById("polyfill-bundle-html");
	bundleURLNode.innerText = createPolyfillBundleURL(options);
	bundleHTMLNode.innerText = createPolyfillBundleHTML(options);
};

const defaultPolyfillBundleOptions = {
	minified: true,
	gated: false,
	always: false,
	rum: 0,
	features: [],
	callback: "",
	version: ""
};

const polyfillBundleOptions = Object.assign({}, defaultPolyfillBundleOptions);

const featuresCache = {};
const featuresRows = Array.from(document.querySelectorAll("#features-list [data-feature-name]"));

featuresRows.forEach(node => {
	featuresCache[node.dataset.featureName.toLowerCase()] = node;
});

const renderFeatureList = ({ polyfillAliases, polyfills }) => {
	let html = "";
	for (const item of polyfillAliases) {
		html += `

					<input type="checkbox" name="${item.name}">
					${item.name}

				<button class="tooltip-button o-buttons o-buttons--secondary o-buttons--mono o-buttons-icon o-buttons-icon--more o-buttons-icon--icon-only" id="${item.name}-tooltip-target">
					More about ${item.name} (opens tooltip).
				</button>

						${item.polyfills.map(polyfill => `<li>${polyfill}</li>`)}

`;
	}
	for (const item of polyfills) {
		html += `

					<input type="checkbox" name="${item.name}">
					${item.name}

				<button class="tooltip-button o-buttons o-buttons--secondary o-buttons--mono o-buttons-icon o-buttons-icon--more o-buttons-icon--icon-only" id="${item.name}-tooltip-target">
					More about ${item.name} (opens tooltip).
				</button>

							${item.aliasFor ? `<li class='alias'>Alias for <code>${item.aliasFor}</code></li>` : ""}
							${item.license ? `<li class='license'><a title='This polyfill has a specific licence' href='https://choosealicense.com/licenses/${item.licenseLowerCase}'>License: ${item.license}</a></li>` : ""}
							${item.spec ? `<li><a href='${item.spec}'>Specification</a></li>` : ""}
							${item.docs ? `<li><a href='${item.docs}'>Documentation</a></li>` : ""}
							${item.baseDir ? `<li><a href='https://github.com/Financial-Times/polyfill-library/tree/master/polyfills/${item.baseDir}'>Polyfill source</a></li>` : ""}

`;
	}
	return html;
};

const libraryVersionInput = document.getElementById("library-version");
if (libraryVersionInput) {
	libraryVersionInput.addEventListener("change", async event => {
		const version = event.target.value;
		// if version is not selected, get the data for the latest version
		const res = await fetch(`/v3/json/library-${version || event.target.options[1].value}.json`);
		if (res.ok) {
			const data = await res.json();
			featuresList.innerHTML = renderFeatureList(data);
			Array.from(featuresList.getElementsByTagName("input")).forEach(node => {
				node.addEventListener("change", updateFeaturesListFromEvent);
			});
			polyfillBundleOptions.features = [];
			polyfillBundleOptions.version = version;
			updatePolyfillBundle(polyfillBundleOptions);
			createTooltips();
		}
	});
}

const filterInput = document.getElementById("filter");
if (filterInput) {
	filterInput.addEventListener("keyup", filterFeatures);
	filterInput.addEventListener("paste", filterFeatures);

	function resetFeaturesList() {
		return Array.from(featuresRows).forEach(function(el) {
			el.removeAttribute("aria-hidden");
		});
	}

	function filterFeatures(e) {
		let inputVal = e.target.value;

		if (e.type === "paste") {
			const clipboardData = e.clipboardData || window.clipboardData;
			inputVal = clipboardData.getData("Text");
		}

		if (inputVal === "") {
			resetFeaturesList();
		} else {
			Object.entries(featuresCache).forEach(function([key, node]) {
				if (key.toLowerCase().includes(inputVal.toLowerCase())) {
					node.removeAttribute("aria-hidden");
				} else {
					node.setAttribute("aria-hidden", true);
				}
			});
		}
	}
}

const callbackInput = document.getElementById("callback");

if (callbackInput) {
	callbackInput.addEventListener("keyup", updateCallback);
	callbackInput.addEventListener("paste", updateCallback);

	function updateCallback(e) {
		let inputVal = e.target.value;

		if (e.type === "paste") {
			const clipboardData = e.clipboardData || window.clipboardData;
			inputVal = clipboardData.getData("Text");
		}

		polyfillBundleOptions.callback = inputVal;
		updatePolyfillBundle(polyfillBundleOptions);
	}
}

const minified1 = document.getElementById("minified1");
if (minified1) {
	minified1.addEventListener("change", function minifiedCallback() {
		polyfillBundleOptions.minified = true;
		updatePolyfillBundle(polyfillBundleOptions);
	});
}

const minified2 = document.getElementById("minified2");
if (minified2) {
	minified2.addEventListener("change", function() {
		polyfillBundleOptions.minified = false;
		updatePolyfillBundle(polyfillBundleOptions);
	});
}

const gated1 = document.getElementById("gated1");
if (gated1) {
	gated1.addEventListener("change", function gatedCallback() {
		polyfillBundleOptions.gated = true;
		updatePolyfillBundle(polyfillBundleOptions);
	});
}

const gated2 = document.getElementById("gated2");
if (gated2) {
	gated2.addEventListener("change", function() {
		polyfillBundleOptions.gated = false;
		updatePolyfillBundle(polyfillBundleOptions);
	});
}

const rum1 = document.getElementById("rum1");
if (rum1) {
	rum1.addEventListener("change", function rumCallback() {
		polyfillBundleOptions.rum = true;
		updatePolyfillBundle(polyfillBundleOptions);
	});
}

const rum2 = document.getElementById("rum2");
if (rum2) {
	rum2.addEventListener("change", function() {
		polyfillBundleOptions.rum = false;
		updatePolyfillBundle(polyfillBundleOptions);
	});
}

const always1 = document.getElementById("always1");
if (always1) {
	always1.addEventListener("change", function alwaysCallback() {
		polyfillBundleOptions.always = true;
		updatePolyfillBundle(polyfillBundleOptions);
	});
}

const always2 = document.getElementById("always2");
if (always2) {
	always2.addEventListener("change", function() {
		polyfillBundleOptions.always = false;
		updatePolyfillBundle(polyfillBundleOptions);
	});
}

const addFeatureToList = (list, feature) => {
	if (!list.includes(feature)) {
		list.push(feature);
	}
};

const removeFeatureToList = (list, feature) => {
	const index = list.indexOf(feature);

	if (index !== -1) {
		list.splice(index, 1);
	}
};

const updateFeaturesListFromEvent = e => {
	if (e.target.checked) {
		addFeatureToList(polyfillBundleOptions.features, e.target.name);
	} else {
		removeFeatureToList(polyfillBundleOptions.features, e.target.name);
	}
	updatePolyfillBundle(polyfillBundleOptions);
};

const featuresList = document.getElementById("features-list");
if (featuresList) {
	Array.from(featuresList.getElementsByTagName("input")).forEach(node => {
		node.addEventListener("change", updateFeaturesListFromEvent);
	});
}

const copyURLButton = document.getElementById("copy-url");
if (copyURLButton) {
	let copyURLButtonTimeout;
	const copyURLButtonOriginalText = copyURLButton.innerText;
	copyURLButton.addEventListener("click", () => {
		copyToClipboard(createPolyfillBundleURL(polyfillBundleOptions));
		copyURLButton.innerText = "Copied to clipboard";
		clearTimeout(copyURLButtonTimeout);
		copyURLButtonTimeout = setTimeout(() => {
			copyURLButton.innerHTML = copyURLButtonOriginalText;
		}, 5000);
	});
}

const copyHTMLButton = document.getElementById("copy-html");
if (copyHTMLButton) {
	let copyHTMLButtonTimeout;
	const copyHTMLButtonOriginalText = copyHTMLButton.innerText;
	copyHTMLButton.addEventListener("click", () => {
		copyToClipboard(createPolyfillBundleHTML(polyfillBundleOptions));
		copyHTMLButton.innerText = "Copied to clipboard";
		clearTimeout(copyHTMLButtonTimeout);
		copyHTMLButtonTimeout = setTimeout(() => {
			copyHTMLButton.innerHTML = copyHTMLButtonOriginalText;
		}, 5000);
	});
}
<|endoftext|>


<commit_before>var messenger = require('./common/messenger');

var recordingEnabled = false,
  currentWindowId,
  currentTabId,
  uiWindow,
  recordingContextMenuItemId,
  api = {
    isRecordingEnabled: function (request, callback) {
      callback(recordingEnabled);
    },
    toggleRecording: function () {
      recordingEnabled = !recordingEnabled;
      chrome.browserAction.setIcon({ path: recordingEnabled ? "assets/icon-recording.png" : "assets/icon.png" });
      // notify self and anyone who is listening
      messenger.send({ call: 'recordingToggled', value: recordingEnabled });

      chrome.tabs.sendMessage(currentTabId, { call: "toggleRecording", value: recordingEnabled });

      chrome.contextMenus.update(recordingContextMenuItemId, { checked: recordingEnabled });
    }
  };

function handleContextMenuClick(command, info, tab) {
    chrome.tabs.sendMessage(tab.id, { call: "handleContextMenuClick", command: command });
}

function openHelperWindow(_tab) {
  if (!uiWindow || uiWindow.closed) {
    uiWindow = window.open("ui/index.html", "extension_popup", "width=700,height=500,status=no,scrollbars=yes,resizable=no");
    uiWindow.currentTabId = currentTabId;
    chrome.contextMenus.update(recordingContextMenuItemId, { enabled: true });

    // can't record without ui window
    uiWindow.addEventListener('beforeunload', function () {
      uiWindow = undefined;
      recordingEnabled = true;
      api.toggleRecording();
    });
  } else {
    uiWindow.focus();
  }
}

// Create a parent item and two children.
recordingContextMenuItemId = chrome.contextMenus.create({ "title": "Record interactions", type: 'checkbox', checked: false, enabled: false, contexts: ["all"], onclick: api.toggleRecording.bind(api) });
chrome.contextMenus.create({ type: 'separator' });

chrome.contextMenus.create({ "title": "Assert Text", contexts: ["all"], onclick: handleContextMenuClick.bind(this, 'assertText') });

chrome.browserAction.onClicked.addListener(openHelperWindow);

// create link to api
messenger.bind(api);

// track active tab
chrome.tabs.onActivated.addListener(function (activeInfo) {
  currentTabId = activeInfo.tabId;
  currentWindowId = activeInfo.windowId;
  if (uiWindow) {
    uiWindow.currentTabId = currentTabId;
  }
  // notify active tab
  chrome.tabs.sendMessage(currentTabId, { call: "toggleRecording", value: recordingEnabled });
});<commit_msg>bugfix for current tab<commit_after>var messenger = require('./common/messenger');

var recordingEnabled = false,
  currentWindowId,
  currentTabId,
  uiWindow,
  recordingContextMenuItemId,
  api = {
    isRecordingEnabled: function (request, callback) {
      callback(recordingEnabled);
    },
    toggleRecording: function () {
      recordingEnabled = !recordingEnabled;
      chrome.browserAction.setIcon({ path: recordingEnabled ? "assets/icon-recording.png" : "assets/icon.png" });
      // notify self and anyone who is listening
      messenger.send({ call: 'recordingToggled', value: recordingEnabled });

      chrome.tabs.sendMessage(currentTabId, { call: "toggleRecording", value: recordingEnabled });

      chrome.contextMenus.update(recordingContextMenuItemId, { checked: recordingEnabled });
    }
  };

function handleContextMenuClick(command, info, tab) {
    chrome.tabs.sendMessage(tab.id, { call: "handleContextMenuClick", command: command });
}

function openHelperWindow(_tab) {
  if (!uiWindow || uiWindow.closed) {
    uiWindow = window.open("ui/index.html", "extension_popup", "width=700,height=500,status=no,scrollbars=yes,resizable=no");
    chrome.contextMenus.update(recordingContextMenuItemId, { enabled: true });

    // can't record without ui window
    uiWindow.addEventListener('beforeunload', function () {
      uiWindow = undefined;
      recordingEnabled = true;
      api.toggleRecording();
    });
  } else {
    uiWindow.focus();
  }
  uiWindow.currentTabId = currentTabId;
}

// Create a parent item and two children.
recordingContextMenuItemId = chrome.contextMenus.create({ "title": "Record interactions", type: 'checkbox', checked: false, enabled: false, contexts: ["all"], onclick: api.toggleRecording.bind(api) });
chrome.contextMenus.create({ type: 'separator' });

chrome.contextMenus.create({ "title": "Assert Text", contexts: ["all"], onclick: handleContextMenuClick.bind(this, 'assertText') });

chrome.browserAction.onClicked.addListener(openHelperWindow);

// create link to api
messenger.bind(api);

// track active tab
chrome.tabs.onActivated.addListener(function (activeInfo) {
  currentTabId = activeInfo.tabId;
  currentWindowId = activeInfo.windowId;
  if (uiWindow) {
    uiWindow.currentTabId = currentTabId;
  }
  // notify active tab
  chrome.tabs.sendMessage(currentTabId, { call: "toggleRecording", value: recordingEnabled });
});<|endoftext|>


<commit_before><commit_msg>Show and hide splash screen when loading <commit_after><|endoftext|>