<commit_before>'use strict';

/**
 * Upload.js service
 *
 * @description: A set of functions similar to controller's actions to avoid code duplication.
 */

const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const _ = require('lodash');
const util = require('util');
const filenamify = require('filenamify');
const { bytesToKbytes } = require('../utils/file');

const randomSuffix = () => crypto.randomBytes(5).toString('hex');
const generateFileName = name => {
  const baseName = filenamify(name, { replacement: '_' }).replace(/\s/g, '_');

  return `${baseName}_${randomSuffix()}`;
};

module.exports = {
  formatFileInfo({ filename, type, size }, fileInfo = {}, metas = {}) {
    const ext = path.extname(filename);
    const baseName = path.basename(filename, ext);

    const usedName = fileInfo.name || baseName;

    const entity = {
      name: usedName,
      alternativeText: fileInfo.alternativeText,
      caption: fileInfo.caption,
      hash: generateFileName(usedName),
      ext,
      mime: type,
      size: bytesToKbytes(size),
    };

    const { refId, ref, source, field } = metas;

    if (refId && ref && field) {
      entity.related = [
        {
          refId,
          ref,
          source,
          field,
        },
      ];
    }

    if (metas.path) {
      entity.path = metas.path;
    }

    return entity;
  },

  async enhanceFile(file, fileInfo = {}, metas = {}) {
    const readBuffer = await util.promisify(fs.readFile)(file.path);

    const { optimize } = strapi.plugins.upload.services['image-manipulation'];

    const { buffer, info } = await optimize(readBuffer);

    const formattedFile = this.formatFileInfo(
      {
        filename: file.name,
        type: file.type,
        size: file.size,
      },
      fileInfo,
      metas
    );

    return _.assign(formattedFile, info, {
      buffer,
    });
  },

  async upload({ data, files }) {
    const { fileInfo, ...metas } = data;

    const fileArray = Array.isArray(files) ? files : [files];
    const fileInfoArray = Array.isArray(fileInfo) ? fileInfo : [fileInfo];

    const doUpload = async (file, fileInfo) => {
      const fileData = await this.enhanceFile(file, fileInfo, metas);

      return this.uploadFileAndPersist(fileData);
    };

    return await Promise.all(
      fileArray.map((file, idx) => doUpload(file, fileInfoArray[idx] || {}))
    );
  },

  async uploadFileAndPersist(fileData) {
    const config = strapi.plugins.upload.config;

    const {
      getDimensions,
      generateThumbnail,
      generateResponsiveFormats,
    } = strapi.plugins.upload.services['image-manipulation'];

    await strapi.plugins.upload.provider.upload(fileData);

    const thumbnailFile = await generateThumbnail(fileData);
    if (thumbnailFile) {
      await strapi.plugins.upload.provider.upload(thumbnailFile);
      delete thumbnailFile.buffer;
      _.set(fileData, 'formats.thumbnail', thumbnailFile);
    }

    const formats = await generateResponsiveFormats(fileData);
    if (Array.isArray(formats) && formats.length > 0) {
      for (const format of formats) {
        if (!format) continue;

        const { key, file } = format;

        await strapi.plugins.upload.provider.upload(file);
        delete file.buffer;

        _.set(fileData, ['formats', key], file);
      }
    }

    const { width, height } = await getDimensions(fileData.buffer);

    delete fileData.buffer;

    _.assign(fileData, {
      provider: config.provider,
      width,
      height,
    });

    return this.add(fileData);
  },

  async updateFileInfo(id, { name, alternativeText, caption }) {
    const dbFile = await this.fetch({ id });

    if (!dbFile) {
      throw strapi.errors.notFound('file not found');
    }

    const newInfos = {
      name: _.isNil(name) ? dbFile.name : name,
      alternativeText: _.isNil(alternativeText) ? dbFile.alternativeText : alternativeText,
      caption: _.isNil(caption) ? dbFile.caption : caption,
    };

    return this.update({ id }, newInfos);
  },

  async replace(id, { data, file }) {
    const config = strapi.plugins.upload.config;

    const {
      getDimensions,
      generateThumbnail,
      generateResponsiveFormats,
    } = strapi.plugins.upload.services['image-manipulation'];

    const dbFile = await this.fetch({ id });

    if (!dbFile) {
      throw strapi.errors.notFound('file not found');
    }

    const { fileInfo } = data;
    const fileData = await this.enhanceFile(file, fileInfo);

    // keep a constant hash
    _.assign(fileData, {
      hash: dbFile.hash,
      ext: dbFile.ext,
    });

    // execute delete function of the provider
    if (dbFile.provider === config.provider) {
      await strapi.plugins.upload.provider.delete(dbFile);

      if (dbFile.formats) {
        await Promise.all(
          Object.keys(dbFile.formats).map(key => {
            return strapi.plugins.upload.provider.delete(dbFile.formats[key]);
          })
        );
      }
    }

    await strapi.plugins.upload.provider.upload(fileData);

    // clear old formats
    _.set(fileData, 'formats', {});

    const thumbnailFile = await generateThumbnail(fileData);
    if (thumbnailFile) {
      await strapi.plugins.upload.provider.upload(thumbnailFile);
      delete thumbnailFile.buffer;
      _.set(fileData, 'formats.thumbnail', thumbnailFile);
    }

    const formats = await generateResponsiveFormats(fileData);
    if (Array.isArray(formats) && formats.length > 0) {
      for (const format of formats) {
        if (!format) continue;

        const { key, file } = format;

        await strapi.plugins.upload.provider.upload(file);
        delete file.buffer;

        _.set(fileData, ['formats', key], file);
      }
    }

    const { width, height } = await getDimensions(fileData.buffer);
    delete fileData.buffer;

    _.assign(fileData, {
      provider: config.provider,
      width,
      height,
    });

    return this.update({ id }, fileData);
  },

  async update(params, values) {
    const res = await strapi.query('file', 'upload').update(params, values);
    strapi.eventHub.emit('media.update', { media: res });
    return res;
  },

  async add(values) {
    const res = await strapi.query('file', 'upload').create(values);
    strapi.eventHub.emit('media.create', { media: res });
    return res;
  },

  fetch(params) {
    return strapi.query('file', 'upload').findOne(params);
  },

  fetchAll(params) {
    // FIXME: until we support boolean operators for querying we need to make mime_ncontains use AND instead of OR
    if (_.has(params, 'mime_ncontains') && Array.isArray(params.mime_ncontains)) {
      params._where = params.mime_ncontains.map(val => ({ mime_ncontains: val }));
      delete params.mime_ncontains;
    }

    return strapi.query('file', 'upload').find(params);
  },

  search(params) {
    return strapi.query('file', 'upload').search(params);
  },

  countSearch(params) {
    return strapi.query('file', 'upload').countSearch(params);
  },

  count(params) {
    return strapi.query('file', 'upload').count(params);
  },

  async remove(file) {
    const config = strapi.plugins.upload.config;

    // execute delete function of the provider
    if (file.provider === config.provider) {
      await strapi.plugins.upload.provider.delete(file);

      if (file.formats) {
        await Promise.all(
          Object.keys(file.formats).map(key => {
            return strapi.plugins.upload.provider.delete(file.formats[key]);
          })
        );
      }
    }

    const media = await strapi.query('file', 'upload').findOne({
      id: file.id,
    });

    strapi.eventHub.emit('media.delete', { media });

    return strapi.query('file', 'upload').delete({ id: file.id });
  },

  async uploadToEntity(params, files, source) {
    const { id, model, field } = params;

    const arr = Array.isArray(files) ? files : [files];
    const enhancedFiles = await Promise.all(
      arr.map(file => {
        return this.enhanceFile(
          file,
          {},
          {
            refId: id,
            ref: model,
            source,
            field,
          }
        );
      })
    );

    await Promise.all(enhancedFiles.map(file => this.uploadFileAndPersist(file)));
  },

  getSettings() {
    return strapi
      .store({
        type: 'plugin',
        name: 'upload',
        key: 'settings',
      })
      .get();
  },

  setSettings(value) {
    return strapi
      .store({
        type: 'plugin',
        name: 'upload',
        key: 'settings',
      })
      .set({ value });
  },
};
<commit_msg>Add same filter logic to file count<commit_after>'use strict';

/**
 * Upload.js service
 *
 * @description: A set of functions similar to controller's actions to avoid code duplication.
 */

const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const _ = require('lodash');
const util = require('util');
const filenamify = require('filenamify');
const { bytesToKbytes } = require('../utils/file');

const randomSuffix = () => crypto.randomBytes(5).toString('hex');
const generateFileName = name => {
  const baseName = filenamify(name, { replacement: '_' }).replace(/\s/g, '_');

  return `${baseName}_${randomSuffix()}`;
};

