<commit_before>/*!
 * Connect Session - Firebase
 * Copyright(c) 2016 Ben Weier <ben.weier@gmail.com>
 * MIT Licensed
 */
'use strict';

/**
 * Module dependencies.
 */
const firebase = require('firebase');

/**
 * Six hours in milliseconds.
 */
const sixHoursInMilliseconds = 21600000;

/**
 * Return the `FirebaseStore` extending `connect`'s session Store.
 *
 * @param {Object} session
 * @return {Function}
 * @api public
 */
const connectSessionFirebase = function connectSessionFirebase (session) {
  /**
   * Connect's Store.
   */
  const Store = session.Store;

  /**
   * FirebaseStore constructor.
   *
   * @param {Object} args
   * @param {Function} fn
   * @api public
   */
  const FirebaseStore = function FirebaseStore (args, fn) {
    const options = args || {};
    const db = options.database || false;
    const sessions = options.sessions || false;

    Store.call(options, fn);

    if (!db) {
      throw new Error('Connect Session Firebase must receive a `database` value.');
    }

    this.cleanRef = function cleanRef (str) {
      // Firebase does not allow certain characters.
      return str.replace(/\.|\$|#|\[|\]|\//g, '_');
    };

    // Initialized `firebase` instance.
    if (db.ref) {
      this.db = db;
    } else if (db.database) {
      this.db = db.database();
    } else if (typeof db === 'object') {
      // Initialize a Firebase instance with the configuration object.
      if (!db.databaseURL) {
        throw new Error('`database` object must have a `databaseURL` value');
      }

      this.db = firebase.initializeApp(db).database();
    } else {
      throw new Error('Invalid `database` value provided.');
    }

    // Set a child reference to the sessions path.
    this.sessions = typeof sessions === 'string' ? this.cleanRef(sessions) : 'sessions';

    this.reapInterval = options.reapInterval || sixHoursInMilliseconds;
    if (typeof this.reapInterval === 'number') {
      setInterval(this.reap.bind(this), this.reapInterval);
    }
  };

  /**
   *  Inherit from `Store`.
   */
  // FirebaseStore.prototype.__proto__ = Store.prototype;
  FirebaseStore.prototype = Object.create(Store.prototype);

  /**
   * Fetch a session by the given `sid`.
   *
   * @param {String} sid
   * @param {Function} fn
   * @api public
   */
  FirebaseStore.prototype.get = function get (sid, fn) {
    sid = this.cleanRef(sid);
    const now = Date.now();
    const session = this.db.ref(this.sessions).child(sid);

    return session.once('value')
      .then(snapshot => {
        if (!snapshot.exists()) {
          return fn();
        }

        if (snapshot.val().expires < now) {
          return this.destroy(sid, fn);
        }

        const sess = snapshot.val().sess.toString();

        return fn(null, JSON.parse(sess));
      })
      .catch(fn);
  };

  /**
   * Save the `sess` object with the given `sid`.
   *
   * @param {String} sid
   * @param {Session} sess
   * @param {Function} fn
   * @api public
   */
  FirebaseStore.prototype.set = function set (sid, sess, fn) {
    sid = this.cleanRef(sid);
    const now = Date.now();
    const expires = sess.cookie && typeof sess.cookie.maxAge === 'number' ? now + sess.cookie.maxAge : now + sixHoursInMilliseconds;
    const session = this.db.ref(this.sessions).child(sid);

    const data = {
      expires: expires,
      sess: JSON.stringify(sess),
      type: 'connect-session'
    };

    return session.set(data).then(fn).catch(fn);
  };

  /**
   * Remove the session with the given `sid`.
   *
   * @param {String} sid
   * @param {Function} fn
   * @api public
   */
  FirebaseStore.prototype.destroy = function destroy (sid, fn) {
    sid = this.cleanRef(sid);
    const session = this.db.ref(this.sessions).child(sid);

    return session.remove().then(fn);
  };

  /**
   * Remove all sessions.
   *
   * @param {Function} fn
   * @api public
   */
  FirebaseStore.prototype.clear = function clear (fn) {
    return this.db.ref(this.sessions).remove().then(fn);
  };

  /**
   * Remove expired sessions.
   *
   * @param {Function} fn
   * @api public
   */
  FirebaseStore.prototype.reap = function reap (fn) {
    const now = Date.now();
    const sessions = this.db.ref(this.sessions);

    return sessions.once('value')
      .then(snapshot => {
        if (!snapshot.exists()) {
          return fn();
        }

        Promise.all(snapshot.forEach(session => {
          if (session.val().expires < now) {
            sessions.child(session.key).remove();
          }
        }))
        .then(fn);

      })
      .catch(fn);
  };

  /**
   * Update a session object with the given `sid`.
   *
   * @param {String} sid
   * @param {Object} sess
   * @param {Function} fn
   * @api public
   */
  FirebaseStore.prototype.touch = function touch (sid, sess, fn) {
    sid = this.cleanRef(sid);
    const session = this.db.ref(this.sessions).child(sid);

    return session.once('value')
      .then(snapshot => {
        if (!snapshot.exists()) {
          return fn();
        }

        let currentSess = JSON.parse(snapshot.val().sess);
        currentSess.cookie = sess.cookie;

        return this.set(sid, currentSess, fn);
      })
      .catch(fn);
  };

  return FirebaseStore;
};

module.exports = connectSessionFirebase;
<commit_msg>No re-assignment of function arguments<commit_after>/*!
 * Connect Session - Firebase
 * Copyright(c) 2016 Ben Weier <ben.weier@gmail.com>
 * MIT Licensed
 */
'use strict';

/**
 * Module dependencies.
 */
const firebase = require('firebase');

/**
 * Six hours in milliseconds.
 */
const sixHoursInMilliseconds = 21600000;

/**
 * Return the `FirebaseStore` extending `connect`'s session Store.
 *
 * @param {Object} session
 * @return {Function}
 * @api public
 */
const connectSessionFirebase = function connectSessionFirebase (session) {
  /**
   * Connect's Store.
   */
  const Store = session.Store;

  /**
   * FirebaseStore constructor.
   *
   * @param {Object} args
   * @param {Function} fn
   * @api public
   */
  const FirebaseStore = function FirebaseStore (args, fn) {
    const options = args || {};
    const db = options.database || false;
    const sessions = options.sessions || false;

    Store.call(options, fn);

    if (!db) {
      throw new Error('Connect Session Firebase must receive a `database` value.');
    }

    this.cleanRef = function cleanRef (str) {
      // Firebase does not allow certain characters.
      return str.replace(/\.|\$|#|\[|\]|\//g, '_');
    };

    // Initialized `firebase` instance.
    if (db.ref) {
      this.db = db;
    } else if (db.database) {
      this.db = db.database();
    } else if (typeof db === 'object') {
      // Initialize a Firebase instance with the configuration object.
      if (!db.databaseURL) {
        throw new Error('`database` object must have a `databaseURL` value');
      }

      this.db = firebase.initializeApp(db).database();
    } else {
      throw new Error('Invalid `database` value provided.');
    }

    // Set a child reference to the sessions path.
    this.sessions = typeof sessions === 'string' ? this.cleanRef(sessions) : 'sessions';

    this.reapInterval = options.reapInterval || sixHoursInMilliseconds;
    if (typeof this.reapInterval === 'number') {
      setInterval(this.reap.bind(this), this.reapInterval);
    }
  };

  /**
   *  Inherit from `Store`.
   */
  // FirebaseStore.prototype.__proto__ = Store.prototype;
  FirebaseStore.prototype = Object.create(Store.prototype);

  /**
   * Fetch a session by the given `sid`.
   *
   * @param {String} sid
   * @param {Function} fn
   * @api public
   */
  FirebaseStore.prototype.get = function get (sid, fn) {
    const key = this.cleanRef(sid);
    const now = Date.now();
    const session = this.db.ref(this.sessions).child(key);

    return session.once('value')
      .then(snapshot => {
        if (!snapshot.exists()) {
          return fn();
        }

        if (snapshot.val().expires < now) {
          return this.destroy(sid, fn);
        }

        const sess = snapshot.val().sess.toString();

        return fn(null, JSON.parse(sess));
      })
      .catch(fn);
  };

  /**
   * Save the `sess` object with the given `sid`.
   *
   * @param {String} sid
   * @param {Session} sess
   * @param {Function} fn
   * @api public
   */
  FirebaseStore.prototype.set = function set (sid, sess, fn) {
    const key = this.cleanRef(sid);
    const now = Date.now();
    const expires = sess.cookie && typeof sess.cookie.maxAge === 'number' ? now + sess.cookie.maxAge : now + sixHoursInMilliseconds;
    const session = this.db.ref(this.sessions).child(key);

    const data = {
      expires: expires,
      sess: JSON.stringify(sess),
      type: 'connect-session'
    };

    return session.set(data).then(fn).catch(fn);
  };

  /**
   * Remove the session with the given `sid`.
   *
   * @param {String} sid
   * @param {Function} fn
   * @api public
   */
  FirebaseStore.prototype.destroy = function destroy (sid, fn) {
    const key = this.cleanRef(sid);
    const session = this.db.ref(this.sessions).child(key);

    return session.remove().then(fn);
  };

  /**
   * Remove all sessions.
   *
   * @param {Function} fn
   * @api public
   */
  FirebaseStore.prototype.clear = function clear (fn) {
    return this.db.ref(this.sessions).remove().then(fn);
  };

  /**
   * Remove expired sessions.
   *
   * @param {Function} fn
   * @api public
   */
  FirebaseStore.prototype.reap = function reap (fn) {
    const now = Date.now();
    const sessions = this.db.ref(this.sessions);

    return sessions.once('value')
      .then(snapshot => {
        if (!snapshot.exists()) {
          return fn();
        }

        Promise.all(snapshot.forEach(session => {
          if (session.val().expires < now) {
            sessions.child(session.key).remove();
          }
        }))
        .then(fn);

      })
      .catch(fn);
  };

  /**
   * Update a session object with the given `sid`.
   *
   * @param {String} sid
   * @param {Object} sess
   * @param {Function} fn
   * @api public
   */
  FirebaseStore.prototype.touch = function touch (sid, sess, fn) {
    const key = this.cleanRef(sid);
    const session = this.db.ref(this.sessions).child(key);

    return session.once('value')
      .then(snapshot => {
        if (!snapshot.exists()) {
          return fn();
        }

        let currentSess = JSON.parse(snapshot.val().sess);
        currentSess.cookie = sess.cookie;

        return this.set(sid, currentSess, fn);
      })
      .catch(fn);
  };

  return FirebaseStore;
};

module.exports = connectSessionFirebase;
<|endoftext|>


<commit_before>'use strict';

var mongoose = require('mongoose'),
	utils = require('../utils'),
	User = mongoose.model('User'),
	Application = mongoose.model('Application'),
	SimpleApiKey = mongoose.model('SimpleApiKey'),
	OauthConsumer = mongoose.model('OauthConsumer'),
	Event = mongoose.model('Event'),
	User = mongoose.model('User'),
	Chapter = mongoose.model('Chapter'),
	middleware = require('../../../middleware'),
	devsite = require('../../../clients/devsite');

module.exports = function(app, cacher) {
	app.route("get", "/organizer/:gplusId", {
		summary: "Returns if the specified Google+ user is an Organizer of one or more Chapters"
	}, cacher.cache('hours', 24), function(req, res) {
		Chapter.find({organizers: req.params.gplusId}, function(err, chapters) {
				if (err) { console.log(err); return res.send(500, "Internal Error"); }

				var response = {
					msg: "ok",
					user: req.params.gplusId,
					chapters: []
				};
				for(var i = 0; i < chapters.length; i++) {
					response.chapters.push({ id: chapters[i]._id, name: chapters[i].name });
				}

				return res.send(200, response);
			}
		);
	});
}
<commit_msg>Fix missin JSONP support on /organizer/:gplusId<commit_after>'use strict';

var mongoose = require('mongoose'),
	utils = require('../utils'),
	User = mongoose.model('User'),
	Application = mongoose.model('Application'),
	SimpleApiKey = mongoose.model('SimpleApiKey'),
	OauthConsumer = mongoose.model('OauthConsumer'),
	Event = mongoose.model('Event'),
	User = mongoose.model('User'),
	Chapter = mongoose.model('Chapter'),
	middleware = require('../../../middleware'),
	devsite = require('../../../clients/devsite');

module.exports = function(app, cacher) {
	app.route("get", "/organizer/:gplusId", {
		summary: "Returns if the specified Google+ user is an Organizer of one or more Chapters"
	}, cacher.cache('hours', 24), function(req, res) {
		Chapter.find({organizers: req.params.gplusId}, function(err, chapters) {
				if (err) { console.log(err); return res.send(500, "Internal Error"); }

				var response = {
					user: req.params.gplusId,
					chapters: []
				};
				for(var i = 0; i < chapters.length; i++) {
					response.chapters.push({ id: chapters[i]._id, name: chapters[i].name });
				}

				return res.jsonp(response);
			}
		);
	});
}
<|endoftext|>


<commit_before>/* eslint-disable
    global-require,
    import/no-dynamic-require,
    no-underscore-dangle,
    prefer-destructuring,
*/
const path = require('path');
const fs = require('fs');
const manifest = require('fbp-manifest');
const fbpGraph = require('fbp-graph');

const utils = require('../Utils');

// Type loading CoffeeScript compiler
let CoffeeScript;
try {
  // eslint-disable-next-line import/no-unresolved
  CoffeeScript = require('coffeescript');
  if (typeof CoffeeScript.register !== 'undefined') {
    CoffeeScript.register();
  }
} catch (e) {
  // If there is no CoffeeScript compiler installed, we simply don't support compiling
}

// Try loading TypeScript compiler
let typescript;
try {
  // eslint-disable-next-line import/no-unresolved,import/no-extraneous-dependencies
  typescript = require('typescript');
} catch (e) {
  // If there is no TypeScript compiler installed, we simply don't support compiling
}

function transpileSource(packageId, name, source, language, callback) {
  let src;
  switch (language) {
    case 'coffeescript': {
      if (!CoffeeScript) {
        callback(new Error(`Unsupported component source language ${language} for ${packageId}/${name}: no CoffeeScript compiler installed`));
      }
      try {
        src = CoffeeScript.compile(source, {
          bare: true,
        });
      } catch (err) {
        callback(err);
        return;
      }
      break;
    }
    case 'typescript': {
      if (!typescript) {
        callback(new Error(`Unsupported component source language ${language} for ${packageId}/${name}: no TypeScript compiler installed`));
      }
      try {
        src = typescript.transpile(source, {
          module: typescript.ModuleKind.CommonJS,
          target: typescript.ScriptTarget.ES2015,
        });
      } catch (err) {
        callback(err);
        return;
      }
      break;
    }
    case 'es6':
    case 'es2015':
    case 'js':
    case 'javascript': {
      src = source;
      break;
    }
    default: {
      callback(new Error(`Unsupported component source language ${language} for ${packageId}/${name}`));
      return;
    }
  }
  callback(null, src);
}

function evaluateModule(baseDir, packageId, name, source, callback) {
  const Module = require('module');
  let implementation;
  try {
    // Use the Node.js module API to evaluate in the correct directory context
    const modulePath = path.resolve(baseDir, `./components/${name}.js`);
    const moduleImpl = new Module(modulePath, module);
    moduleImpl.paths = Module._nodeModulePaths(path.dirname(modulePath));
    moduleImpl.filename = modulePath;
    moduleImpl._compile(source, modulePath);
    implementation = moduleImpl.exports;
  } catch (e) {
    callback(e);
    return;
  }
  if ((typeof implementation !== 'function') && (typeof implementation.getComponent !== 'function')) {
    callback(new Error(`Provided source for ${packageId}/${name} failed to create a runnable component`));
    return;
  }
  callback(null, implementation);
}

function registerSources(loader, packageId, name, source, language) {
  if (!loader.sourcesForComponents) {
    // eslint-disable-next-line no-param-reassign
    loader.sourcesForComponents = {};
  }
  const componentName = `${packageId}/${name}`;
  // eslint-disable-next-line no-param-reassign
  loader.sourcesForComponents[componentName] = {
    language,
    source,
  };
}

function registerSpecs(loader, packageId, name, specs) {
  if (!loader.specsForComponents) {
    // eslint-disable-next-line no-param-reassign
    loader.specsForComponents = {};
  }
  const componentName = `${packageId}/${name}`;
  // eslint-disable-next-line no-param-reassign
  loader.specsForComponents[componentName] = specs;
}

function transpileAndRegisterForModule(loader, module, component, source, language, callback) {
  transpileSource(module.name, component.name, source, language, (transpileError, src) => {
    if (transpileError) {
      callback(transpileError);
      return;
    }
    const moduleBase = path.resolve(loader.baseDir, module.base);
    evaluateModule(moduleBase, module.name, component.name, src, (evalError, implementation) => {
      if (evalError) {
        callback(evalError);
        return;
      }
      registerSources(loader, module.name, component.name, source, language);
      registerSpecs(loader, module.name, component.name, component.tests);
      loader.registerComponent(module.name, component.name, implementation, callback);
    });
  });
}

exports.setSource = function setSource(loader, packageId, name, source, language, callback) {
  transpileAndRegisterForModule(loader, {
    name: packageId,
    base: '',
  }, {
    name,
  }, source, language, callback);
};

exports.getSource = function getSource(loader, name, callback) {
  let componentName = name;
  let component = loader.components[name];
  if (!component) {
    // Try an alias
    const keys = Object.keys(loader.components);
    for (let i = 0; i < keys.length; i += 1) {
      const key = keys[i];
      if (key.split('/')[1] === name) {
        component = loader.components[key];
        componentName = key;
        break;
      }
    }
    if (!component) {
      callback(new Error(`Component ${componentName} not installed`));
      return;
    }
  }

  const nameParts = componentName.split('/');
  if (nameParts.length === 1) {
    nameParts[1] = nameParts[0];
    nameParts[0] = '';
  }

  const finalize = (err, src) => {
    if (err) {
      callback(err);
      return;
    }
    if (!loader.specsForComponents) {
      callback(err, src);
      return;
    }
    if (!loader.specsForComponents[componentName]) {
      callback(err, src);
    }
    const specPath = loader.specsForComponents[componentName];
    fs.readFile(path.resolve(loader.baseDir, specPath), 'utf-8', (fsErr, specs) => {
      if (fsErr) {
        // Ignore spec reading errors
        callback(err, src);
        return;
      }
      callback(err, {
        ...src,
        tests: specs,
      });
    });
  };

  if (loader.isGraph(component)) {
    if (typeof component === 'object') {
      if (typeof component.toJSON === 'function') {
        finalize(null, {
          name: nameParts[1],
          library: nameParts[0],
          code: JSON.stringify(component.toJSON()),
          tests: loader.specsForComponents ? loader.specsForComponents[componentName] : '',
          language: 'json',
        });
        return;
      }
      finalize(new Error(`Can't provide source for ${componentName}. Not a file`));
      return;
    }
    fbpGraph.graph.loadFile(component, (err, graph) => {
      if (err) {
        finalize(err);
        return;
      }
      if (!graph) {
        finalize(new Error('Unable to load graph'));
        return;
      }
      finalize(null, {
        name: nameParts[1],
        library: nameParts[0],
        code: JSON.stringify(graph.toJSON()),
        tests: loader.specsForComponents ? loader.specsForComponents[componentName] : '',
        language: 'json',
      });
    });
    return;
  }

  if (loader.sourcesForComponents && loader.sourcesForComponents[componentName]) {
    finalize(null, {
      name: nameParts[1],
      library: nameParts[0],
      code: loader.sourcesForComponents[componentName].source,
      tests: loader.specsForComponents ? loader.specsForComponents[componentName] : '',
      language: loader.sourcesForComponents[componentName].language,
    });
    return;
  }

  if (typeof component !== 'string') {
    finalize(new Error(`Can't provide source for ${componentName}. Not a file`));
    return;
  }

  fs.readFile(component, 'utf-8', (err, code) => {
    if (err) {
      finalize(err);
      return;
    }
    finalize(null, {
      name: nameParts[1],
      library: nameParts[0],
      language: utils.guessLanguageFromFilename(component),
      code,
    });
  });
};

exports.getLanguages = function getLanguages() {
  const languages = ['javascript', 'es2015'];
  if (CoffeeScript) {
    languages.push('coffeescript');
  }
  if (typescript) {
    languages.push('typescript');
  }
  return languages;
};

function registerCustomLoaders(loader, componentLoaders, callback) {
  if (!componentLoaders.length) {
    callback(null);
    return;
  }
  const customLoader = require(componentLoaders.shift());
  loader.registerLoader(customLoader, (err) => {
    if (err) {
      callback(err);
      return;
    }
    registerCustomLoaders(loader, componentLoaders, callback);
  });
}

function registerModules(loader, modules, callback) {
  const compatible = modules.filter((m) => ['noflo', 'noflo-nodejs'].includes(m.runtime));
  const componentLoaders = [];
  Promise.all(compatible.map((m) => {
    if (m.icon) {
      loader.setLibraryIcon(m.name, m.icon);
    }

    if (m.noflo != null ? m.noflo.loader : undefined) {
      const loaderPath = path.resolve(loader.baseDir, m.base, m.noflo.loader);
      componentLoaders.push(loaderPath);
    }

    return Promise.all(m.components.map((c) => new Promise((resolve, reject) => {
      const language = utils.guessLanguageFromFilename(c.path);
      if (language === 'typescript') {
        // We can't require a TypeScript module, go the setSource route
        fs.readFile(path.resolve(loader.baseDir, c.path), 'utf-8', (fsErr, source) => {
          if (fsErr) {
            reject(fsErr);
            return;
          }
          transpileAndRegisterForModule(loader, m, c, source, language, (err) => {
            if (err) {
              reject(err);
              return;
            }
            resolve();
          });
        });
        return;
      }
      registerSpecs(loader, m.name, c.name, c.tests);
      loader.registerComponent(m.name, c.name, path.resolve(loader.baseDir, c.path), (err) => {
        if (err) {
          reject(err);
          return;
        }
        resolve();
      });
    })));
  }))
    .then(
      () => {
        registerCustomLoaders(loader, componentLoaders, callback);
      },
      callback,
    );
}

const dynamicLoader = {
  listComponents(loader, manifestOptions, callback) {
    const opts = manifestOptions;
    opts.discover = true;
    manifest.list.list(loader.baseDir, opts, (err, modules) => {
      if (err) {
        callback(err);
        return;
      }
      registerModules(loader, modules, (err2) => {
        if (err2) {
          callback(err2);
          return;
        }
        callback(null, modules);
      });
    });
  },
};

const manifestLoader = {
  writeCache(loader, options, manifestContents, callback) {
    const filePath = path.resolve(loader.baseDir, options.manifest);
    fs.writeFile(filePath, JSON.stringify(manifestContents, null, 2),
      { encoding: 'utf-8' },
      callback);
  },

  readCache(loader, options, callback) {
    const opts = options;
    opts.discover = false;
    manifest.load.load(loader.baseDir, opts, callback);
  },

  prepareManifestOptions(loader) {
    const l = loader;
    if (!l.options) { l.options = {}; }
    const options = {};
    options.runtimes = l.options.runtimes || [];
    if (options.runtimes.indexOf('noflo') === -1) { options.runtimes.push('noflo'); }
    options.recursive = typeof l.options.recursive === 'undefined' ? true : l.options.recursive;
    options.manifest = l.options.manifest || 'fbp.json';
    return options;
  },

  listComponents(loader, manifestOptions, callback) {
    this.readCache(loader, manifestOptions, (err, manifestContents) => {
      if (err) {
        if (!loader.options.discover) {
          callback(err);
          return;
        }
        dynamicLoader.listComponents(loader, manifestOptions, (err2, modules) => {
          if (err2) {
            callback(err2);
            return;
          }
          this.writeCache(loader, manifestOptions, {
            version: 1,
            modules,
          },
          (err3) => {
            if (err3) {
              callback(err3);
              return;
            }
            callback(null, modules);
          });
        });
        return;
      }
      registerModules(loader, manifestContents.modules, (err2) => {
        if (err2) {
          callback(err2);
          return;
        }
        callback(null, manifestContents.modules);
      });
    });
  },
};

function registerSubgraph(loader) {
  // Inject subgraph component
  const graphPath = path.resolve(__dirname, '../../components/Graph.js');
  loader.registerComponent(null, 'Graph', graphPath);
}

exports.register = function register(loader, callback) {
  const manifestOptions = manifestLoader.prepareManifestOptions(loader);

  if (loader.options != null ? loader.options.cache : undefined) {
    manifestLoader.listComponents(loader, manifestOptions, (err, modules) => {
      if (err) {
        callback(err);
        return;
      }
      registerSubgraph(loader);
      callback(null, modules);
    });
    return;
  }

  dynamicLoader.listComponents(loader, manifestOptions, (err, modules) => {
    if (err) {
      callback(err);
      return;
    }
    registerSubgraph(loader);
    callback(null, modules);
  });
};

exports.dynamicLoad = function dynamicLoad(name, cPath, metadata, callback) {
  let implementation; let instance;
  try {
    implementation = require(cPath);
  } catch (err) {
    callback(err);
    return;
  }

  if (typeof implementation.getComponent === 'function') {
    try {
      instance = implementation.getComponent(metadata);
    } catch (err) {
      callback(err);
      return;
    }
  } else if (typeof implementation === 'function') {
    try {
      instance = implementation(metadata);
    } catch (err) {
      callback(err);
      return;
    }
  } else {
    callback(new Error(`Unable to instantiate ${cPath}`));
    return;
  }
  if (typeof name === 'string') { instance.componentName = name; }
  callback(null, instance);
};
<commit_msg>Load only fbp-spec files<commit_after>/* eslint-disable
    global-require,
    import/no-dynamic-require,
    no-underscore-dangle,
    prefer-destructuring,
*/
const path = require('path');
const fs = require('fs');
const manifest = require('fbp-manifest');
const fbpGraph = require('fbp-graph');

const utils = require('../Utils');

// Type loading CoffeeScript compiler
let CoffeeScript;
try {
  // eslint-disable-next-line import/no-unresolved
  CoffeeScript = require('coffeescript');
  if (typeof CoffeeScript.register !== 'undefined') {
    CoffeeScript.register();
  }
} catch (e) {
  // If there is no CoffeeScript compiler installed, we simply don't support compiling
}

// Try loading TypeScript compiler
let typescript;
try {
  // eslint-disable-next-line import/no-unresolved,import/no-extraneous-dependencies
  typescript = require('typescript');
} catch (e) {
  // If there is no TypeScript compiler installed, we simply don't support compiling
}

function transpileSource(packageId, name, source, language, callback) {
  let src;
  switch (language) {
    case 'coffeescript': {
      if (!CoffeeScript) {
        callback(new Error(`Unsupported component source language ${language} for ${packageId}/${name}: no CoffeeScript compiler installed`));
      }
      try {
        src = CoffeeScript.compile(source, {
          bare: true,
        });
      } catch (err) {
        callback(err);
        return;
      }
      break;
    }
    case 'typescript': {
      if (!typescript) {
        callback(new Error(`Unsupported component source language ${language} for ${packageId}/${name}: no TypeScript compiler installed`));
      }
      try {
        src = typescript.transpile(source, {
          module: typescript.ModuleKind.CommonJS,
          target: typescript.ScriptTarget.ES2015,
        });
      } catch (err) {
        callback(err);
        return;
      }
      break;
    }
    case 'es6':
    case 'es2015':
    case 'js':
    case 'javascript': {
      src = source;
      break;
    }
    default: {
      callback(new Error(`Unsupported component source language ${language} for ${packageId}/${name}`));
      return;
    }
  }
  callback(null, src);
}

function evaluateModule(baseDir, packageId, name, source, callback) {
  const Module = require('module');
  let implementation;
  try {
    // Use the Node.js module API to evaluate in the correct directory context
    const modulePath = path.resolve(baseDir, `./components/${name}.js`);
    const moduleImpl = new Module(modulePath, module);
    moduleImpl.paths = Module._nodeModulePaths(path.dirname(modulePath));
    moduleImpl.filename = modulePath;
    moduleImpl._compile(source, modulePath);
    implementation = moduleImpl.exports;
  } catch (e) {
    callback(e);
    return;
  }
  if ((typeof implementation !== 'function') && (typeof implementation.getComponent !== 'function')) {
    callback(new Error(`Provided source for ${packageId}/${name} failed to create a runnable component`));
    return;
  }
  callback(null, implementation);
}

function registerSources(loader, packageId, name, source, language) {
  if (!loader.sourcesForComponents) {
    // eslint-disable-next-line no-param-reassign
    loader.sourcesForComponents = {};
  }
  const componentName = `${packageId}/${name}`;
  // eslint-disable-next-line no-param-reassign
  loader.sourcesForComponents[componentName] = {
    language,
    source,
  };
}

function registerSpecs(loader, packageId, name, specs) {
  if (!specs || specs.indexOf('.yaml') === -1) {
    // We support only fbp-spec specs
    return;
  }
  if (!loader.specsForComponents) {
    // eslint-disable-next-line no-param-reassign
    loader.specsForComponents = {};
  }
  const componentName = `${packageId}/${name}`;
  // eslint-disable-next-line no-param-reassign
  loader.specsForComponents[componentName] = specs;
}

function transpileAndRegisterForModule(loader, module, component, source, language, callback) {
  transpileSource(module.name, component.name, source, language, (transpileError, src) => {
    if (transpileError) {
      callback(transpileError);
      return;
    }
    const moduleBase = path.resolve(loader.baseDir, module.base);
    evaluateModule(moduleBase, module.name, component.name, src, (evalError, implementation) => {
      if (evalError) {
        callback(evalError);
        return;
      }
      registerSources(loader, module.name, component.name, source, language);
      registerSpecs(loader, module.name, component.name, component.tests);
      loader.registerComponent(module.name, component.name, implementation, callback);
    });
  });
}

exports.setSource = function setSource(loader, packageId, name, source, language, callback) {
  transpileAndRegisterForModule(loader, {
    name: packageId,
    base: '',
  }, {
    name,
  }, source, language, callback);
};

exports.getSource = function getSource(loader, name, callback) {
  let componentName = name;
  let component = loader.components[name];
  if (!component) {
    // Try an alias
    const keys = Object.keys(loader.components);
    for (let i = 0; i < keys.length; i += 1) {
      const key = keys[i];
      if (key.split('/')[1] === name) {
        component = loader.components[key];
        componentName = key;
        break;
      }
    }
    if (!component) {
      callback(new Error(`Component ${componentName} not installed`));
      return;
    }
  }

  const nameParts = componentName.split('/');
  if (nameParts.length === 1) {
    nameParts[1] = nameParts[0];
    nameParts[0] = '';
  }

  const finalize = (err, src) => {
    if (err) {
      callback(err);
      return;
    }
    if (!loader.specsForComponents) {
      callback(err, src);
      return;
    }
    if (!loader.specsForComponents[componentName]) {
      callback(err, src);
    }
    const specPath = loader.specsForComponents[componentName];
    fs.readFile(path.resolve(loader.baseDir, specPath), 'utf-8', (fsErr, specs) => {
      if (fsErr) {
        // Ignore spec reading errors
        callback(err, src);
        return;
      }
      callback(err, {
        ...src,
        tests: specs,
      });
    });
  };

  if (loader.isGraph(component)) {
    if (typeof component === 'object') {
      if (typeof component.toJSON === 'function') {
        finalize(null, {
          name: nameParts[1],
          library: nameParts[0],
          code: JSON.stringify(component.toJSON()),
          tests: loader.specsForComponents ? loader.specsForComponents[componentName] : '',
          language: 'json',
        });
        return;
      }
      finalize(new Error(`Can't provide source for ${componentName}. Not a file`));
      return;
    }
    fbpGraph.graph.loadFile(component, (err, graph) => {
      if (err) {
        finalize(err);
        return;
      }
      if (!graph) {
        finalize(new Error('Unable to load graph'));
        return;
      }
      finalize(null, {
        name: nameParts[1],
        library: nameParts[0],
        code: JSON.stringify(graph.toJSON()),
        tests: loader.specsForComponents ? loader.specsForComponents[componentName] : '',
        language: 'json',
      });
    });
    return;
  }

  if (loader.sourcesForComponents && loader.sourcesForComponents[componentName]) {
    finalize(null, {
      name: nameParts[1],
      library: nameParts[0],
      code: loader.sourcesForComponents[componentName].source,
      tests: loader.specsForComponents ? loader.specsForComponents[componentName] : '',
      language: loader.sourcesForComponents[componentName].language,
    });
    return;
  }

  if (typeof component !== 'string') {
    finalize(new Error(`Can't provide source for ${componentName}. Not a file`));
    return;
  }

  fs.readFile(component, 'utf-8', (err, code) => {
    if (err) {
      finalize(err);
      return;
    }
    finalize(null, {
      name: nameParts[1],
      library: nameParts[0],
      language: utils.guessLanguageFromFilename(component),
      code,
    });
  });
};

exports.getLanguages = function getLanguages() {
  const languages = ['javascript', 'es2015'];
  if (CoffeeScript) {
    languages.push('coffeescript');
  }
  if (typescript) {
    languages.push('typescript');
  }
  return languages;
};

function registerCustomLoaders(loader, componentLoaders, callback) {
  if (!componentLoaders.length) {
    callback(null);
    return;
  }
  const customLoader = require(componentLoaders.shift());
  loader.registerLoader(customLoader, (err) => {
    if (err) {
      callback(err);
      return;
    }
    registerCustomLoaders(loader, componentLoaders, callback);
  });
}

function registerModules(loader, modules, callback) {
  const compatible = modules.filter((m) => ['noflo', 'noflo-nodejs'].includes(m.runtime));
  const componentLoaders = [];
  Promise.all(compatible.map((m) => {
    if (m.icon) {
      loader.setLibraryIcon(m.name, m.icon);
    }

    if (m.noflo != null ? m.noflo.loader : undefined) {
      const loaderPath = path.resolve(loader.baseDir, m.base, m.noflo.loader);
      componentLoaders.push(loaderPath);
    }

    return Promise.all(m.components.map((c) => new Promise((resolve, reject) => {
      const language = utils.guessLanguageFromFilename(c.path);
      if (language === 'typescript') {
        // We can't require a TypeScript module, go the setSource route
        fs.readFile(path.resolve(loader.baseDir, c.path), 'utf-8', (fsErr, source) => {
          if (fsErr) {
            reject(fsErr);
            return;
          }
          transpileAndRegisterForModule(loader, m, c, source, language, (err) => {
            if (err) {
              reject(err);
              return;
            }
            resolve();
          });
        });
        return;
      }
      registerSpecs(loader, m.name, c.name, c.tests);
      loader.registerComponent(m.name, c.name, path.resolve(loader.baseDir, c.path), (err) => {
        if (err) {
          reject(err);
          return;
        }
        resolve();
      });
    })));
  }))
    .then(
      () => {
        registerCustomLoaders(loader, componentLoaders, callback);
      },
      callback,
    );
}

const dynamicLoader = {
  listComponents(loader, manifestOptions, callback) {
    const opts = manifestOptions;
    opts.discover = true;
    manifest.list.list(loader.baseDir, opts, (err, modules) => {
      if (err) {
        callback(err);
        return;
      }
      registerModules(loader, modules, (err2) => {
        if (err2) {
          callback(err2);
          return;
        }
        callback(null, modules);
      });
    });
  },
};

const manifestLoader = {
  writeCache(loader, options, manifestContents, callback) {
    const filePath = path.resolve(loader.baseDir, options.manifest);
    fs.writeFile(filePath, JSON.stringify(manifestContents, null, 2),
      { encoding: 'utf-8' },
      callback);
  },

  readCache(loader, options, callback) {
    const opts = options;
    opts.discover = false;
    manifest.load.load(loader.baseDir, opts, callback);
  },

  prepareManifestOptions(loader) {
    const l = loader;
    if (!l.options) { l.options = {}; }
    const options = {};
    options.runtimes = l.options.runtimes || [];
    if (options.runtimes.indexOf('noflo') === -1) { options.runtimes.push('noflo'); }
    options.recursive = typeof l.options.recursive === 'undefined' ? true : l.options.recursive;
    options.manifest = l.options.manifest || 'fbp.json';
    return options;
  },

  listComponents(loader, manifestOptions, callback) {
    this.readCache(loader, manifestOptions, (err, manifestContents) => {
      if (err) {
        if (!loader.options.discover) {
          callback(err);
          return;
        }
        dynamicLoader.listComponents(loader, manifestOptions, (err2, modules) => {
          if (err2) {
            callback(err2);
            return;
          }
          this.writeCache(loader, manifestOptions, {
            version: 1,
            modules,
          },
          (err3) => {
            if (err3) {
              callback(err3);
              return;
            }
            callback(null, modules);
          });
        });
        return;
      }
      registerModules(loader, manifestContents.modules, (err2) => {
        if (err2) {
          callback(err2);
          return;
        }
        callback(null, manifestContents.modules);
      });
    });
  },
};

function registerSubgraph(loader) {
  // Inject subgraph component
  const graphPath = path.resolve(__dirname, '../../components/Graph.js');
  loader.registerComponent(null, 'Graph', graphPath);
}

exports.register = function register(loader, callback) {
  const manifestOptions = manifestLoader.prepareManifestOptions(loader);

  if (loader.options != null ? loader.options.cache : undefined) {
    manifestLoader.listComponents(loader, manifestOptions, (err, modules) => {
      if (err) {
        callback(err);
        return;
      }
      registerSubgraph(loader);
      callback(null, modules);
    });
    return;
  }

  dynamicLoader.listComponents(loader, manifestOptions, (err, modules) => {
    if (err) {
      callback(err);
      return;
    }
    registerSubgraph(loader);
    callback(null, modules);
  });
};

exports.dynamicLoad = function dynamicLoad(name, cPath, metadata, callback) {
  let implementation; let instance;
  try {
    implementation = require(cPath);
  } catch (err) {
    callback(err);
    return;
  }

  if (typeof implementation.getComponent === 'function') {
    try {
      instance = implementation.getComponent(metadata);
    } catch (err) {
      callback(err);
      return;
    }
  } else if (typeof implementation === 'function') {
    try {
      instance = implementation(metadata);
    } catch (err) {
      callback(err);
      return;
    }
  } else {
    callback(new Error(`Unable to instantiate ${cPath}`));
    return;
  }
  if (typeof name === 'string') { instance.componentName = name; }
  callback(null, instance);
};
<|endoftext|>


<commit_before>
define([
    'flight/lib/component',
    'tpl!./overlay',
    'util/formatters',
    'service/workspace'
], function(defineComponent, template, formatters, WorkspaceService) {
    'use strict';

    var LAST_SAVED_UPDATE_FREQUENCY_SECONDS = 30;
    var MENUBAR_WIDTH = 30;
    var UPDATE_WORKSPACE_DIFF_SECONDS = 5;

    return defineComponent(WorkspaceOverlay);

    function WorkspaceOverlay() {

        var workspaceService = new WorkspaceService();

        this.defaultAttrs({
            userSelector: '.user',
            nameSelector: '.name',
            subtitleSelector: '.subtitle'
        });

        this.after('initialize', function() {
            var self = this;

            this.userDeferred = $.Deferred();
            this.workspaceDeferred = $.Deferred();
            this.updateDiffBadge = _.throttle(this.updateDiffBadge.bind(this), UPDATE_WORKSPACE_DIFF_SECONDS * 1000)

            $.when(this.userDeferred, this.workspaceDeferred).done(function() {
                self.$node.show();

                requestAnimationFrame(function() {
                    self.$node.addClass('visible');
                });
            })

            this.$node.hide().html(template({}));

            this.on(document, 'workspaceSaving', this.onWorkspaceSaving);
            this.on(document, 'workspaceSaved', this.onWorkspaceSaved);
            this.on(document, 'workspaceLoaded', this.onWorkspaceLoaded);
            this.on(document, 'switchWorkspace', this.onSwitchWorkspace);
            this.on(document, 'graphPaddingUpdated', this.onGraphPaddingUpdated);
            this.on(document, 'currentUserChanged', this.onCurrentUserChanged)
            this.on(document, 'relationshipsLoaded', this.onRelationshipsLoaded)
            this.on(document, 'ajaxComplete', this.onAjaxComplete);
        });

        this.onAjaxComplete = function(event, xhr, settings) {
            // Automatically call diff after every POST
            if (/post/i.test(settings.type)) {
                this.updateDiffBadge();
            }
        };

        this.onCurrentUserChanged = function(event, data) {
            this.userDeferred.resolve();
            this.updateUserTooltip(data);
        }

        this.onGraphPaddingUpdated = function(event, data) {
            this.$node.css('left', data.padding.l + MENUBAR_WIDTH);
        };

        this.setContent = function(title, isEditable, subtitle) {
            this.select('nameSelector').text(title);
            this.select('subtitleSelector').html(isEditable === false ? 'read only' : subtitle);
        };

        this.onSwitchWorkspace = function() {
            this.$node.find('.badge').remove();
        };

        this.onWorkspaceLoaded = function(event, data) {
            this.workspaceDeferred.resolve();
            this.setContent(data.title, data.isEditable, 'no changes');
            clearTimeout(this.updateTimer);
            this.updateWorkspaceTooltip(data);
            this.updateDiffBadge();
        };

        this.onRelationshipsLoaded = function(event, data) {
            this.updateWorkspaceTooltip(data);
        };

        this.onWorkspaceSaving = function(event, data) {
            this.select('subtitleSelector').text('saving...');
            clearTimeout(this.updateTimer);
            this.updateWorkspaceTooltip(data);
        };

        this.onWorkspaceSaved = function(event, data) {
            clearTimeout(this.updateTimer);
            this.lastSaved = formatters.date.utc(Date.now());

            if (data.title) {
                this.select('nameSelector').text(data.title);
            }

            this.updateWorkspaceTooltip(data);

            var prefix = 'last saved ',
                subtitle = this.select('subtitleSelector').text(prefix + 'moments ago'),
                setTimer = function() {
                    this.updateTimer = setTimeout(function () {

                        var time = formatters.date.relativeToNow(this.lastSaved);
                        subtitle.text(prefix + time);

                        setTimer();
                    }.bind(this), LAST_SAVED_UPDATE_FREQUENCY_SECONDS * 1000);
                }.bind(this);

            setTimer();
        };

        this.updateDiffBadge = function() {
            var self = this,
                node = this.select('nameSelector'),
                badge = this.$node.find('.badge');

            if (!badge.length) {
                badge = $('').insertAfter(node)
            }

            workspaceService.diff()
                .fail(function() {
                    badge.removePrefixedClasses('badge-').addClass('badge-important')
                        .attr('title', 'An error occured')
                        .text('!');
                })
                .done(function(diff) {
                    var count = diff.diffs.length,
                        formattedCount = formatters.number.pretty(count);

                    require(['workspaces/diff/diff'], function(Diff) {
                        var popover = badge.data('popover'),
                            tip = popover && popover.tip();

                        if (tip && tip.is(':visible')) {
                            self.trigger(popover.tip().find('.popover-content'), 'diffsChanged', { diffs: diff.diffs });
                        } else {
                            badge
                                .popover('destroy')
                                .popover({placement:'top', content:'Loading...', title: 'Unpublished Changes'})

                            popover = badge.data('popover');
                            tip = popover.tip();

                            tip.find('.arrow').css({
                                left: badge.position().left,
                                marginLeft: 0
                            })

                            // We fill in our own content
                            popover.setContent = function() {}

                            Diff.teardownAll();
                            Diff.attachTo(tip.find('.popover-content'), {
                                diffs: diff.diffs
                            });
                        }
                    });

                    badge.removePrefixedClasses('badge-').addClass('badge-info')
                        .attr('title', formatters.string.plural(formattedCount, 'unpublished change'))
                        .text(count > 0 ? formattedCount : '');
                })
        };

        this.updateUserTooltip = function(data) {
            if (data && data.user) {
                this.select('userSelector').text(data.user.userName)
                    .tooltip('destroy')
                    .tooltip({
                        placement: 'right',
                        html: true,
                        title: 'Authorizations: ' + (data.user.authorizations.join(', ') || 'none') + '',
                        trigger: 'hover',
                        delay: { show:500, hide:0 }
                    })
            }
        }

        this.updateWorkspaceTooltip = function(data) {
            if (data && data.data && data.data.vertices) {
                this.verticesCount = data.data.vertices.length;
            }
            if (this.verticesCount === 0) {
                this.edgesCount = 0;
            } else if (data.relationships) {
                this.edgesCount = data.relationships.length;
            } else {
                this.edgesCount = $('.cytoscape-container').cytoscape('get').edges().length;
            }

            var name = this.select('nameSelector'),
                tooltip = name.data('tooltip'),
                tip = tooltip && tooltip.tip(),
                text = 'Vertices: ' + formatters.number.pretty(this.verticesCount || 0) +
                    ', Edges: ' + formatters.number.pretty(this.edgesCount || 0)

            if (tip && tip.is(':visible')) {
                tip.find('.tooltip-inner span').text(text);
            } else {
                name
                    .tooltip('destroy')
                    .tooltip({
                        placement: 'right',
                        html: true,
                        title: '' + text + '',
                        trigger: 'hover',
                        delay: { show:500, hide:0 }
                    });
            }

        }
    }
});
<commit_msg>Filter diff view entries about with underscore properties<commit_after>
define([
    'flight/lib/component',
    'tpl!./overlay',
    'util/formatters',
    'service/workspace'
], function(defineComponent, template, formatters, WorkspaceService) {
    'use strict';

    var LAST_SAVED_UPDATE_FREQUENCY_SECONDS = 30;
    var MENUBAR_WIDTH = 30;
    var UPDATE_WORKSPACE_DIFF_SECONDS = 5;

    return defineComponent(WorkspaceOverlay);

    function WorkspaceOverlay() {

        var workspaceService = new WorkspaceService();

        this.defaultAttrs({
            userSelector: '.user',
            nameSelector: '.name',
            subtitleSelector: '.subtitle'
        });

        this.after('initialize', function() {
            var self = this;

            this.userDeferred = $.Deferred();
            this.workspaceDeferred = $.Deferred();
            this.updateDiffBadge = _.throttle(this.updateDiffBadge.bind(this), UPDATE_WORKSPACE_DIFF_SECONDS * 1000)

            $.when(this.userDeferred, this.workspaceDeferred).done(function() {
                self.$node.show();

                requestAnimationFrame(function() {
                    self.$node.addClass('visible');
                });
            })

            this.$node.hide().html(template({}));

            this.on(document, 'workspaceSaving', this.onWorkspaceSaving);
            this.on(document, 'workspaceSaved', this.onWorkspaceSaved);
            this.on(document, 'workspaceLoaded', this.onWorkspaceLoaded);
            this.on(document, 'switchWorkspace', this.onSwitchWorkspace);
            this.on(document, 'graphPaddingUpdated', this.onGraphPaddingUpdated);
            this.on(document, 'currentUserChanged', this.onCurrentUserChanged)
            this.on(document, 'relationshipsLoaded', this.onRelationshipsLoaded)
            this.on(document, 'ajaxComplete', this.onAjaxComplete);
        });

        this.onAjaxComplete = function(event, xhr, settings) {
            // Automatically call diff after every POST
            if (/post/i.test(settings.type)) {
                this.updateDiffBadge();
            }
        };

        this.onCurrentUserChanged = function(event, data) {
            this.userDeferred.resolve();
            this.updateUserTooltip(data);
        }

        this.onGraphPaddingUpdated = function(event, data) {
            this.$node.css('left', data.padding.l + MENUBAR_WIDTH);
        };

        this.setContent = function(title, isEditable, subtitle) {
            this.select('nameSelector').text(title);
            this.select('subtitleSelector').html(isEditable === false ? 'read only' : subtitle);
        };

        this.onSwitchWorkspace = function() {
            this.$node.find('.badge').remove();
        };

        this.onWorkspaceLoaded = function(event, data) {
            this.workspaceDeferred.resolve();
            this.setContent(data.title, data.isEditable, 'no changes');
            clearTimeout(this.updateTimer);
            this.updateWorkspaceTooltip(data);
            this.updateDiffBadge();
        };

        this.onRelationshipsLoaded = function(event, data) {
            this.updateWorkspaceTooltip(data);
        };

        this.onWorkspaceSaving = function(event, data) {
            this.select('subtitleSelector').text('saving...');
            clearTimeout(this.updateTimer);
            this.updateWorkspaceTooltip(data);
        };

        this.onWorkspaceSaved = function(event, data) {
            clearTimeout(this.updateTimer);
            this.lastSaved = formatters.date.utc(Date.now());

            if (data.title) {
                this.select('nameSelector').text(data.title);
            }

            this.updateWorkspaceTooltip(data);

            var prefix = 'last saved ',
                subtitle = this.select('subtitleSelector').text(prefix + 'moments ago'),
                setTimer = function() {
                    this.updateTimer = setTimeout(function () {

                        var time = formatters.date.relativeToNow(this.lastSaved);
                        subtitle.text(prefix + time);

                        setTimer();
                    }.bind(this), LAST_SAVED_UPDATE_FREQUENCY_SECONDS * 1000);
                }.bind(this);

            setTimer();
        };

        this.updateDiffBadge = function() {
            var self = this,
                node = this.select('nameSelector'),
                badge = this.$node.find('.badge');

            if (!badge.length) {
                badge = $('').insertAfter(node)
            }

            workspaceService.diff()
                .fail(function() {
                    badge.removePrefixedClasses('badge-').addClass('badge-important')
                        .attr('title', 'An error occured')
                        .text('!');
                })
                .done(function(diff) {
                    var filteredDiffs = diff.diffs.filter(function(diff) {
                            return !diff.name || /^[^_]/.test(diff.name);
                        }),
                        count = filteredDiffs.length,
                        formattedCount = formatters.number.pretty(count);

                    require(['workspaces/diff/diff'], function(Diff) {
                        var popover = badge.data('popover'),
                            tip = popover && popover.tip();

                        if (tip && tip.is(':visible')) {
                            self.trigger(popover.tip().find('.popover-content'), 'diffsChanged', { diffs: filteredDiffs });
                        } else {
                            badge
                                .popover('destroy')
                                .popover({placement:'top', content:'Loading...', title: 'Unpublished Changes'})

                            popover = badge.data('popover');
                            tip = popover.tip();

                            tip.find('.arrow').css({
                                left: badge.position().left,
                                marginLeft: 0
                            })

                            // We fill in our own content
                            popover.setContent = function() {}

                            Diff.teardownAll();
                            Diff.attachTo(tip.find('.popover-content'), {
                                diffs: filteredDiffs
                            });
                        }
                    });

                    badge.removePrefixedClasses('badge-').addClass('badge-info')
                        .attr('title', formatters.string.plural(formattedCount, 'unpublished change'))
                        .text(count > 0 ? formattedCount : '');
                })
        };

        this.updateUserTooltip = function(data) {
            if (data && data.user) {
                this.select('userSelector').text(data.user.userName)
                    .tooltip('destroy')
                    .tooltip({
                        placement: 'right',
                        html: true,
                        title: 'Authorizations: ' + (data.user.authorizations.join(', ') || 'none') + '',
                        trigger: 'hover',
                        delay: { show:500, hide:0 }
                    })
            }
        }

        this.updateWorkspaceTooltip = function(data) {
            if (data && data.data && data.data.vertices) {
                this.verticesCount = data.data.vertices.length;
            }
            if (this.verticesCount === 0) {
                this.edgesCount = 0;
            } else if (data.relationships) {
                this.edgesCount = data.relationships.length;
            } else {
                this.edgesCount = $('.cytoscape-container').cytoscape('get').edges().length;
            }

            var name = this.select('nameSelector'),
                tooltip = name.data('tooltip'),
                tip = tooltip && tooltip.tip(),
                text = 'Vertices: ' + formatters.number.pretty(this.verticesCount || 0) +
                    ', Edges: ' + formatters.number.pretty(this.edgesCount || 0)

            if (tip && tip.is(':visible')) {
                tip.find('.tooltip-inner span').text(text);
            } else {
                name
                    .tooltip('destroy')
                    .tooltip({
                        placement: 'right',
                        html: true,
                        title: '' + text + '',
                        trigger: 'hover',
                        delay: { show:500, hide:0 }
                    });
            }

        }
    }
});
<|endoftext|>


<commit_before>import {h, Component} 					from 'preact/preact';

import Notification, {
	isNotificationComment,
	isNotificationFeedback,
	isNotificationFriendGame,
	isNotificationFriendPost,
	isNotificationMention,
	isNotificationOther
}										from 'com/content-notifications/notification';

import $Node							from 'shrub/js/node/node';
import $Comment							from 'shrub/js/comment/comment';
import $Notification					from 'shrub/js/notification/notification';

export default class NotificationsBase extends Component {
	constructor( props ) {
		super(props);

		this.state = {
			'notifications': null,
			'notificationIds': [],
			'notificationsTotal': -1,
			'count': 0,
			'status': null,
			'feed': [],
			'filtered': null,
			'loading': true,
			'highestRead': -1,
		};
		this.handleFilterChange = this.handleFilterChange.bind(this);
	}

	clearNotifications() {
		this.setState({
			'notifications': null,
			'notificationIds': [],
			'notificationsTotal': -1,
			'count': 0,
			'status': null,
			'feed': [],
			'filtered': null,
			'loading': true,
			'highestRead': -1,
		});
	}

	hasUnreadNotifications() {
		const highestInFeed = this.getHighestNotificationInFeed();
		if ( highestInFeed !== null ) {
			if ( highestInFeed > this.state.highestRead ) {
				return true;
			}
		}
		return false;
	}

	markReadHighest() {
		// TODO: Triggering this should immidiately update the counter on the
		// icon in the top bar
		if ( this.hasUnreadNotifications() ) {
			const highestInFeed = this.getHighestNotificationInFeed();
			$Notification.SetMarkRead(highestInFeed).then((r) => {
				if ( r.status == 200 ) {
					this.setState({'highestRead': highestInFeed});
				}
			});
		}
	}

	getHighestNotificationInFeed() {
		const notificationsOrder = this.getNotificationsOrder();
		const {filtered} = this.state;
		let highestFiltered = -1;
		if ( filtered ) {
			for ( let i = 0; i < filtered.length; i++ ) {
				const nid = filtered[i].id;
				if ( nid > highestFiltered ) {
					highestFiltered = nid;
				}
			}
		}
		if ( notificationsOrder && (notificationsOrder.length > 0) ) {
			return Math.max(notificationsOrder[0], highestFiltered);
		}
		else if ( highestFiltered > -1 ) {
			return highestFiltered;
		}
		return null;
	}

	processNotificationFeed( r ) {
		const callerID = r.caller_id;
		this.collectAllNodesAndNodes(r.feed, callerID);
		let highestRead = (r.max_read !== undefined) ? r.max_read : this.state.highestRead;
		this.setState({
			'feed': r.feed,
			'filtered': r.filtered,
			'status': r.status,
			'count': r.count,
			'loading': true,
			'highestRead': highestRead,
		});
	}

	collectAllNodesAndNodes( feed, callerID ) {
		let nodeLookup = new Map();
		let nodes = [];
		let notificationLookup = new Map();
		let social = {};

		let soicialPromise = $Node.GetMy().then((response) => {
			social.following = response.meta.star ? response.meta.star : [];
			social.followers = response.refs.star ? response.refs.star : [];
			social.friends = social.followers.filter((i) => social.following.indexOf(i) > -1);
		});

		feed.forEach((notification) => {
			if ( nodes.indexOf(notification.node) < 0 ) {
				nodes.push(notification.node);
			}
			notificationLookup.set(notification.id, notification);
		});

		let node2notes = new Map();
		let notification2nodeAndNote = new Map();
		let noteLookup = new Map();
		let users = [];
		let usersLookup = new Map();

		users.push(callerID);
		let notes = [];

		feed.forEach(({id, node, note}) => {
			if ( note ) {
				// Only fetch nonzero notes. Don't add zero notes to the list of notes in a node
				if ( node2notes.has(node) ) {
					node2notes.get(node).push(note);
				}
				else {
					node2notes.set(node, [note]);
				}
				notes.push(note);
				notification2nodeAndNote.set(id, {'node': node, 'note': note});
			}
		});

		let nodesPromise = $Node.Get(nodes)
		.then((response) => {
			//console.log('[Notifications:Nodes]', response.node);
			if ( response.node ) {
				response.node.forEach((node) => {
					node.authors = [];
					if ( node.author > 0 ) {
						if ( users.indexOf(node.author) < 0 ) {
							users.push(node.author);
						}
						node.authors.push(node.author);
					}
					if ( node.meta && node.meta.author ) {
						node.meta.author.forEach((author) => {
							if ( (author > 0) && (users.indexOf(author) < 0) ) {
								users.push(author);
							}
							if ( (author > 0) && (node.authors.indexOf(author) < 0) ) {
								node.authors.push(author);
							}
						});
					}
					nodeLookup.set(node.id, node);
				});
			}

		});

		let notesPromise = $Comment.Get(notes).then((response) => {
			//console.log('[Notifications:Notes]', response.note);
			if ( response.comment ) {
				response.comment.forEach((note) => {
					noteLookup.set(note.id, note);

					if ( (note.author > 0) && (users.indexOf(note.author) < 0) ) {
						users.push(note.author);
					}
				});
			}
		});

		Promise.all([nodesPromise, notesPromise, soicialPromise])
			.then(() => {
				noteLookup.forEach((note, id) => {
					if ( note.node ) {
						note.isNodeAuthor = nodeLookup.get(note.node).authors.indexOf(note.author) > -1;
					}
				});
				return $Node.Get(users);
			})
			.then((response) => {
				//console.log('[Notifications:Users]', response.node);
				if ( response.node ) {
					response.node.forEach((node) => {
						if ( node.type == 'user' ) {
							node.isFriend = social.friends.indexOf(node.id) > -1;
							node.isFollowing = node.isFriend && social.following.indexOf(node.id) > -1;
							node.isFollower = node.isFriend && social.followers.indexOf(node.id) > -1;

							usersLookup.set(node.id, node);
						}
					});
				}

				this.composeNotifications(feed, callerID, notification2nodeAndNote, node2notes, nodeLookup, noteLookup, usersLookup, notificationLookup, social);
			});
	}

	composeNotifications( feed, callerID, notification2nodeAndNote, node2notes, nodeLookup, noteLookup, usersLookup, notificationLookup, social ) {

		const myAtName = '@' + usersLookup.get(callerID).name;
		noteLookup.forEach((note, id) => {
			note.selfauthored = note.author == callerID;
			note.mention = note.body.indexOf(myAtName) >= 0;
		});

		nodeLookup.forEach((node, id) => {
			node.selfauthored = false;
			if ( node.author == callerID ) {
				node.selfauthored = true;
			}
			else if ( node.meta && node.meta.author ) {
				node.meta.author.forEach((author) => {
					if ( author == callerID ) {
						node.selfauthored = true;
					}
				});
			}
			node.mention = (node.body.indexOf(myAtName) >= 0) || (node.name.indexOf(myAtName) >= 0);
		});

		let notifications = this.state.notifications ? this.state.notifications : new Map();
		let notificationIds = this.state.notificationIds;

		let processedNotifications = [];

		let previousData = null;

		feed.forEach((notification) => {
			if ( processedNotifications.indexOf(notification.id) < 0 ) {
				let node = nodeLookup.get(notification.node);
				let data = {
					'node': node,
					'note': undefined,
					'time': new Date(notification.created).getTime(),
					'earliestNoteId': undefined, // Track the note id to link to for this notification.
					'unread': notification.id > this.state.highestRead,
					'notification': [notification],
					'mergeable': false,
					'multi': false,
					'users': new Map(),
					'social': social,
					'callerID': callerID,
				};

				// Look up user records for self and node author.
				data.users.set(callerID, usersLookup.get(callerID));
				data.users.set(node.author, usersLookup.get(node.author));

				// Look up other authors for the node (for team games)
				if ( node.meta && node.meta.author ) {
					node.meta.author.forEach((author) => {
						if ( !data.users.has(author) ) {
							data.users.set(author, usersLookup.get(author));
						}
					});
				}

				// Add author for note, check if this notification be merged
				// Allow merge if it's a note, and if it doesn't contain a mention, isn't self authored, and isn't written by the note author.
				if ( notification.note ) {
					let note = noteLookup.get(notification.note);
					data.note = [note];
					data.earliestNote = notification.note;
					data.users.set(note.author, usersLookup.get(note.author));
					if ( !note.mention && !note.selfauthored && !note.isNodeAuthor ) {
						data.mergeable = true;
					}
				}

				// Will this notification merge with the previous notification?
				let doMerge = false;
				if ( previousData && data.mergeable && previousData.mergeable ) {
					// We can only merge if they're both on the same node, both on the same side of the 'read' boundary, and within a certain amount of time.
					if ( (notification.node == previousData.notification[0].node) && (data.unread == previousData.unread) ) {
						let MaximumMergeTime = 4*60*60*1000; // 4 hours in milliseconds
						if ( Math.abs(data.time - previousData.time) < MaximumMergeTime ) {
							doMerge = true;
						}
					}
				}

				let notificationId = data.notification[0].id;
				processedNotifications.push(notification.id);

				// Skip processing if notification is malformed (note.node == 0)
				if ( data.note && (data.note[0].node == 0) ) {
					// Don't use this notification
				}
				else if ( doMerge ) {
					// Roll current notification into previousData
					previousData.multi = true;
					previousData.notification.push(notification);

					// Add this note and note's author to the previousData lists.
					let note = noteLookup.get(notification.note);
					previousData.note.push(note);
					previousData.users.set(note.author, usersLookup.get(note.author));
					previousData.earliestNote = Math.min(previousData.earliestNote, notification.note);
				}
				else {
					// Not merged into another notification, just add the data structure to output.

					notifications.set(notificationId, data);
					previousData = data;
					notificationIds.push(notificationId);
				}

			}
		});

		this.setState({
			'notifications': notifications,
			'notificationIds': notificationIds.sort((a, b) => b - a),
			'loading': false,
			'feedSize': (this.state.feedSize ? this.state.feedSize : 0) + processedNotifications.length,
		});
	}

	updateCallback() {
		//For subclass to do stuff with
	}

	isLoading() {
		return this.state.loading;
	}

	getNotificationsOrder() {
		return this.state.notificationIds;
	}

	getNotifications( maxCount, clickCallback ) {
		let Notifications = [];
		const notifications = this.state.notifications;
		const notificationsOrder = this.getNotificationsOrder();
		if ( !this.state.loading ) {

			notificationsOrder.forEach((id) => {
				let notification = notifications.get(id);
				if ( (maxCount > 0) && this.shouldShowNotification(notification) ) {
					Notifications.push([id, <Notification notification={notification} onclick={clickCallback} />]);
					maxCount -= 1;
				}
			});

		}

		return Notifications;
	}

	shouldShowNotification( notification ) {
		const {Mention, FriendGame, FriendPost, Feedback, Comment, Other} = $Notification.GetFilters();
		if ( (Feedback !== false) && isNotificationFeedback(notification) ) {
			return true;
		}
		else if ( (Mention !== false) && isNotificationMention(notification) ) {
			return true;
		}
		else if ( (FriendGame !== false) && isNotificationFriendGame(notification) ) {
			return true;
		}
		else if ( (FriendPost !== false) && isNotificationFriendPost(notification) ) {
			return true;
		}
		else if ( (Comment !== false) && isNotificationComment(notification) ) {
			return true;
		}
		return (Other !== false) && isNotificationOther(notification);
	}

	handleFilterChange( filterType, otherStuff ) {
		const myFilter = $Notification.GetFilters();
		myFilter[filterType] = (myFilter[filterType] === undefined) ? false : !myFilter[filterType];
		$Notification.SetFilters(myFilter);
		this.setState({'filters': isFinite(this.state.filters) ? (this.state.filters + 1) : 1});
		//this.setState({'filters': myFilter});
	}
}
<commit_msg>fix perhaps<commit_after>import {h, Component} 					from 'preact/preact';

import Notification, {
	isNotificationComment,
	isNotificationFeedback,
	isNotificationFriendGame,
	isNotificationFriendPost,
	isNotificationMention,
	isNotificationOther
}										from 'com/content-notifications/notification';

import $Node							from 'shrub/js/node/node';
import $Comment							from 'shrub/js/comment/comment';
import $Notification					from 'shrub/js/notification/notification';

export default class NotificationsBase extends Component {
	constructor( props ) {
		super(props);

		this.state = {
			'notifications': null,
			'notificationIds': [],
			'notificationsTotal': -1,
			'count': 0,
			'status': null,
			'feed': [],
			'filtered': null,
			'loading': true,
			'highestRead': -1,
		};
		this.handleFilterChange = this.handleFilterChange.bind(this);
	}

	clearNotifications() {
		this.setState({
			'notifications': null,
			'notificationIds': [],
			'notificationsTotal': -1,
			'count': 0,
			'status': null,
			'feed': [],
			'filtered': null,
			'loading': true,
			'highestRead': -1,
		});
	}

	hasUnreadNotifications() {
		const highestInFeed = this.getHighestNotificationInFeed();
		if ( highestInFeed !== null ) {
			if ( highestInFeed > this.state.highestRead ) {
				return true;
			}
		}
		return false;
	}

	markReadHighest() {
		// TODO: Triggering this should immidiately update the counter on the
		// icon in the top bar
		if ( this.hasUnreadNotifications() ) {
			const highestInFeed = this.getHighestNotificationInFeed();
			$Notification.SetMarkRead(highestInFeed).then((r) => {
				if ( r.status == 200 ) {
					this.setState({'highestRead': highestInFeed});
				}
			});
		}
	}

	getHighestNotificationInFeed() {
		const notificationsOrder = this.getNotificationsOrder();
		const {filtered} = this.state;
		let highestFiltered = -1;
		if ( filtered ) {
			for ( let i = 0; i < filtered.length; i++ ) {
				const nid = filtered[i].id;
				if ( nid > highestFiltered ) {
					highestFiltered = nid;
				}
			}
		}
		if ( notificationsOrder && (notificationsOrder.length > 0) ) {
			return Math.max(notificationsOrder[0], highestFiltered);
		}
		else if ( highestFiltered > -1 ) {
			return highestFiltered;
		}
		return null;
	}

	processNotificationFeed( r ) {
		if (r == null) return;
		const callerID = r.caller_id;
		this.collectAllNodesAndNodes(r.feed, callerID);
		let highestRead = (r.max_read !== undefined) ? r.max_read : this.state.highestRead;
		this.setState({
			'feed': r.feed,
			'filtered': r.filtered,
			'status': r.status,
			'count': r.count,
			'loading': true,
			'highestRead': highestRead,
		});
	}

	collectAllNodesAndNodes( feed, callerID ) {
		let nodeLookup = new Map();
		let nodes = [];
		let notificationLookup = new Map();
		let social = {};

		let soicialPromise = $Node.GetMy().then((response) => {
			social.following = response.meta.star ? response.meta.star : [];
			social.followers = response.refs.star ? response.refs.star : [];
			social.friends = social.followers.filter((i) => social.following.indexOf(i) > -1);
		});

		feed.forEach((notification) => {
			if ( nodes.indexOf(notification.node) < 0 ) {
				nodes.push(notification.node);
			}
			notificationLookup.set(notification.id, notification);
		});

		let node2notes = new Map();
		let notification2nodeAndNote = new Map();
		let noteLookup = new Map();
		let users = [];
		let usersLookup = new Map();

		users.push(callerID);
		let notes = [];

		feed.forEach(({id, node, note}) => {
			if ( note ) {
				// Only fetch nonzero notes. Don't add zero notes to the list of notes in a node
				if ( node2notes.has(node) ) {
					node2notes.get(node).push(note);
				}
				else {
					node2notes.set(node, [note]);
				}
				notes.push(note);
				notification2nodeAndNote.set(id, {'node': node, 'note': note});
			}
		});

		let nodesPromise = $Node.Get(nodes)
		.then((response) => {
			//console.log('[Notifications:Nodes]', response.node);
			if ( response.node ) {
				response.node.forEach((node) => {
					node.authors = [];
					if ( node.author > 0 ) {
						if ( users.indexOf(node.author) < 0 ) {
							users.push(node.author);
						}
						node.authors.push(node.author);
					}
					if ( node.meta && node.meta.author ) {
						node.meta.author.forEach((author) => {
							if ( (author > 0) && (users.indexOf(author) < 0) ) {
								users.push(author);
							}
							if ( (author > 0) && (node.authors.indexOf(author) < 0) ) {
								node.authors.push(author);
							}
						});
					}
					nodeLookup.set(node.id, node);
				});
			}

		});

		let notesPromise = $Comment.Get(notes).then((response) => {
			//console.log('[Notifications:Notes]', response.note);
			if ( response.comment ) {
				response.comment.forEach((note) => {
					noteLookup.set(note.id, note);

					if ( (note.author > 0) && (users.indexOf(note.author) < 0) ) {
						users.push(note.author);
					}
				});
			}
		});

		Promise.all([nodesPromise, notesPromise, soicialPromise])
			.then(() => {
				noteLookup.forEach((note, id) => {
					if ( note.node ) {
						note.isNodeAuthor = nodeLookup.get(note.node).authors.indexOf(note.author) > -1;
					}
				});
				return $Node.Get(users);
			})
			.then((response) => {
				//console.log('[Notifications:Users]', response.node);
				if ( response.node ) {
					response.node.forEach((node) => {
						if ( node.type == 'user' ) {
							node.isFriend = social.friends.indexOf(node.id) > -1;
							node.isFollowing = node.isFriend && social.following.indexOf(node.id) > -1;
							node.isFollower = node.isFriend && social.followers.indexOf(node.id) > -1;

							usersLookup.set(node.id, node);
						}
					});
				}

				this.composeNotifications(feed, callerID, notification2nodeAndNote, node2notes, nodeLookup, noteLookup, usersLookup, notificationLookup, social);
			});
	}

	composeNotifications( feed, callerID, notification2nodeAndNote, node2notes, nodeLookup, noteLookup, usersLookup, notificationLookup, social ) {

		const myAtName = '@' + usersLookup.get(callerID).name;
		noteLookup.forEach((note, id) => {
			note.selfauthored = note.author == callerID;
			note.mention = note.body.indexOf(myAtName) >= 0;
		});

		nodeLookup.forEach((node, id) => {
			node.selfauthored = false;
			if ( node.author == callerID ) {
				node.selfauthored = true;
			}
			else if ( node.meta && node.meta.author ) {
				node.meta.author.forEach((author) => {
					if ( author == callerID ) {
						node.selfauthored = true;
					}
				});
			}
			node.mention = (node.body.indexOf(myAtName) >= 0) || (node.name.indexOf(myAtName) >= 0);
		});

		let notifications = this.state.notifications ? this.state.notifications : new Map();
		let notificationIds = this.state.notificationIds;

		let processedNotifications = [];

		let previousData = null;

		feed.forEach((notification) => {
			if ( processedNotifications.indexOf(notification.id) < 0 ) {
				let node = nodeLookup.get(notification.node);
				let data = {
					'node': node,
					'note': undefined,
					'time': new Date(notification.created).getTime(),
					'earliestNoteId': undefined, // Track the note id to link to for this notification.
					'unread': notification.id > this.state.highestRead,
					'notification': [notification],
					'mergeable': false,
					'multi': false,
					'users': new Map(),
					'social': social,
					'callerID': callerID,
				};

				// Look up user records for self and node author.
				data.users.set(callerID, usersLookup.get(callerID));
				data.users.set(node.author, usersLookup.get(node.author));

				// Look up other authors for the node (for team games)
				if ( node.meta && node.meta.author ) {
					node.meta.author.forEach((author) => {
						if ( !data.users.has(author) ) {
							data.users.set(author, usersLookup.get(author));
						}
					});
				}

				// Add author for note, check if this notification be merged
				// Allow merge if it's a note, and if it doesn't contain a mention, isn't self authored, and isn't written by the note author.
				if ( notification.note ) {
					let note = noteLookup.get(notification.note);
					data.note = [note];
					data.earliestNote = notification.note;
					data.users.set(note.author, usersLookup.get(note.author));
					if ( !note.mention && !note.selfauthored && !note.isNodeAuthor ) {
						data.mergeable = true;
					}
				}

				// Will this notification merge with the previous notification?
				let doMerge = false;
				if ( previousData && data.mergeable && previousData.mergeable ) {
					// We can only merge if they're both on the same node, both on the same side of the 'read' boundary, and within a certain amount of time.
					if ( (notification.node == previousData.notification[0].node) && (data.unread == previousData.unread) ) {
						let MaximumMergeTime = 4*60*60*1000; // 4 hours in milliseconds
						if ( Math.abs(data.time - previousData.time) < MaximumMergeTime ) {
							doMerge = true;
						}
					}
				}

				let notificationId = data.notification[0].id;
				processedNotifications.push(notification.id);

				// Skip processing if notification is malformed (note.node == 0)
				if ( data.note && (data.note[0].node == 0) ) {
					// Don't use this notification
				}
				else if ( doMerge ) {
					// Roll current notification into previousData
					previousData.multi = true;
					previousData.notification.push(notification);

					// Add this note and note's author to the previousData lists.
					let note = noteLookup.get(notification.note);
					previousData.note.push(note);
					previousData.users.set(note.author, usersLookup.get(note.author));
					previousData.earliestNote = Math.min(previousData.earliestNote, notification.note);
				}
				else {
					// Not merged into another notification, just add the data structure to output.

					notifications.set(notificationId, data);
					previousData = data;
					notificationIds.push(notificationId);
				}

			}
		});

		this.setState({
			'notifications': notifications,
			'notificationIds': notificationIds.sort((a, b) => b - a),
			'loading': false,
			'feedSize': (this.state.feedSize ? this.state.feedSize : 0) + processedNotifications.length,
		});
	}

	updateCallback() {
		//For subclass to do stuff with
	}

	isLoading() {
		return this.state.loading;
	}

	getNotificationsOrder() {
		return this.state.notificationIds;
	}

	getNotifications( maxCount, clickCallback ) {
		let Notifications = [];
		const notifications = this.state.notifications;
		const notificationsOrder = this.getNotificationsOrder();
		if ( !this.state.loading ) {

			notificationsOrder.forEach((id) => {
				let notification = notifications.get(id);
				if ( (maxCount > 0) && this.shouldShowNotification(notification) ) {
					Notifications.push([id, <Notification notification={notification} onclick={clickCallback} />]);
					maxCount -= 1;
				}
			});

		}

		return Notifications;
	}

	shouldShowNotification( notification ) {
		const {Mention, FriendGame, FriendPost, Feedback, Comment, Other} = $Notification.GetFilters();
		if ( (Feedback !== false) && isNotificationFeedback(notification) ) {
			return true;
		}
		else if ( (Mention !== false) && isNotificationMention(notification) ) {
			return true;
		}
		else if ( (FriendGame !== false) && isNotificationFriendGame(notification) ) {
			return true;
		}
		else if ( (FriendPost !== false) && isNotificationFriendPost(notification) ) {
			return true;
		}
		else if ( (Comment !== false) && isNotificationComment(notification) ) {
			return true;
		}
		return (Other !== false) && isNotificationOther(notification);
	}

	handleFilterChange( filterType, otherStuff ) {
		const myFilter = $Notification.GetFilters();
		myFilter[filterType] = (myFilter[filterType] === undefined) ? false : !myFilter[filterType];
		$Notification.SetFilters(myFilter);
		this.setState({'filters': isFinite(this.state.filters) ? (this.state.filters + 1) : 1});
		//this.setState({'filters': myFilter});
	}
}
<|endoftext|>


<commit_before>/* global chrome */
function testStorageClear (callback) {
  chrome.storage.sync.clear(function () {
    chrome.storage.sync.get(null, function (syncItems) {
      chrome.storage.local.clear(function () {
        chrome.storage.local.get(null, function (localItems) {
          callback(syncItems, localItems);
        });
      });
    });
  });
}

function testStorageRemove (callback) {
  chrome.storage.sync.remove('bar', function () {
    chrome.storage.sync.get({ foo: 'baz' }, function (syncItems) {
      chrome.storage.local.remove(['hello'], function () {
        chrome.storage.local.get(null, function (localItems) {
          callback(syncItems, localItems);
        });
      });
    });
  });
}

function testStorageSet (callback) {
  chrome.storage.sync.set({ foo: 'bar', bar: 'foo' }, function () {
    chrome.storage.sync.get({ foo: 'baz', bar: 'fooo' }, function (syncItems) {
      chrome.storage.local.set({ hello: 'world', world: 'hello' }, function () {
        chrome.storage.local.get(null, function (localItems) {
          callback(syncItems, localItems);
        });
      });
    });
  });
}

function testStorage (callback) {
  testStorageSet(function (syncForSet, localForSet) {
    testStorageRemove(function (syncForRemove, localForRemove) {
      testStorageClear(function (syncForClear, localForClear) {
        callback(
          syncForSet, localForSet,
          syncForRemove, localForRemove,
          syncForClear, localForClear
        );
      });
    });
  });
}

testStorage(function (
  syncForSet, localForSet,
  syncForRemove, localForRemove,
  syncForClear, localForClear
) {
  const message = JSON.stringify({
    runtimeId: chrome.runtime.id,
    tabId: chrome.devtools.inspectedWindow.tabId,
    i18nString: chrome.i18n.getMessage('foo', ['bar', 'baz']),
    storageItems: {
      local: {
        set: localForSet,
        remove: localForRemove,
        clear: localForClear
      },
      sync: {
        set: syncForSet,
        remove: syncForRemove,
        clear: syncForClear
      }
    }
  });

  const sendMessage = `require('electron').ipcRenderer.send('answer', ${message})`;
  window.chrome.devtools.inspectedWindow.eval(sendMessage, function () {});
});
<commit_msg>test: deflake <webview> tag loads devtools extensions on WOA (#31702)<commit_after>/* global chrome */
function testStorageClear (callback) {
  chrome.storage.sync.clear(function () {
    chrome.storage.sync.get(null, function (syncItems) {
      chrome.storage.local.clear(function () {
        chrome.storage.local.get(null, function (localItems) {
          callback(syncItems, localItems);
        });
      });
    });
  });
}

function testStorageRemove (callback) {
  chrome.storage.sync.remove('bar', function () {
    chrome.storage.sync.get({ foo: 'baz' }, function (syncItems) {
      chrome.storage.local.remove(['hello'], function () {
        chrome.storage.local.get(null, function (localItems) {
          callback(syncItems, localItems);
        });
      });
    });
  });
}

function testStorageSet (callback) {
  chrome.storage.sync.set({ foo: 'bar', bar: 'foo' }, function () {
    chrome.storage.sync.get({ foo: 'baz', bar: 'fooo' }, function (syncItems) {
      chrome.storage.local.set({ hello: 'world', world: 'hello' }, function () {
        chrome.storage.local.get(null, function (localItems) {
          callback(syncItems, localItems);
        });
      });
    });
  });
}

function testStorage (callback) {
  testStorageSet(function (syncForSet, localForSet) {
    testStorageRemove(function (syncForRemove, localForRemove) {
      testStorageClear(function (syncForClear, localForClear) {
        callback(
          syncForSet, localForSet,
          syncForRemove, localForRemove,
          syncForClear, localForClear
        );
      });
    });
  });
}

testStorage(function (
  syncForSet, localForSet,
  syncForRemove, localForRemove,
  syncForClear, localForClear
) {
  setTimeout(() => {
    const message = JSON.stringify({
      runtimeId: chrome.runtime.id,
      tabId: chrome.devtools.inspectedWindow.tabId,
      i18nString: chrome.i18n.getMessage('foo', ['bar', 'baz']),
      storageItems: {
        local: {
          set: localForSet,
          remove: localForRemove,
          clear: localForClear
        },
        sync: {
          set: syncForSet,
          remove: syncForRemove,
          clear: syncForClear
        }
      }
    });

    const sendMessage = `require('electron').ipcRenderer.send('answer', ${message})`;
    window.chrome.devtools.inspectedWindow.eval(sendMessage, function () {});
  });
});
<|endoftext|>


<commit_before>'use strict';

/* eslint-env node */

const leadingWhiteSpace = /^[ \t\r\n]+/;
const trailingWhiteSpace = /[ \t\r\n]+$/;
const WHITESPACE = /^[ \t\r\n]+$/;

function createGlimmerPlugin(config) {
  // in this stack we track the nodes that cause us to skip the minification
  // e.g. `{{#no-minify}} ... {{/no-minify}}` blocks or `<pre></pre>` tags
  // depending on the configuration
  let skipStack = [];

  function insideSkipBlock() {
    return skipStack.length !== 0;
  }

  return {
    name: 'hbs-minifier-plugin',

    visitor: {
      TextNode(node) {
        if (!insideSkipBlock()) {
          // replace leading and trailing whitespace with a single whitespace character
          node.chars = node.chars.replace(leadingWhiteSpace, ' ').replace(trailingWhiteSpace, ' ');
        }
      },

      BlockStatement: {
        enter(node) {
          if (shouldSkipBlockStatement(node, config)) {
            skipStack.push(node);
          }
        },

        exit(node) {
          if (skipStack[skipStack.length - 1] === node) {
            skipStack.pop();
          }
        },
      },

      Program: {
        enter(node) {
          if (insideSkipBlock()) {
            return;
          }

          let firstChild = node.body[0];
          if (isWhitespaceTextNode(firstChild)) {
            node.body.shift();
          }

          let lastChild = node.body[node.body.length - 1];
          if (isWhitespaceTextNode(lastChild)) {
            node.body.pop();
          }
        },

        exit(node) {
          node.body = stripNoMinifyBlocks(node.body);
        },
      },

      ElementNode: {
        enter(node) {
          if (shouldSkipElementNode(node, config)) {
            skipStack.push(node);
          }

          if (insideSkipBlock()) {
            return;
          }

          let firstChild = node.children[0];
          if (isWhitespaceTextNode(firstChild)) {
            node.children.shift();
          }

          let lastChild = node.children[node.children.length - 1];
          if (isWhitespaceTextNode(lastChild)) {
            node.children.pop();
          }
        },

        exit(node) {
          node.children = stripNoMinifyBlocks(node.children);

          if (skipStack[skipStack.length - 1] === node) {
            skipStack.pop();
          }
        }
      },
    }
  };
}

function createRegistryPlugin(config) {
  return class HBSMinifierPlugin {
    transform(ast) {
      let startLoc = ast.loc ? ast.loc.start : {};
      /*
        Checking for line and column to avoid registering the plugin for ProgramNode inside a BlockStatement since transform is called for all ProgramNodes in Ember 2.15.X. Removing this would result in minifying all the TextNodes.
      */
      if (startLoc.line !== 1 || startLoc.column !== 0) {
        return ast;
      }

      let plugin = createGlimmerPlugin(config);
      this.syntax.traverse(ast, plugin.visitor);
      return ast;
    }
  };
}

function isWhitespaceTextNode(node) {
  return node && node.type === 'TextNode' && WHITESPACE.test(node.chars);
}

function stripNoMinifyBlocks(nodes) {
  return nodes.map(node => {
    if (node.type === 'BlockStatement' && node.path.original === 'no-minify') {
      return node.program.body;
    }
    return node;
  }).reduce((a, b) => a.concat(b), []);
}

function isClassIncluded(chars, classes) {
  chars = (chars || '').trim().split(' ');

  return chars.some((char) => {
    return classes.indexOf(char) !== -1;
  });
}

function canTrimWhiteSpaceBasedOnClassNames(value, configClassNames) {
  /*
    1. If no value is provided for class, the we can minify the content.
    2. If all classNames need to be preserved, then we must preserve the whitespace.
    3. If a string specified(class) contains a class which needs to be skipped then we must preserve the whitespace.
        For instance:

            baz

    4. If a PathExpression is provided as mentioned below, we should preserve the whitespace since the value is known only at runtime.
        For instance:

            bar

    5. If a MustacheStatement is provided as mentioned below. Incase if its a helper if/unless, we need to preserve if any class that needs to be skipped is specified which can be found by following steps 1 to 4.
        For instance:

            qux

    6. If a ConcatStatement is provided, for instance,

          bar

       we need to preserve the whitespace if any class that needs to be skipped is specified which can be found by following steps 1 to 4.
  */
  if (!value) {
    return true;
  }
  if (configClassNames === 'all') {
    return false;
  }
  let type = value.type;

  if (type === 'TextNode') {
    return !isClassIncluded(value.chars, configClassNames);
  } else if (type === 'StringLiteral') {
    return !isClassIncluded(value.value, configClassNames);
  } else if (type === 'PathExpression') {
    return false;
  } else if (type === 'MustacheStatement') {
    let canTrim = true;

    if (['if', 'unless'].indexOf(value.path.original) !== -1) {
      let params = value.params;
      for (let i = 1; i < params.length; i++) {
        canTrim = canTrimWhiteSpaceBasedOnClassNames(params[i], configClassNames);
        if (!canTrim) {
          break;
        }
      }
    }
    return canTrim;
  } else if (type === 'ConcatStatement') {
    let parts = value.parts;

    return parts.every((part) => {
      return canTrimWhiteSpaceBasedOnClassNames(part, configClassNames);
    });
  }
  return true;
}

function shouldSkipBlockStatement(node, config) {
  // If a block or all the blocks is/are skiped (or) named as 'no-minify' then we need to preserve the whitespace.
  let componentName = node.path.original;
  let components = config.components;
  return components.indexOf(componentName) !== -1 || components === 'all';
}

function shouldSkipElementNode(node, config) {
  // If a element or all the element is/are skiped then we need to preserve the whitespace.
  let elements = config.elements;
  let tag = node.tag;
  if (elements.indexOf(tag) !== -1 || elements === 'all') {
    return true;
  }

  let classAttrNode = node.attributes.find(attr => attr.name === 'class');
  if (classAttrNode) {
    return !canTrimWhiteSpaceBasedOnClassNames(classAttrNode.value, config.classes);
  }

  return false;
}

module.exports = {
  createGlimmerPlugin,
  createRegistryPlugin,
};
<commit_msg>Return early for `all` config values<commit_after>'use strict';

/* eslint-env node */

const leadingWhiteSpace = /^[ \t\r\n]+/;
const trailingWhiteSpace = /[ \t\r\n]+$/;
const WHITESPACE = /^[ \t\r\n]+$/;

function createGlimmerPlugin(config) {
  // in this stack we track the nodes that cause us to skip the minification
  // e.g. `{{#no-minify}} ... {{/no-minify}}` blocks or `<pre></pre>` tags
  // depending on the configuration
  let skipStack = [];

  function insideSkipBlock() {
    return skipStack.length !== 0;
  }

  return {
    name: 'hbs-minifier-plugin',

    visitor: {
      TextNode(node) {
        if (!insideSkipBlock()) {
          // replace leading and trailing whitespace with a single whitespace character
          node.chars = node.chars.replace(leadingWhiteSpace, ' ').replace(trailingWhiteSpace, ' ');
        }
      },

      BlockStatement: {
        enter(node) {
          if (shouldSkipBlockStatement(node, config)) {
            skipStack.push(node);
          }
        },

        exit(node) {
          if (skipStack[skipStack.length - 1] === node) {
            skipStack.pop();
          }
        },
      },

      Program: {
        enter(node) {
          if (insideSkipBlock()) {
            return;
          }

          let firstChild = node.body[0];
          if (isWhitespaceTextNode(firstChild)) {
            node.body.shift();
          }

          let lastChild = node.body[node.body.length - 1];
          if (isWhitespaceTextNode(lastChild)) {
            node.body.pop();
          }
        },

        exit(node) {
          node.body = stripNoMinifyBlocks(node.body);
        },
      },

      ElementNode: {
        enter(node) {
          if (shouldSkipElementNode(node, config)) {
            skipStack.push(node);
          }

          if (insideSkipBlock()) {
            return;
          }

          let firstChild = node.children[0];
          if (isWhitespaceTextNode(firstChild)) {
            node.children.shift();
          }

          let lastChild = node.children[node.children.length - 1];
          if (isWhitespaceTextNode(lastChild)) {
            node.children.pop();
          }
        },

        exit(node) {
          node.children = stripNoMinifyBlocks(node.children);

          if (skipStack[skipStack.length - 1] === node) {
            skipStack.pop();
          }
        }
      },
    }
  };
}

function createRegistryPlugin(config) {
  return class HBSMinifierPlugin {
    transform(ast) {
      let startLoc = ast.loc ? ast.loc.start : {};
      /*
        Checking for line and column to avoid registering the plugin for ProgramNode inside a BlockStatement since transform is called for all ProgramNodes in Ember 2.15.X. Removing this would result in minifying all the TextNodes.
      */
      if (startLoc.line !== 1 || startLoc.column !== 0) {
        return ast;
      }

      let plugin = createGlimmerPlugin(config);
      this.syntax.traverse(ast, plugin.visitor);
      return ast;
    }
  };
}

function isWhitespaceTextNode(node) {
  return node && node.type === 'TextNode' && WHITESPACE.test(node.chars);
}

function stripNoMinifyBlocks(nodes) {
  return nodes.map(node => {
    if (node.type === 'BlockStatement' && node.path.original === 'no-minify') {
      return node.program.body;
    }
    return node;
  }).reduce((a, b) => a.concat(b), []);
}

function isClassIncluded(chars, classes) {
  chars = (chars || '').trim().split(' ');

  return chars.some((char) => {
    return classes.indexOf(char) !== -1;
  });
}

function canTrimWhiteSpaceBasedOnClassNames(value, configClassNames) {
  /*
    1. If no value is provided for class, the we can minify the content.
    2. If all classNames need to be preserved, then we must preserve the whitespace.
    3. If a string specified(class) contains a class which needs to be skipped then we must preserve the whitespace.
        For instance:

            baz

    4. If a PathExpression is provided as mentioned below, we should preserve the whitespace since the value is known only at runtime.
        For instance:

            bar

    5. If a MustacheStatement is provided as mentioned below. Incase if its a helper if/unless, we need to preserve if any class that needs to be skipped is specified which can be found by following steps 1 to 4.
        For instance:

            qux

    6. If a ConcatStatement is provided, for instance,

          bar

       we need to preserve the whitespace if any class that needs to be skipped is specified which can be found by following steps 1 to 4.
  */
  if (!value) {
    return true;
  }
  if (configClassNames === 'all') {
    return false;
  }
  let type = value.type;

  if (type === 'TextNode') {
    return !isClassIncluded(value.chars, configClassNames);
  } else if (type === 'StringLiteral') {
    return !isClassIncluded(value.value, configClassNames);
  } else if (type === 'PathExpression') {
    return false;
  } else if (type === 'MustacheStatement') {
    let canTrim = true;

    if (['if', 'unless'].indexOf(value.path.original) !== -1) {
      let params = value.params;
      for (let i = 1; i < params.length; i++) {
        canTrim = canTrimWhiteSpaceBasedOnClassNames(params[i], configClassNames);
        if (!canTrim) {
          break;
        }
      }
    }
    return canTrim;
  } else if (type === 'ConcatStatement') {
    let parts = value.parts;

    return parts.every((part) => {
      return canTrimWhiteSpaceBasedOnClassNames(part, configClassNames);
    });
  }
  return true;
}

function shouldSkipBlockStatement(node, config) {
  let components = config.components;
  if (components === 'all') {
    return true;
  }

  // If a block or all the blocks is/are skiped (or) named as 'no-minify' then we need to preserve the whitespace.
  let componentName = node.path.original;
  return components.indexOf(componentName) !== -1;
}

function shouldSkipElementNode(node, config) {
  let elements = config.elements;
  if (elements === 'all') {
    return true;
  }

  // If a element or all the element is/are skiped then we need to preserve the whitespace.
  let tag = node.tag;
  if (elements.indexOf(tag) !== -1) {
    return true;
  }

  let classAttrNode = node.attributes.find(attr => attr.name === 'class');
  if (classAttrNode) {
    return !canTrimWhiteSpaceBasedOnClassNames(classAttrNode.value, config.classes);
  }

  return false;
}

module.exports = {
  createGlimmerPlugin,
  createRegistryPlugin,
};
<|endoftext|>