module.exports = {
  formatFileInfo({ filename, type, size }, fileInfo = {}, metas = {}) {
    const ext = path.extname(filename);
    const baseName = path.basename(filename, ext);

    const usedName = fileInfo.name || baseName;

    const entity = {
      name: usedName,
      alternativeText: fileInfo.alternativeText,
      caption: fileInfo.caption,
      hash: generateFileName(usedName),
      ext,
      mime: type,
      size: bytesToKbytes(size),
    };

    const { refId, ref, source, field } = metas;

    if (refId && ref && field) {
      entity.related = [
        {
          refId,
          ref,
          source,
          field,
        },
      ];
    }

    if (metas.path) {
      entity.path = metas.path;
    }

    return entity;
  },

  async enhanceFile(file, fileInfo = {}, metas = {}) {
    const readBuffer = await util.promisify(fs.readFile)(file.path);

    const { optimize } = strapi.plugins.upload.services['image-manipulation'];

    const { buffer, info } = await optimize(readBuffer);

    const formattedFile = this.formatFileInfo(
      {
        filename: file.name,
        type: file.type,
        size: file.size,
      },
      fileInfo,
      metas
    );

    return _.assign(formattedFile, info, {
      buffer,
    });
  },

  async upload({ data, files }) {
    const { fileInfo, ...metas } = data;

    const fileArray = Array.isArray(files) ? files : [files];
    const fileInfoArray = Array.isArray(fileInfo) ? fileInfo : [fileInfo];

    const doUpload = async (file, fileInfo) => {
      const fileData = await this.enhanceFile(file, fileInfo, metas);

      return this.uploadFileAndPersist(fileData);
    };

    return await Promise.all(
      fileArray.map((file, idx) => doUpload(file, fileInfoArray[idx] || {}))
    );
  },

  async uploadFileAndPersist(fileData) {
    const config = strapi.plugins.upload.config;

    const {
      getDimensions,
      generateThumbnail,
      generateResponsiveFormats,
    } = strapi.plugins.upload.services['image-manipulation'];

    await strapi.plugins.upload.provider.upload(fileData);

    const thumbnailFile = await generateThumbnail(fileData);
    if (thumbnailFile) {
      await strapi.plugins.upload.provider.upload(thumbnailFile);
      delete thumbnailFile.buffer;
      _.set(fileData, 'formats.thumbnail', thumbnailFile);
    }

    const formats = await generateResponsiveFormats(fileData);
    if (Array.isArray(formats) && formats.length > 0) {
      for (const format of formats) {
        if (!format) continue;

        const { key, file } = format;

        await strapi.plugins.upload.provider.upload(file);
        delete file.buffer;

        _.set(fileData, ['formats', key], file);
      }
    }

    const { width, height } = await getDimensions(fileData.buffer);

    delete fileData.buffer;

    _.assign(fileData, {
      provider: config.provider,
      width,
      height,
    });

    return this.add(fileData);
  },

  async updateFileInfo(id, { name, alternativeText, caption }) {
    const dbFile = await this.fetch({ id });

    if (!dbFile) {
      throw strapi.errors.notFound('file not found');
    }

    const newInfos = {
      name: _.isNil(name) ? dbFile.name : name,
      alternativeText: _.isNil(alternativeText) ? dbFile.alternativeText : alternativeText,
      caption: _.isNil(caption) ? dbFile.caption : caption,
    };

    return this.update({ id }, newInfos);
  },

  async replace(id, { data, file }) {
    const config = strapi.plugins.upload.config;

    const {
      getDimensions,
      generateThumbnail,
      generateResponsiveFormats,
    } = strapi.plugins.upload.services['image-manipulation'];

    const dbFile = await this.fetch({ id });

    if (!dbFile) {
      throw strapi.errors.notFound('file not found');
    }

    const { fileInfo } = data;
    const fileData = await this.enhanceFile(file, fileInfo);

    // keep a constant hash
    _.assign(fileData, {
      hash: dbFile.hash,
      ext: dbFile.ext,
    });

    // execute delete function of the provider
    if (dbFile.provider === config.provider) {
      await strapi.plugins.upload.provider.delete(dbFile);

      if (dbFile.formats) {
        await Promise.all(
          Object.keys(dbFile.formats).map(key => {
            return strapi.plugins.upload.provider.delete(dbFile.formats[key]);
          })
        );
      }
    }

    await strapi.plugins.upload.provider.upload(fileData);

    // clear old formats
    _.set(fileData, 'formats', {});

    const thumbnailFile = await generateThumbnail(fileData);
    if (thumbnailFile) {
      await strapi.plugins.upload.provider.upload(thumbnailFile);
      delete thumbnailFile.buffer;
      _.set(fileData, 'formats.thumbnail', thumbnailFile);
    }

    const formats = await generateResponsiveFormats(fileData);
    if (Array.isArray(formats) && formats.length > 0) {
      for (const format of formats) {
        if (!format) continue;

        const { key, file } = format;

        await strapi.plugins.upload.provider.upload(file);
        delete file.buffer;

        _.set(fileData, ['formats', key], file);
      }
    }

    const { width, height } = await getDimensions(fileData.buffer);
    delete fileData.buffer;

    _.assign(fileData, {
      provider: config.provider,
      width,
      height,
    });

    return this.update({ id }, fileData);
  },

  async update(params, values) {
    const res = await strapi.query('file', 'upload').update(params, values);
    strapi.eventHub.emit('media.update', { media: res });
    return res;
  },

  async add(values) {
    const res = await strapi.query('file', 'upload').create(values);
    strapi.eventHub.emit('media.create', { media: res });
    return res;
  },

  fetch(params) {
    return strapi.query('file', 'upload').findOne(params);
  },

  fetchAll(params) {
    // FIXME: until we support boolean operators for querying we need to make mime_ncontains use AND instead of OR
    if (_.has(params, 'mime_ncontains') && Array.isArray(params.mime_ncontains)) {
      params._where = params.mime_ncontains.map(val => ({ mime_ncontains: val }));
      delete params.mime_ncontains;
    }

    return strapi.query('file', 'upload').find(params);
  },

  search(params) {
    return strapi.query('file', 'upload').search(params);
  },

  countSearch(params) {
    return strapi.query('file', 'upload').countSearch(params);
  },

  count(params) {
    // FIXME: until we support boolean operators for querying we need to make mime_ncontains use AND instead of OR
    if (_.has(params, 'mime_ncontains') && Array.isArray(params.mime_ncontains)) {
      params._where = params.mime_ncontains.map(val => ({ mime_ncontains: val }));
      delete params.mime_ncontains;
    }

    return strapi.query('file', 'upload').count(params);
  },

  async remove(file) {
    const config = strapi.plugins.upload.config;

    // execute delete function of the provider
    if (file.provider === config.provider) {
      await strapi.plugins.upload.provider.delete(file);

      if (file.formats) {
        await Promise.all(
          Object.keys(file.formats).map(key => {
            return strapi.plugins.upload.provider.delete(file.formats[key]);
          })
        );
      }
    }

    const media = await strapi.query('file', 'upload').findOne({
      id: file.id,
    });

    strapi.eventHub.emit('media.delete', { media });

    return strapi.query('file', 'upload').delete({ id: file.id });
  },

  async uploadToEntity(params, files, source) {
    const { id, model, field } = params;

    const arr = Array.isArray(files) ? files : [files];
    const enhancedFiles = await Promise.all(
      arr.map(file => {
        return this.enhanceFile(
          file,
          {},
          {
            refId: id,
            ref: model,
            source,
            field,
          }
        );
      })
    );

    await Promise.all(enhancedFiles.map(file => this.uploadFileAndPersist(file)));
  },

  getSettings() {
    return strapi
      .store({
        type: 'plugin',
        name: 'upload',
        key: 'settings',
      })
      .get();
  },

  setSettings(value) {
    return strapi
      .store({
        type: 'plugin',
        name: 'upload',
        key: 'settings',
      })
      .set({ value });
  },
};
<|endoftext|>


<commit_before>global.$ = $;

const {remote} = require('electron');
const {Menu, BrowserWindow, MenuItem, shell} = remote;
const favicon = require('favicon');
const nodeConsole = require('console');
const appitem_style = require('./js/appitem_style');
const http = require('http');
const mainConsole= new nodeConsole.Console(process.stdout, process.stderr);
let aboutWindow = null;

const App = {
  // log to console
  log: function(mess) {
    mainConsole.log(mess);
  },

  // show "about" window
  about: function () {
    var params = {toolbar: false, resizable: false, show: true, height: 150, width: 400};
    aboutWindow = new BrowserWindow(params);
    aboutWindow.loadURL('file://' + __dirname + '/about.html');
  },

  addApp: function(name, appUrl) {

    getAppIcon(name, appUrl);
  },

  // change folder for sidebar links
  cd: function (anchor) {
    anchor = $(anchor);

    $('#sidebar li').removeClass('active');
    $('#sidebar i').removeClass('icon-white');

    anchor.closest('li').addClass('active');
    anchor.find('i').addClass('icon-white');

    this.setPath(anchor.attr('nw-path'));
  },

  // set path for file explorer
  setPath: function (path) {
    if (path.indexOf('~') == 0) {
      path = path.replace('~', process.env['HOME']);
    }
    this.folder.open(path);
    this.addressbar.set(path);
  }
};

function getAppIcon(name, appurl) {
  favicon(appurl, (err, iconUrl)=>{

    if (err) {
      App.log('error');
      throw err;
    } else {
      App.log(iconUrl);
      let data = '';
      http.get(iconUrl, (res) => {
        let buf = Buffer('', 'utf8');
        res.on('data', (buf)=> {
          data += buf.toString();
        });

        res.on('end', ()=> {
          //construct listiem
          // <i> element
          const iconData = (new Buffer(data)).toString('base64');
          const iElement = document.createElement('i');

          for (propName in appitem_style) {
            if (appitem_style.hasOwnProperty(propName)) {
              iElement.style[propName] = appitem_style[propName];
            }
          }
          //App.log(iElement.style);
          //iElement.style.backgroundImage = 'url(data:image/x-icon;base64,' + iconData + ')'
          iElement.style.background = 'rgba(0, 0, 0, 0) url("' + iconUrl + '") no-repeat left top)';
          iElement.style.backgroundImage = 'url("' + iconUrl + '")';

          // <a> element
          const aElement = document.createElement('a');
          aElement.setAttribute('href', '#');
          aElement.setAttribute('app-url', appurl);

          aElement.appendChild(iElement);
          aElement.appendChild(document.createTextNode(name));
          //App.log(aElement.innerHTML)

          // <li> item
          const liElement = document.createElement('li');
          liElement.appendChild(aElement);
          //App.log(liElement.innerHTML);

          document.getElementById('sidebar').appendChild(liElement);

        });

      });
    }
  });
}

$(document).ready(function() {

  //initMenu();
  //App.addApp('Duolingo', 'http://www.duolingo.com/');

  /*
  var folder = new folder_view.Fo.lder($('#files'));
  var addressbar = new abar.AddressBar($('#addressbar'));

  folder.open(process.cwd());
  addressbar.set(process.cwd());

  App.folder = folder;
  App.addressbar = addressbar;

  folder.on('navigate', function(dir, mime) {
    if (mime.type == 'folder') {
      addressbar.enter(mime);
    } else {
      shell.openItem(mime.path);
    }
  });

  addressbar.on('navigate', function(dir) {
    folder.open(dir);
  });
  */
  // sidebar favorites
  $('[nw-path]').bind('click', function (event) {
    event.preventDefault();
    App.cd(this);
  });
});

<commit_msg>Changed listitem background properties so as to display entire icon<commit_after>global.$ = $;

const {remote} = require('electron');
const {Menu, BrowserWindow, MenuItem, shell} = remote;
const favicon = require('favicon');
const nodeConsole = require('console');
const appitem_style = require('./js/appitem_style');
const http = require('http');
const mainConsole= new nodeConsole.Console(process.stdout, process.stderr);
let aboutWindow = null;

const App = {
  // log to console
  log: function(mess) {
    mainConsole.log(mess);
  },

  // show "about" window
  about: function () {
    var params = {toolbar: false, resizable: false, show: true, height: 150, width: 400};
    aboutWindow = new BrowserWindow(params);
    aboutWindow.loadURL('file://' + __dirname + '/about.html');
  },

  addApp: function(name, appUrl) {

    getAppIcon(name, appUrl);
  },

  // change folder for sidebar links
  cd: function (anchor) {
    anchor = $(anchor);

    $('#sidebar li').removeClass('active');
    $('#sidebar i').removeClass('icon-white');

    anchor.closest('li').addClass('active');
    anchor.find('i').addClass('icon-white');

    this.setPath(anchor.attr('nw-path'));
  },

  // set path for file explorer
  setPath: function (path) {
    if (path.indexOf('~') == 0) {
      path = path.replace('~', process.env['HOME']);
    }
    this.folder.open(path);
    this.addressbar.set(path);
  }
};

function getAppIcon(name, appurl) {
  favicon(appurl, (err, iconUrl)=>{

    if (err) {
      App.log(err);
      //give feedback
    } else {
      App.log(iconUrl);
      let data = '';
      http.get(iconUrl, (res) => {
        let buf = Buffer('', 'utf8');
        res.on('data', (buf)=> {
          data += buf.toString();
        });

        res.on('end', ()=> {
          //construct listiem
          // <i> element
          const iconData = (new Buffer(data)).toString('base64');
          const iElement = document.createElement('i');

          for (propName in appitem_style) {
            if (appitem_style.hasOwnProperty(propName)) {
              iElement.style[propName] = appitem_style[propName];
            }
          }
          //App.log(iElement.style);
          //iElement.style.backgroundImage = 'url(data:image/x-icon;base64,' + iconData + ')'
          iElement.style.background = 'rgba(0, 0, 0, 0) url("' + iconUrl + '") no-repeat 0px 0px)';
          iElement.style.backgroundImage = 'url("' + iconUrl + '")';
          iElement.style.backgroundOrigin = 'padding-box';
          iElement.style.backgroundPosition = '0px 0px';
          iElement.style.backgroundPositionX = '0px';
          iElement.style.backgroundPositionY = '0px';
          // <a> element
          const aElement = document.createElement('a');
          aElement.setAttribute('href', '#');
          aElement.setAttribute('app-url', appurl);

          aElement.appendChild(iElement);
          aElement.appendChild(document.createTextNode(name));
          //App.log(aElement.innerHTML)

          // <li> item
          const liElement = document.createElement('li');
          liElement.appendChild(aElement);
          //App.log(liElement.innerHTML);

          document.getElementById('sidebar').appendChild(liElement);

        });

      });
    }
  });
}

$(document).ready(function() {

  //initMenu();
  //App.addApp('Duolingo', 'http://www.duolingo.com/');

  /*
  var folder = new folder_view.Fo.lder($('#files'));
  var addressbar = new abar.AddressBar($('#addressbar'));

  folder.open(process.cwd());
  addressbar.set(process.cwd());

  App.folder = folder;
  App.addressbar = addressbar;

  folder.on('navigate', function(dir, mime) {
    if (mime.type == 'folder') {
      addressbar.enter(mime);
    } else {
      shell.openItem(mime.path);
    }
  });

  addressbar.on('navigate', function(dir) {
    folder.open(dir);
  });
  */
  // sidebar favorites
  $('[nw-path]').bind('click', function (event) {
    event.preventDefault();
    App.cd(this);
  });
});

<|endoftext|>


<commit_before>/**
 * Author: Olaolu Olawuyi

 * Date Created: 20/08/2015

 * Last Modified: 02/07/2016

 * Twitter: @whizkydee, @webtwic

 * Github: https://github.com/webtwic/webtwic.github.io

*/

var Webtwic = Webtwic || {};

var browser_name = bowser.name.toLowerCase();

var test_canvas = document.createElement( 'canvas' );
var canvascheck = ( test_canvas.getContext ) ? true : false;

Webtwic = {
	// Basic Information about Webtwic
	name: 'Webtwic',
	description: 'A community focused on code tweaks, articles and tutorials for newbie and professional developers.',
	repo: 'https://github.com/webtwic/webtwic.github.io',
	author: 'Olaolu Olawuyi',
	version: '1.0.0',

	// Start main functions for the object

	// Function to add browser name and version to body class
	showBrowser: function( b ) {
		b = (typeof b == 'undefined') ? browser_name : b.toLowerCase();
		if ( ! ( bowser.webkit ) )
			$( 'body' ).addClass( b + ' ' + b + parseInt( bowser.version ) );
		else
			$( 'body' ).addClass( 'webkit' + ' ' + b + ' ' + b + parseInt( bowser.version ) );
	},

	// Function to set attributes and values to elements
	setAttr: function( elem, name, val ) {
		$( elem, this ).attr( name, val );
	},

	// Function to push down .site-content
	adjustSiteContent: function() {
		if ( $( 'body' ).hasClass( 'default-body' ) ) {
			$( '.top-stripe' ).addClass( 'adjustedMargin' );
			$( '#header .logo' ).addClass( 'adjustedTop' );
			$( '.main-menu' ).addClass( 'adjustedMargin' );
			$( '#header' ).addClass( 'adjustSiteContent' );
		} else if ( $( 'body' ).hasClass( 'secondary-body' ) ) {
			$( '.site-content' ).addClass( 'adjustSiteContent' );
			$( '.secondary-header' ).addClass( 'adjustedMargin' );
		}
	},

	// Function to push up .site-content to default state
	undoSiteAdjust: function() {
		if ( $( 'body' ).hasClass( 'default-body' ) ) {
			$( '.top-stripe' ).removeClass( 'adjustedMargin' );
			$( '#header .logo' ).removeClass( 'adjustedTop' );
			$( '.main-menu' ).removeClass( 'adjustedMargin' );
			$( '#header' ).removeClass( 'adjustSiteContent' );
		} else if ( $( 'body' ).hasClass( 'secondary-body' ) ) {
			$( '.secondary-header' ).removeClass( 'adjustedMargin' );
		}
	},

	// Function to show Notifications before header
	showNotif: function( text ) {
		text = ( typeof text == 'undefined' ) ? notif_text : text;
		Webtwic.adjustSiteContent();
		$( 'body' ).prepend(
			'
'+text+''+close_button+'
'
		);

		$( '.close-button' ).on( 'click', function( e ) {
			e.preventDefault();
			Webtwic.undoReset();
		});
	},

	// Function to hide Notifications
	hideNotif: function() {
		$( '#notifBanner' ).removeClass( 'notif_vis' );
	},

	resetSite: function() {
		Webtwic.showNotif();
		Webtwic.adjustSiteContent();
	},

	undoReset: function() {
		Webtwic.hideNotif();
		Webtwic.undoSiteAdjust();
	},
};

var WBT = Webtwic,
	 $$ = Webtwic,
	 close_button = '',
	 browser_link = ' ' + 'browser.' + ' ',
	 notif_text = 'Whoa. Your browser currently dosen\'t support some important features. Please try refreshing this page or get a better' +browser_link;

// Detect when the DOM is ready

jQuery( document ).ready( function() {
	// Test for HTML5 support
	if ( window.Modernizr )
		if ( !Modernizr.canvas )
			Webtwic.showNotif ( "Oops. Looks like your browser doesn't support HTML5. Please get a better" +browser_link+ "for an awesome experience." );
		 else /* do nothing */ ;
	 else
		if ( canvascheck === false )
			Webtwic.showNotif ( "Oops. Looks like your browser doesn't support HTML5. Please get a better" +browser_link+ "for an awesome experience." );
		 else /* do nothing */ ;

	// Define Variables
	var $scroll_top = $( 'a.scroll_top' ),
	is_toggle_active = $( '.hamburger' ).hasClass( 'is_active' ),
	height = 200;

	// Performance Fixes

	// Start browser detection
	switch ( browser_name ) {
	   case 'microsoft edge':
	      Webtwic.showBrowser( 'msedge' );
	      break;
		default:
			Webtwic.showBrowser();
	}

	/*
		TODO: Rebuild Performance fixes for attributes
		See history of Performance fixes.
		Also, check StackOverflow.
		See Issue #12
	*/

	var required_features = [
		Modernizr.flexbox,
		Modernizr.inlinesvg,
		Modernizr.svg,
		Modernizr.svgclippaths,
		Modernizr.fontface,
		Modernizr.boxshadow,
		Modernizr.borderradius,
		Modernizr.csstransforms
	];

	if ( window.Modernizr )
		if ( !( required_features ) )
			Webtwic.showNotif();
		else /* do nothing */ ;
	else /* do nothing */ ;

	// Start Main Page

	$( 'nav li ul.drop-nav' )
	.hide()
	.removeClass( 'dropNavFallback' );

	if ( $( window ).width() > 768 ) {
		$( 'nav li.dropdown' ).hover(function() {
			$( 'ul.drop-nav', this )
			.stop()
			.slideDown( 'fast' );
		}, function() {
			$( 'ul.drop-nav', this )
			.stop()
			.slideUp( 'fast' );
		});
	}

	$( '.active-item' ).append(
		'<svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M 16.682,19.674c 0.010-0.012, 0.014-0.028, 0.024-0.040l 6.982-7.714c 0.39-0.434, 0.39-1.138,0-1.572 c-0.004-0.004-0.008-0.006-0.012-0.008C 23.496,10.13, 23.244,10, 22.964,10L 8.998,10 c-0.286,0-0.54,0.138-0.722,0.352L 8.272,10.348 c-0.39,0.434-0.39,1.138,0,1.572l 6.998,7.754C 15.66,20.108, 16.292,20.108, 16.682,19.674z"></path></svg>'
	);

	$( 'a.hamburger' ).on('click', function( e ) {
		$( this ).toggleClass( 'is_active' );

		e.preventDefault();
	});
	$( '.mobile-header .mobile-nav' ).hide();

	$( window ).on('scroll', function() {
		( $( this ).scrollTop() > height ) ? $scroll_top.addClass( 'topVisible' ): $scroll_top.removeClass( 'topVisible' );
	});

	// Styles for homepage article-social

	$( '.article .article-social a' ).hover(function() {
		$( 'span', this )
		.find( '.tooltip' )
		.addClass( 'visible' );
	}, function() {
		$( 'span', this )
		.find( '.tooltip' )
		.removeClass( 'visible' );
	});

	// Functions for search-icon and ui-overlay-search

	$( '.header-follow-search .search-wrap' ).on( 'click', function() {
		openUIOverlay();
	});

	$( '.header-follow-search .close-icon' ).on( 'click', function( e ) {
		e.preventDefault();
		closeUIOverlay();
	});

	$( '.header-follow-search .close-icon' ).hover(
		function() {
			$( this ).addClass( 'spin' );
		}, function() {
			$( this ).removeClass( 'spin' );
	});

	function openUIOverlay() {
		$( '.ui-overlay-search' )
		.fadeIn(400)
		.addClass( 'is_open' );
	}

	function closeUIOverlay() {
		$( '.ui-overlay-search' )
		.fadeOut(400)
		.removeClass( 'is_open' );
	}

	$( '.ui-overlay-search .search-input input' ).on('focus', function() {
		$( '.search-input' ).attr( 'data-focused', 'true' );
		$( '.search-input input' ).addClass( '_is-focused' );
		$( '#__expand' ).addClass( 'visible' );
	});

	$( '.ui-overlay-search .search-input input' ).on('blur', function() {
		$( '.search-input' ).attr( 'data-focused', 'false' );
		$( '.search-input input' ).removeClass( '_is-focused' );
		$( '#__expand' ).removeClass( 'visible' );
	});

	$( '.post-meta .post-meta-inner .cta-btns a.is_inactive' ).on( 'click', function( e ) {
		e.preventDefault();
	});

	$( document ).on('keydown', function( e ) {
		if ( e.keyCode == 27 )
			closeUIOverlay();
		else /* do nothing */ ;
 	});

	$( '.mobile-nav' ).hide();

	// Functions for more-posts button click

	var loadMoreBtn = '.secondary-category-section .category-posts-column [data-clicked="false"]';

	function addLoader() {
		$( loadMoreBtn )
		.attr( 'data-clicked', 'true' )
		.append( '' );
	}

	$( loadMoreBtn ).on( 'click', function( e ) {
		$( this ).css({
			'background': '#50ABF1',
			'color': '#fff'
		});
		addLoader();
		e.preventDefault();
	});

	$scroll_top.on( 'click', function( e ) {
		$( 'body, html' ).animate({
			scrollTop: 0
		}, 1000);

		e.preventDefault();
	});

});
<commit_msg>Use old tooltip function<commit_after>/**
 * Author: Olaolu Olawuyi

 * Date Created: 20/08/2015

 * Last Modified: 02/07/2016

 * Twitter: @whizkydee, @webtwic

 * Github: https://github.com/webtwic/webtwic.github.io

*/

var Webtwic = Webtwic || {};

var browser_name = bowser.name.toLowerCase();

var test_canvas = document.createElement( 'canvas' );
var canvascheck = ( test_canvas.getContext ) ? true : false;

Webtwic = {
	// Basic Information about Webtwic
	name: 'Webtwic',
	description: 'A community focused on code tweaks, articles and tutorials for newbie and professional developers.',
	repo: 'https://github.com/webtwic/webtwic.github.io',
	author: 'Olaolu Olawuyi',
	version: '1.0.0',

	// Start main functions for the object

	// Function to add browser name and version to body class
	showBrowser: function( b ) {
		b = (typeof b == 'undefined') ? browser_name : b.toLowerCase();
		if ( ! ( bowser.webkit ) )
			$( 'body' ).addClass( b + ' ' + b + parseInt( bowser.version ) );
		else
			$( 'body' ).addClass( 'webkit' + ' ' + b + ' ' + b + parseInt( bowser.version ) );
	},

	// Function to set attributes and values to elements
	setAttr: function( elem, name, val ) {
		$( elem, this ).attr( name, val );
	},

	// Function to push down .site-content
	adjustSiteContent: function() {
		if ( $( 'body' ).hasClass( 'default-body' ) ) {
			$( '.top-stripe' ).addClass( 'adjustedMargin' );
			$( '#header .logo' ).addClass( 'adjustedTop' );
			$( '.main-menu' ).addClass( 'adjustedMargin' );
			$( '#header' ).addClass( 'adjustSiteContent' );
		} else if ( $( 'body' ).hasClass( 'secondary-body' ) ) {
			$( '.site-content' ).addClass( 'adjustSiteContent' );
			$( '.secondary-header' ).addClass( 'adjustedMargin' );
		}
	},

	// Function to push up .site-content to default state
	undoSiteAdjust: function() {
		if ( $( 'body' ).hasClass( 'default-body' ) ) {
			$( '.top-stripe' ).removeClass( 'adjustedMargin' );
			$( '#header .logo' ).removeClass( 'adjustedTop' );
			$( '.main-menu' ).removeClass( 'adjustedMargin' );
			$( '#header' ).removeClass( 'adjustSiteContent' );
		} else if ( $( 'body' ).hasClass( 'secondary-body' ) ) {
			$( '.secondary-header' ).removeClass( 'adjustedMargin' );
		}
	},

	// Function to show Notifications before header
	showNotif: function( text ) {
		text = ( typeof text == 'undefined' ) ? notif_text : text;
		Webtwic.adjustSiteContent();
		$( 'body' ).prepend(
			'
'+text+''+close_button+'
'
		);

		$( '.close-button' ).on( 'click', function( e ) {
			e.preventDefault();
			Webtwic.undoReset();
		});
	},

	// Function to hide Notifications
	hideNotif: function() {
		$( '#notifBanner' ).removeClass( 'notif_vis' );
	},

	resetSite: function() {
		Webtwic.showNotif();
		Webtwic.adjustSiteContent();
	},

	undoReset: function() {
		Webtwic.hideNotif();
		Webtwic.undoSiteAdjust();
	},
};

var WBT = Webtwic,
	 $$ = Webtwic,
	 close_button = '',
	 browser_link = ' ' + 'browser.' + ' ',
	 notif_text = 'Whoa. Your browser currently dosen\'t support some important features. Please try refreshing this page or get a better' +browser_link;

// Detect when the DOM is ready

jQuery( document ).ready( function() {
	// Test for HTML5 support
	if ( window.Modernizr )
		if ( !Modernizr.canvas )
			Webtwic.showNotif ( "Oops. Looks like your browser doesn't support HTML5. Please get a better" +browser_link+ "for an awesome experience." );
		 else /* do nothing */ ;
	 else
		if ( canvascheck === false )
			Webtwic.showNotif ( "Oops. Looks like your browser doesn't support HTML5. Please get a better" +browser_link+ "for an awesome experience." );
		 else /* do nothing */ ;

	// Define Variables
	var $scroll_top = $( 'a.scroll_top' ),
	is_toggle_active = $( '.hamburger' ).hasClass( 'is_active' ),
	height = 200;

	// Performance Fixes

	// Start browser detection
	switch ( browser_name ) {
	   case 'microsoft edge':
	      Webtwic.showBrowser( 'msedge' );
	      break;
		default:
			Webtwic.showBrowser();
	}

	/*
		TODO: Rebuild Performance fixes for attributes
		See history of Performance fixes.
		Also, check StackOverflow.
		See Issue #12
	*/

	var required_features = [
		Modernizr.flexbox,
		Modernizr.inlinesvg,
		Modernizr.svg,
		Modernizr.svgclippaths,
		Modernizr.fontface,
		Modernizr.boxshadow,
		Modernizr.borderradius,
		Modernizr.csstransforms
	];

	if ( window.Modernizr )
		if ( !( required_features ) )
			Webtwic.showNotif();
		else /* do nothing */ ;
	else /* do nothing */ ;

	// Start Main Page

	$( 'nav li ul.drop-nav' )
	.hide()
	.removeClass( 'dropNavFallback' );

	if ( $( window ).width() > 768 ) {
		$( 'nav li.dropdown' ).hover(function() {
			$( 'ul.drop-nav', this )
			.stop()
			.slideDown( 'fast' );
		}, function() {
			$( 'ul.drop-nav', this )
			.stop()
			.slideUp( 'fast' );
		});
	}

	$( '.active-item' ).append(
		'<svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M 16.682,19.674c 0.010-0.012, 0.014-0.028, 0.024-0.040l 6.982-7.714c 0.39-0.434, 0.39-1.138,0-1.572 c-0.004-0.004-0.008-0.006-0.012-0.008C 23.496,10.13, 23.244,10, 22.964,10L 8.998,10 c-0.286,0-0.54,0.138-0.722,0.352L 8.272,10.348 c-0.39,0.434-0.39,1.138,0,1.572l 6.998,7.754C 15.66,20.108, 16.292,20.108, 16.682,19.674z"></path></svg>'
	);

	$( 'a.hamburger' ).on('click', function( e ) {
		$( this ).toggleClass( 'is_active' );

		e.preventDefault();
	});
	$( '.mobile-header .mobile-nav' ).hide();

	$( window ).on('scroll', function() {
		( $( this ).scrollTop() > height ) ? $scroll_top.addClass( 'topVisible' ): $scroll_top.removeClass( 'topVisible' );
	});

	// Styles for homepage article-social

	$( '.article .article-social a' ).hover(function() {
		$( 'span', this )
		.find( '.tooltip' )
		.fadeIn(400);
	}, function() {
		$( 'span', this )
		.find( '.tooltip' )
		.fadeOut(250);
	});

	// Functions for search-icon and ui-overlay-search

	$( '.header-follow-search .search-wrap' ).on( 'click', function() {
		openUIOverlay();
	});

	$( '.header-follow-search .close-icon' ).on( 'click', function( e ) {
		e.preventDefault();
		closeUIOverlay();
	});

	$( '.header-follow-search .close-icon' ).hover(
		function() {
			$( this ).addClass( 'spin' );
		}, function() {
			$( this ).removeClass( 'spin' );
	});

	function openUIOverlay() {
		$( '.ui-overlay-search' )
		.fadeIn(400)
		.addClass( 'is_open' );
	}

	function closeUIOverlay() {
		$( '.ui-overlay-search' )
		.fadeOut(400)
		.removeClass( 'is_open' );
	}

	$( '.ui-overlay-search .search-input input' ).on('focus', function() {
		$( '.search-input' ).attr( 'data-focused', 'true' );
		$( '.search-input input' ).addClass( '_is-focused' );
		$( '#__expand' ).addClass( 'visible' );
	});

	$( '.ui-overlay-search .search-input input' ).on('blur', function() {
		$( '.search-input' ).attr( 'data-focused', 'false' );
		$( '.search-input input' ).removeClass( '_is-focused' );
		$( '#__expand' ).removeClass( 'visible' );
	});

	$( '.post-meta .post-meta-inner .cta-btns a.is_inactive' ).on( 'click', function( e ) {
		e.preventDefault();
	});

	$( document ).on('keydown', function( e ) {
		if ( e.keyCode == 27 )
			closeUIOverlay();
		else /* do nothing */ ;
 	});

	$( '.mobile-nav' ).hide();

	// Functions for more-posts button click

	var loadMoreBtn = '.secondary-category-section .category-posts-column [data-clicked="false"]';

	function addLoader() {
		$( loadMoreBtn )
		.attr( 'data-clicked', 'true' )
		.append( '' );
	}

	$( loadMoreBtn ).on( 'click', function( e ) {
		$( this ).css({
			'background': '#50ABF1',
			'color': '#fff'
		});
		addLoader();
		e.preventDefault();
	});

	$scroll_top.on( 'click', function( e ) {
		$( 'body, html' ).animate({
			scrollTop: 0
		}, 1000);

		e.preventDefault();
	});

});
<|endoftext|>


<commit_before>import Users from './collection.js';
import intersection from 'lodash/intersection';
import compact from 'lodash/compact';
import map from 'lodash/map';
import difference from 'lodash/difference';
import get from 'lodash/get';
import { getCollection } from 'meteor/vulcan:lib';
import { Utils, deprecate } from 'meteor/vulcan:lib';

/**
 * @summary Users.groups object
 */
Users.groups = {};

/**
 * @summary Group class
 */
class Group {

  constructor() {
    this.actions = [];
  }

  can(actions) {
    actions = Array.isArray(actions) ? actions : [actions];
    this.actions = this.actions.concat(actions.map(a => a.toLowerCase()));
  }

  cannot(actions) {
    actions = Array.isArray(actions) ? actions : [actions];
    this.actions = _.difference(this.actions, actions.map(a => a.toLowerCase()));
  }

}

////////////////////
// Helpers        //
////////////////////

/**
 * @summary create a new group
 * @param {String} groupName
 */
Users.createGroup = groupName => {
  Users.groups[groupName] = new Group();
};

/**
 * @summary get a list of a user's groups
 * @param {Object} user
 */
Users.getGroups = (user, document) => {

  let userGroups = ['guests'];

  if (user) {

    userGroups.push('members');

    if (document && Users.owns(user, document)) {
      userGroups.push('owners');
    }

    if (user.groups) { // custom groups
      userGroups = userGroups.concat(user.groups);
    }

    if (Users.isAdmin(user)) { // admin
      userGroups.push('admins');
    }

  }

  return userGroups;

};

/**
 * @summary get a list of all the actions a user can perform
 * @param {Object} user
 */
Users.getActions = user => {
  let userGroups = Users.getGroups(user);
  if (!userGroups.includes('guests')) {
    // always give everybody permission for guests actions, too
    userGroups.push('guests');
  }
  let groupActions = userGroups.map(groupName => {
    // note: make sure groupName corresponds to an actual group
    const group = Users.groups[groupName];
    return group && group.actions;
  });
  return _.unique(_.flatten(groupActions));
};

/**
 * @summary check if a user is a member of a group
 * @param {Array} user
 * @param {String} group or array of groups
 */
Users.isMemberOf = (user, groupOrGroups, document) => {
  const groups = Array.isArray(groupOrGroups) ? groupOrGroups : [groupOrGroups];
  return intersection(Users.getGroups(user, document), groups).length > 0;
};

/**
 * @summary check if a user can perform at least one of the specified actions
 * @param {Object} user
 * @param {String/Array} action or actions
 */
Users.canDo = (user, actionOrActions) => {
  const authorizedActions = Users.getActions(user);
  const actions = Array.isArray(actionOrActions) ? actionOrActions : [actionOrActions];
  return Users.isAdmin(user) || intersection(authorizedActions, actions).length > 0;
};

// DEPRECATED
// TODO: remove this
/**
 * @summary Check if a user can edit a document
 * @param {Object} user - The user performing the action
 * @param {Object} document - The document being edited
 */
// Users.canEdit = function (user, document) {

//   user = (typeof user === 'undefined') ? Meteor.user() : user;

//   // note(apollo): use of `__typename` given by react-apollo
//   //const collectionName = document.getCollectionName();
//   const collectionName = document.__typename ? Utils.getCollectionNameFromTypename(document.__typename) : document.getCollectionName();

//   if (!user || !document) {
//     return false;
//   }

//   if (document.hasOwnProperty('isDeleted') && document.isDeleted) return false;

//   if (Users.owns(user, document)) {
//     // if this is user's document, check if user can edit own documents
//     return Users.canDo(user, `${collectionName}.edit.own`);
//   } else {
//     // if this is not user's document, check if they can edit all documents
//     return Users.canDo(user, `${collectionName}.edit.all`);
//   }

// };

/**
 * @summary Check if a user owns a document
 * @param {Object|string} userOrUserId - The user or their userId
 * @param {Object} document - The document to check (post, comment, user object, etc.)
 */
Users.owns = function (user, document) {
  try {
    if (!!document.userId) {
      // case 1: document is a post or a comment, use userId to check
      return user._id === document.userId;
    } else {
      // case 2: document is a user, use _id or slug to check
      return document.slug ? user.slug === document.slug : user._id === document._id;
    }
  } catch (e) {
    return false; // user not logged in
  }
};

/**
 * @summary Check if a user is an admin
 * @param {Object|string} userOrUserId - The user or their userId
 */
Users.isAdmin = function (userOrUserId) {
  try {
    var user = Users.getUser(userOrUserId);
    return !!user && !!user.isAdmin;
  } catch (e) {
    return false; // user not logged in
  }
};
Users.isAdminById = Users.isAdmin;

export const isAdmin = Users.isAdmin;

/**
 * @summary Check if a user can view a field
 * @param {Object} user - The user performing the action
 * @param {Object} field - The schema of the requested field
 * @param {Object} field - The full document of the collection
 * @returns {Boolean} - true if the user can read the field, false if not
 */
Users.canReadField = function (user, field, document) {
  const canRead = field.canRead || field.viewableBy; //OpenCRUD backwards compatibility
  if (canRead) {
    if (typeof canRead === 'function') {
      // if canRead is a function, execute it with user and document passed. it must return a boolean
      return canRead(user, document);
    } else if (typeof canRead === 'string') {
      // if canRead is just a string, we assume it's the name of a group and pass it to isMemberOf
      return canRead === 'guests' || Users.isMemberOf(user, canRead);
    } else if (Array.isArray(canRead) && canRead.length > 0) {
      // if canRead is an array, we do a recursion on every item and return true if one of the items return true
      return canRead.some(group => Users.canReadField(user, { canRead: group }, document));
    }
  }
  return false;
};

/**
 * @summary Get a list of fields viewable by a user
 * @param {Object} user - The user performing the action
 * @param {Object} collection - The collection
 * @param {Object} document - Optionally, get a list for a specific document
 */
Users.getViewableFields = function (user, collection, document) {
  deprecate('1.13.4', 'getViewableFields is deprecated. Use Users.getReadableProjection to get a Mongo projection, or Users.getReadableFields if you need an array of field.');
  return Users.getReadableProjection(user, collection, document);
};

Users.getReadableFields = function (user, collection, document) {
  return compact(map(collection.simpleSchema()._schema,
    (field, fieldName) => {
      if (fieldName.indexOf('.$') > -1) return null;
      return Users.canReadField(user, field, document) ? fieldName : null;
    }
  ));
};

Users.getReadableProjection = function (user, collection, document) {
  return Utils.arrayToFields(Users.getReadableFields(user, collection, document));
};

// collection helper
Users.helpers({
  getViewableFields(collection, document) {
    return Users.getViewableFields(this, collection, document);
  }
});

/**
 * @summary Check if a user can access a list of fields
 * @param {Object} user - The user performing the action
 * @param {Object} collection - The collection
 * @param {Object} fields - The list of fields
 */
Users.checkFields = (user, collection, fields) => {
  const viewableFields = Users.getReadableFields(user, collection);
  const diff = difference(fields, viewableFields);

  if (diff.length) {
    throw new Error(
      `You don't have permission to filter collection ${collection.options.collectionName} by the following fields: ${diff.join(
        ', '
      )}.`
    );
  }
  return true;
};

/**
 * @summary For a given document or list of documents, keep only fields viewable by current user
 * @param {Object} user - The user performing the action
 * @param {Object} collection - The collection
 * @param {Object} document - The document being returned by the resolver
 */
Users.restrictViewableFields = function (user, collection, docOrDocs) {

  if (!docOrDocs) return {};

  const restrictDoc = document => {

    // get array of all keys viewable by user
    const viewableKeys = Users.getReadableFields(user, collection, document);
    const restrictedDocument = _.clone(document);

    // loop over each property in the document and delete it if it's not viewable
    _.forEach(restrictedDocument, (value, key) => {
      if (!viewableKeys.includes(key)) {
        delete restrictedDocument[key];
      }
    });

    return restrictedDocument;

  };

  return Array.isArray(docOrDocs) ? docOrDocs.map(restrictDoc) : restrictDoc(docOrDocs);

};

/**
 * @summary Check if a user can submit a field
 * @param {Object} user - The user performing the action
 * @param {Object} field - The field being edited or inserted
 */
Users.canCreateField = function (user, field) {
  const canCreate = field.canCreate || field.insertableBy; //OpenCRUD backwards compatibility
  if (canCreate) {
    if (typeof canCreate === 'function') {
      // if canCreate is a function, execute it with user and document passed. it must return a boolean
      return canCreate(user);
    } else if (typeof canCreate === 'string') {
      // if canCreate is just a string, we assume it's the name of a group and pass it to isMemberOf
      // note: if canCreate is 'guests' then anybody can create it
      return canCreate === 'guests' || Users.isMemberOf(user, canCreate);
    } else if (Array.isArray(canCreate) && canCreate.length > 0) {
      // if canCreate is an array, we do a recursion on every item and return true if one of the items return true
      return canCreate.some(group => Users.canCreateField(user, { canCreate: group }));
    }
  }
  return false;
};

/** @function
 * Check if a user can edit a field
 * @param {Object} user - The user performing the action
 * @param {Object} field - The field being edited or inserted
 * @param {Object} document - The document being edited or inserted
 */
Users.canUpdateField = function (user, field, document) {
  const canUpdate = field.canUpdate || field.editableBy; //OpenCRUD backwards compatibility

  if (canUpdate) {
    if (typeof canUpdate === 'function') {
      // if canUpdate is a function, execute it with user and document passed. it must return a boolean
      return canUpdate(user, document);
    } else if (typeof canUpdate === 'string') {
      // if canUpdate is just a string, we assume it's the name of a group and pass it to isMemberOf
      // note: if canUpdate is 'guests' then anybody can create it
      return canUpdate === 'guests' || Users.isMemberOf(user, canUpdate);
    } else if (Array.isArray(canUpdate) && canUpdate.length > 0) {
      // if canUpdate is an array, we look at every item and return true if one of the items return true
      return canUpdate.some(group => Users.canUpdateField(user, { canUpdate: group }, document));

    }
  }
  return false;
};

/** @function
 * Check if a user passes a permission check (new API)
 * @param {Object} check - The permission check being tested
 * @param {Object} user - The user performing the action
 * @param {Object} document - The document being edited or inserted
 */
Users.permissionCheck = (options) => {
  const { check, user, document } = options;
  if (Users.isAdmin(user)) {
    // admins always pass all permission checks
    return true;
  } else if (typeof check === 'function') {
    return check(options);
  } else if (Array.isArray(check)) {
    return Users.isMemberOf(user, check, document);
  }
};

Users.canCreate = options => {
  const { collectionName, collection = getCollection(collectionName) } = options;
  const check = get(collection, 'options.permissions.canCreate');
  if (!check) {
    // eslint-disable-next-line no-console
    console.warn(`Users.canCreate() was called but no [canCreate] permission was defined for collection [${collection.options.collectionName}]`);
  }
  return check && Users.permissionCheck({ ...options, check, operationName: 'create' });
};

Users.canUpdate = options => {
  const { collectionName, collection = getCollection(collectionName) } = options;
  const check = get(collection, 'options.permissions.canUpdate');
  if (!check) {
    // eslint-disable-next-line no-console
    console.warn(`Users.canUpdate() was called but no [canUpdate] permission was defined for collection [${collection.options.collectionName}]`);
  }
  return check && Users.permissionCheck({ ...options, check, operationName: 'update' });
};

Users.canDelete = options => {
  const { collectionName, collection = getCollection(collectionName) } = options;
  const check = get(collection, 'options.permissions.canDelete');
  if (!check) {
    // eslint-disable-next-line no-console
    console.warn(`Users.canDelete() was called but no [canDelete] permission was defined for collection [${collection.options.collectionName}]`);
  }
  return check && Users.permissionCheck({ ...options, check, operationName: 'delete' });
};

////////////////////
// Initialize     //
////////////////////

/**
 * @summary initialize the 3 out-of-the-box groups
 */
Users.createGroup('guests'); // non-logged-in users
Users.createGroup('members'); // regular users

const membersActions = [
  'user.create',
  'user.update.own',
  // OpenCRUD backwards compatibility
  'users.new',
  'users.edit.own',
  'users.remove.own',
];
Users.groups.members.can(membersActions);

Users.createGroup('admins'); // admin users

const adminActions = [
  'user.create',
  'user.update.all',
  'user.delete.all',
  'setting.update',
  // OpenCRUD backwards compatibility
  'users.new',
  'users.edit.all',
  'users.remove.all',
  'settings.edit',
];
Users.groups.admins.can(adminActions);
<commit_msg>Add Users.canRead and Users.restrictDocuments<commit_after>import Users from './collection.js';
import intersection from 'lodash/intersection';
import compact from 'lodash/compact';
import map from 'lodash/map';
import difference from 'lodash/difference';
import get from 'lodash/get';
import { getCollection } from 'meteor/vulcan:lib';
import { Utils, deprecate } from 'meteor/vulcan:lib';

/**
 * @summary Users.groups object
 */
Users.groups = {};

/**
 * @summary Group class
 */
class Group {
  constructor() {
    this.actions = [];
  }

  can(actions) {
    actions = Array.isArray(actions) ? actions : [actions];
    this.actions = this.actions.concat(actions.map(a => a.toLowerCase()));
  }

  cannot(actions) {
    actions = Array.isArray(actions) ? actions : [actions];
    this.actions = _.difference(this.actions, actions.map(a => a.toLowerCase()));
  }
}

////////////////////
// Helpers        //
////////////////////

/**
 * @summary create a new group
 * @param {String} groupName
 */
Users.createGroup = groupName => {
  Users.groups[groupName] = new Group();
};

/**
 * @summary get a list of a user's groups
 * @param {Object} user
 */
Users.getGroups = (user, document) => {
  let userGroups = ['guests'];

  if (user) {
    userGroups.push('members');

    if (document && Users.owns(user, document)) {
      userGroups.push('owners');
    }

    if (user.groups) {
      // custom groups
      userGroups = userGroups.concat(user.groups);
    }

    if (Users.isAdmin(user)) {
      // admin
      userGroups.push('admins');
    }
  }

  return userGroups;
};

/**
 * @summary get a list of all the actions a user can perform
 * @param {Object} user
 */
Users.getActions = user => {
  let userGroups = Users.getGroups(user);
  if (!userGroups.includes('guests')) {
    // always give everybody permission for guests actions, too
    userGroups.push('guests');
  }
  let groupActions = userGroups.map(groupName => {
    // note: make sure groupName corresponds to an actual group
    const group = Users.groups[groupName];
    return group && group.actions;
  });
  return _.unique(_.flatten(groupActions));
};

/**
 * @summary check if a user is a member of a group
 * @param {Array} user
 * @param {String} group or array of groups
 */
Users.isMemberOf = (user, groupOrGroups, document) => {
  const groups = Array.isArray(groupOrGroups) ? groupOrGroups : [groupOrGroups];
  return intersection(Users.getGroups(user, document), groups).length > 0;
};

/**
 * @summary check if a user can perform at least one of the specified actions
 * @param {Object} user
 * @param {String/Array} action or actions
 */
Users.canDo = (user, actionOrActions) => {
  const authorizedActions = Users.getActions(user);
  const actions = Array.isArray(actionOrActions) ? actionOrActions : [actionOrActions];
  return Users.isAdmin(user) || intersection(authorizedActions, actions).length > 0;
};

// DEPRECATED
// TODO: remove this
/**
 * @summary Check if a user can edit a document
 * @param {Object} user - The user performing the action
 * @param {Object} document - The document being edited
 */
// Users.canEdit = function (user, document) {

//   user = (typeof user === 'undefined') ? Meteor.user() : user;

//   // note(apollo): use of `__typename` given by react-apollo
//   //const collectionName = document.getCollectionName();
//   const collectionName = document.__typename ? Utils.getCollectionNameFromTypename(document.__typename) : document.getCollectionName();

//   if (!user || !document) {
//     return false;
//   }

//   if (document.hasOwnProperty('isDeleted') && document.isDeleted) return false;

//   if (Users.owns(user, document)) {
//     // if this is user's document, check if user can edit own documents
//     return Users.canDo(user, `${collectionName}.edit.own`);
//   } else {
//     // if this is not user's document, check if they can edit all documents
//     return Users.canDo(user, `${collectionName}.edit.all`);
//   }

// };

/**
 * @summary Check if a user owns a document
 * @param {Object|string} userOrUserId - The user or their userId
 * @param {Object} document - The document to check (post, comment, user object, etc.)
 */
Users.owns = function(user, document) {
  try {
    if (!!document.userId) {
      // case 1: document is a post or a comment, use userId to check
      return user._id === document.userId;
    } else {
      // case 2: document is a user, use _id or slug to check
      return document.slug ? user.slug === document.slug : user._id === document._id;
    }
  } catch (e) {
    return false; // user not logged in
  }
};

/**
 * @summary Check if a user is an admin
 * @param {Object|string} userOrUserId - The user or their userId
 */
Users.isAdmin = function(userOrUserId) {
  try {
    var user = Users.getUser(userOrUserId);
    return !!user && !!user.isAdmin;
  } catch (e) {
    return false; // user not logged in
  }
};
Users.isAdminById = Users.isAdmin;

export const isAdmin = Users.isAdmin;

/**
 * @summary Check if a user can view a field
 * @param {Object} user - The user performing the action
 * @param {Object} field - The schema of the requested field
 * @param {Object} field - The full document of the collection
 * @returns {Boolean} - true if the user can read the field, false if not
 */
Users.canReadField = function(user, field, document) {
  const canRead = field.canRead || field.viewableBy; //OpenCRUD backwards compatibility
  if (canRead) {
    if (typeof canRead === 'function') {
      // if canRead is a function, execute it with user and document passed. it must return a boolean
      return canRead(user, document);
    } else if (typeof canRead === 'string') {
      // if canRead is just a string, we assume it's the name of a group and pass it to isMemberOf
      return canRead === 'guests' || Users.isMemberOf(user, canRead);
    } else if (Array.isArray(canRead) && canRead.length > 0) {
      // if canRead is an array, we do a recursion on every item and return true if one of the items return true
      return canRead.some(group => Users.canReadField(user, { canRead: group }, document));
    }
  }
  return false;
};

/**
 * @summary Get a list of fields viewable by a user
 * @param {Object} user - The user performing the action
 * @param {Object} collection - The collection
 * @param {Object} document - Optionally, get a list for a specific document
 */
Users.getViewableFields = function(user, collection, document) {
  deprecate(
    '1.13.4',
    'getViewableFields is deprecated. Use Users.getReadableProjection to get a Mongo projection, or Users.getReadableFields if you need an array of field.'
  );
  return Users.getReadableProjection(user, collection, document);
};

Users.getReadableFields = function(user, collection, document) {
  return compact(
    map(collection.simpleSchema()._schema, (field, fieldName) => {
      if (fieldName.indexOf('.$') > -1) return null;
      return Users.canReadField(user, field, document) ? fieldName : null;
    })
  );
};

Users.getReadableProjection = function(user, collection, document) {
  return Utils.arrayToFields(Users.getReadableFields(user, collection, document));
};

// collection helper
Users.helpers({
  getViewableFields(collection, document) {
    return Users.getViewableFields(this, collection, document);
  },
});

/**
 * @summary Check if a user can access a list of fields
 * @param {Object} user - The user performing the action
 * @param {Object} collection - The collection
 * @param {Object} fields - The list of fields
 */
Users.checkFields = (user, collection, fields) => {
  const viewableFields = Users.getReadableFields(user, collection);
  const diff = difference(fields, viewableFields);

  if (diff.length) {
    throw new Error(
      `You don't have permission to filter collection ${
        collection.options.collectionName
      } by the following fields: ${diff.join(', ')}.`
    );
  }
  return true;
};

/**
 * @summary For a given document or list of documents, keep only fields viewable by current user
 * @param {Object} user - The user performing the action
 * @param {Object} collection - The collection
 * @param {Object} document - The document being returned by the resolver
 */
Users.restrictViewableFields = function(user, collection, docOrDocs) {
  if (!docOrDocs) return {};

  const restrictDoc = document => {
    // get array of all keys viewable by user
    const viewableKeys = Users.getReadableFields(user, collection, document);
    const restrictedDocument = _.clone(document);

    // loop over each property in the document and delete it if it's not viewable
    _.forEach(restrictedDocument, (value, key) => {
      if (!viewableKeys.includes(key)) {
        delete restrictedDocument[key];
      }
    });

    return restrictedDocument;
  };

  return Array.isArray(docOrDocs) ? docOrDocs.map(restrictDoc) : restrictDoc(docOrDocs);
};

/**
 * @summary For a given of documents, keep only documents and fields viewable by current user (new APIs)
 * @param {Object} user - The user performing the action
 * @param {Object} collection - The collection
 * @param {Object} document - The document being returned by the resolver
 */
Users.restrictDocuments = function({ user, collection, documents }) {
  const check = get(collection, 'options.permissions.canRead');
  let readableDocuments = documents;
  if (check) {
    readableDocuments = documents.filter(comment =>
      Users.canRead({ collection, document: comment, user })
    );
  }
  const restrictedDocuments = Users.restrictViewableFields(user, collection, readableDocuments);
  return restrictedDocuments;
};

/**
 * @summary Check if a user can submit a field
 * @param {Object} user - The user performing the action
 * @param {Object} field - The field being edited or inserted
 */
Users.canCreateField = function(user, field) {
  const canCreate = field.canCreate || field.insertableBy; //OpenCRUD backwards compatibility
  if (canCreate) {
    if (typeof canCreate === 'function') {
      // if canCreate is a function, execute it with user and document passed. it must return a boolean
      return canCreate(user);
    } else if (typeof canCreate === 'string') {
      // if canCreate is just a string, we assume it's the name of a group and pass it to isMemberOf
      // note: if canCreate is 'guests' then anybody can create it
      return canCreate === 'guests' || Users.isMemberOf(user, canCreate);
    } else if (Array.isArray(canCreate) && canCreate.length > 0) {
      // if canCreate is an array, we do a recursion on every item and return true if one of the items return true
      return canCreate.some(group => Users.canCreateField(user, { canCreate: group }));
    }
  }
  return false;
};

/** @function
 * Check if a user can edit a field
 * @param {Object} user - The user performing the action
 * @param {Object} field - The field being edited or inserted
 * @param {Object} document - The document being edited or inserted
 */
Users.canUpdateField = function(user, field, document) {
  const canUpdate = field.canUpdate || field.editableBy; //OpenCRUD backwards compatibility

  if (canUpdate) {
    if (typeof canUpdate === 'function') {
      // if canUpdate is a function, execute it with user and document passed. it must return a boolean
      return canUpdate(user, document);
    } else if (typeof canUpdate === 'string') {
      // if canUpdate is just a string, we assume it's the name of a group and pass it to isMemberOf
      // note: if canUpdate is 'guests' then anybody can create it
      return canUpdate === 'guests' || Users.isMemberOf(user, canUpdate);
    } else if (Array.isArray(canUpdate) && canUpdate.length > 0) {
      // if canUpdate is an array, we look at every item and return true if one of the items return true
      return canUpdate.some(group => Users.canUpdateField(user, { canUpdate: group }, document));
    }
  }
  return false;
};

/** @function
 * Check if a user passes a permission check (new API)
 * @param {Object} check - The permission check being tested
 * @param {Object} user - The user performing the action
 * @param {Object} document - The document being edited or inserted
 */
Users.permissionCheck = options => {
  const { check, user, document } = options;
  if (Users.isAdmin(user)) {
    // admins always pass all permission checks
    return true;
  } else if (typeof check === 'function') {
    return check(options);
  } else if (Array.isArray(check)) {
    return Users.isMemberOf(user, check, document);
  }
};

Users.canRead = options => {
  const { collectionName, collection = getCollection(collectionName) } = options;
  const check = get(collection, 'options.permissions.canRead');
  if (!check) {
    // eslint-disable-next-line no-console
    console.warn(
      `Users.canRead() was called but no [canRead] permission was defined for collection [${
        collection.options.collectionName
      }]`
    );
  }
  return check && Users.permissionCheck({ ...options, check, operationName: 'read' });
};

Users.canCreate = options => {
  const { collectionName, collection = getCollection(collectionName) } = options;
  const check = get(collection, 'options.permissions.canCreate');
  if (!check) {
    // eslint-disable-next-line no-console
    console.warn(
      `Users.canCreate() was called but no [canCreate] permission was defined for collection [${
        collection.options.collectionName
      }]`
    );
  }
  return check && Users.permissionCheck({ ...options, check, operationName: 'create' });
};

Users.canUpdate = options => {
  const { collectionName, collection = getCollection(collectionName) } = options;
  const check = get(collection, 'options.permissions.canUpdate');
  if (!check) {
    // eslint-disable-next-line no-console
    console.warn(
      `Users.canUpdate() was called but no [canUpdate] permission was defined for collection [${
        collection.options.collectionName
      }]`
    );
  }
  return check && Users.permissionCheck({ ...options, check, operationName: 'update' });
};

Users.canDelete = options => {
  const { collectionName, collection = getCollection(collectionName) } = options;
  const check = get(collection, 'options.permissions.canDelete');
  if (!check) {
    // eslint-disable-next-line no-console
    console.warn(
      `Users.canDelete() was called but no [canDelete] permission was defined for collection [${
        collection.options.collectionName
      }]`
    );
  }
  return check && Users.permissionCheck({ ...options, check, operationName: 'delete' });
};

////////////////////
// Initialize     //
////////////////////

/**
 * @summary initialize the 3 out-of-the-box groups
 */
Users.createGroup('guests'); // non-logged-in users
Users.createGroup('members'); // regular users

const membersActions = [
  'user.create',
  'user.update.own',
  // OpenCRUD backwards compatibility
  'users.new',
  'users.edit.own',
  'users.remove.own',
];
Users.groups.members.can(membersActions);

Users.createGroup('admins'); // admin users

const adminActions = [
  'user.create',
  'user.update.all',
  'user.delete.all',
  'setting.update',
  // OpenCRUD backwards compatibility
  'users.new',
  'users.edit.all',
  'users.remove.all',
  'settings.edit',
];
Users.groups.admins.can(adminActions);
<|endoftext|>


<commit_before>const section = {
    0: '.aboutMe',
    1: '.work',
    2: '.social'
};

function scroll(num) {
    $('body').animate({
        scrollTop: $(section[num]).offset().top
    }, 1000);
}

const translateX = [222, 250, 211, 210, 250, 226, 207, 236, 216, 200, 180, 176, 150, 142, 123, 126, 130, 107, 133, 100, 138, 198, 200, 189, 177, 178];
const translateY = [155, 110, 151, 98, 80, 82, 64, 56, 50, 42, 34, 22, 34, 27, 38, 51, 87, 55, 74, 100, 97, 51, 167, 166, 188, 221];
const deg = [210, 40, 36, 10, 0, -5, 0, -40, -21, -80, -90, 80, -115, 50, 30, 0, 180, 15, 0, -80, 80, -45, -148, 30, 18, -180];
const scale = [1, 1, 0.65, 1, 1, 1, 1, 1, 1, 1, 0.6, 0.5, 0.5, 0.5, 0.6, 0.7, 0.8, 0.7, 0.7, 0.9, 0.7, 0.6, 0.65, 0.7, 0.6, 0.35];
const fill = '#446CB3';
const path1 = 'M3.6,5c0.6,0.2,1,0.4,1.4,0.6c1.8,0.8,2.4,1.9,2.2,3.7c-0.2,1.5-1.1,2.5-2.7,2.7c-1.7,0.3-3.2-0.4-4-1.9c-1.4-2.6-0.3-6.4,2.5-8.2c3.7-2.4,7.7-2.5,11.6-0.6c4.7,2.3,5.2,8.1,1,11.3c-0.4,0.3-0.8,0.6-1.2,0.9c-3.4,2.5-3.7,2.8-4.5,6.7c-0.6,0-1.2,0-1.9,0.1C8,17.5,8.6,15.2,9.9,13c1.1-1.9,2-3.9,1.8-6.3c-0.2-2-1.4-3.5-3.3-3.9C6.4,2.4,4.7,3.2,3.6,5z';
const path2 = 'M8.5,30.6c-2.1,0-3.7-1.7-3.6-3.8c0.1-2.1,1.7-3.7,3.8-3.6c2,0.1,3.6,1.7,3.6,3.7C12.2,29,10.6,30.6,8.5,30.6z';

const svgNS = 'http://www.w3.org/2000/svg';
const mainG = document.getElementById('questions');

for (var i = 0; i < deg.length; i++) {
    const group = document.createElementNS(svgNS, 'g');
    const top = document.createElementNS(svgNS, 'path');
    const btm = document.createElementNS(svgNS, 'path');

    group.setAttributeNS(null, 'transform', 'translate(' + translateX[i] + ',' + translateY[i] + ') rotate(' + deg[i] + ') scale(' + scale[i] + ')');
    top.setAttributeNS(null, 'd', path1);
    top.setAttributeNS(null, 'fill', fill);
    btm.setAttributeNS(null, 'd', path2);
    btm.setAttributeNS(null, 'fill', fill);

    group.appendChild(top);
    group.appendChild(btm);

    mainG.appendChild(group);
}

<commit_msg>Update script.js<commit_after>
<|endoftext|>