<commit_before>
import errors from 'feathers-errors';

import getByDot from '../common/get-by-dot';
import setByDot from '../common/set-by-dot';
import getItems from './get-items';
import legacyPopulate from './legacy-populate';
import replaceItems from './replace-items';

export default function (options, ...rest) {
  // options.schema is like { service: '...', permissions: '...', include: [ ... ] }
  if (typeof options === 'string') {
    return legacyPopulate(options, ...rest);
  }

  return function (hook) {
    const optionsDefault = {
      schema: {},
      checkPermissions: () => true,
      profile: false
    };

    if (hook.params._populate === 'skip') { // this service call made from another populate
      return hook;
    }

    return Promise.resolve()
      .then(() => {
        // 'options.schema' resolves to { permissions: '...', include: [ ... ] }

        const items = getItems(hook);
        const options1 = Object.assign({}, optionsDefault, options);
        const { schema, checkPermissions } = options1;
        const schema1 = typeof schema === 'function' ? schema(hook, options1) : schema;
        const permissions = schema1.permissions || null;
        const baseService = schema1.service;

        if (typeof checkPermissions !== 'function') {
          throw new errors.BadRequest('Permissions param is not a function. (populate)');
        }

        if (baseService && baseService !== hook.path) {
          throw new errors.BadRequest(`Schema is for ${baseService} not ${hook.path}. (populate)`);
        }

        if (permissions && !checkPermissions(hook, hook.path, permissions, 0)) {
          throw new errors.BadRequest('Permissions do not allow this populate. (populate)');
        }

        if (typeof schema1 !== 'object') {
          throw new errors.BadRequest('Schema does not resolve to an object. (populate)');
        }

        const include = [].concat(schema1.include || []);
        return !include.length ? items : populateItemArray(options1, hook, items, include, 0);
      })
      .then(items => {
        replaceItems(hook, items);
        return hook;
      });
  };
}

function populateItemArray (options, hook, items, includeSchema, depth) {
  // 'items' is an item or an array of items
  // 'includeSchema' is like [ { nameAs: 'author', ... }, { nameAs: 'readers', ... } ]

  if (!Array.isArray(items)) {
    return populateItem(options, hook, items, includeSchema, depth + 1);
  }

  return Promise.all(
    items.map(item => populateItem(options, hook, item, includeSchema, depth + 1))
  );
}

function populateItem (options, hook, item, includeSchema, depth) {
  // 'item' is one item
  // 'includeSchema' is like [ { nameAs: 'author', ... }, { nameAs: 'readers', ... } ]

  const elapsed = {};
  const startAtAllIncludes = process.hrtime();
  const include = [].concat(includeSchema || []);
  item._include = [];

  return Promise.all(
    include.map(childSchema => {
      const startAtThisInclude = process.hrtime();
      return populateAddChild(options, hook, item, childSchema, depth)
        .then(result => {
          const nameAs = childSchema.nameAs || childSchema.service;
          elapsed[nameAs] = getElapsed(options, startAtThisInclude, depth);

          return result;
        });
    })
  )
    .then(children => {
      // 'children' is like
      //   [{ nameAs: 'authorInfo', items: {...} }, { nameAs: readersInfo, items: [{...}, {...}] }]
      if (options.profile !== false) {
        elapsed.total = getElapsed(options, startAtAllIncludes, depth);
        item._elapsed = elapsed;
      }

      children.forEach(({ nameAs, items }) => {
        setByDot(item, nameAs, items);
      });

      return item;
    });
}

function populateAddChild (options, hook, parentItem, childSchema, depth) {
  /*
  @params
    'parentItem' is the item we are adding children to
    'childSchema' is like
      { service: 'comments',
        permissions: '...',
        nameAs: 'comments',
        asArray: true,
        parentField: 'id',
        childField: 'postId',
        query: { $limit: 5, $select: ['title', 'content', 'postId'], $sort: { createdAt: -1 } },
        select: (hook, parent, depth) => ({ something: { $exists: false }}),
        paginate: false,
        include: [ ... ] }
  @returns { nameAs: string, items: array }
  */

  // note: parentField & childField are req'd, plus parentItem[parentField} !== undefined .
  // childSchema.select may override their relationship but some relationship must be given.
  if (!childSchema.service || !childSchema.parentField || !childSchema.childField) {
    throw new errors.BadRequest('Child schema is missing a required property. (populate)');
  }

  if (childSchema.permissions &&
    !options.checkPermissions(hook, childSchema.service, childSchema.permissions, depth)
  ) {
    throw new errors.BadRequest(
      `Permissions for ${childSchema.service} do not allow include. (populate)`
    );
  }

  const nameAs = childSchema.nameAs || childSchema.service;
  parentItem._include.push(nameAs);

  let promise = Promise.resolve()
    .then(() => (childSchema.select ? childSchema.select(hook, parentItem, depth) : {}))
    .then(selectQuery => {
      const parentVal = getByDot(parentItem, childSchema.parentField);

      if (parentVal === undefined) {
        throw new errors.BadRequest(
          `ParentField ${childSchema.parentField} for ${nameAs} depth ${depth} is undefined. (populate)`
        );
      }

      const query = Object.assign({},
        childSchema.query,
        { [childSchema.childField]: Array.isArray(parentVal) ? { $in: parentVal } : parentVal },
        selectQuery // dynamic options override static ones
      );

      const serviceHandle = hook.app.service(childSchema.service);

      if (!serviceHandle) {
        throw new errors.BadRequest(`Service ${childSchema.service} is not configured. (populate)`);
      }

      let paginate = { paginate: false };
      const paginateOption = childSchema.paginate;
      if (paginateOption === true) { paginate = null; }
      if (typeof paginateOption === 'number') { paginate = { paginate: { default: paginateOption } }; }

      const params = Object.assign({}, hook.params, paginate, { query, _populate: 'skip' });
      return serviceHandle.find(params);
    })
    .then(result => {
      result = result.data || result;

      if (result.length === 1 && !childSchema.asArray) {
        result = result[0];
      }

      return result;
    });

  if (childSchema.include) {
    promise = promise
      .then(items => populateItemArray(options, hook, items, childSchema.include, depth));
  }

  return promise
    .then(items => ({ nameAs, items }));
}

// Helpers

function getElapsed (options, startHrtime, depth) {
  if (options.profile === true) {
    const elapsed = process.hrtime(startHrtime);
    return elapsed[0] * 1e9 + elapsed[1];
  } else if (options.profile !== false) {
    return depth; // for testing _elapsed
  }
}
<commit_msg>Changed populate so it throws if option.schema not an object<commit_after>
import errors from 'feathers-errors';

import getByDot from '../common/get-by-dot';
import setByDot from '../common/set-by-dot';
import getItems from './get-items';
import legacyPopulate from './legacy-populate';
import replaceItems from './replace-items';

export default function (options, ...rest) {
  // options.schema is like { service: '...', permissions: '...', include: [ ... ] }
  if (typeof options === 'string') {
    return legacyPopulate(options, ...rest);
  }

  if (typeof options.schema !== object || options.schema === null) {
    throw new Error('Options.schema is not an object. (populate)');
  }

  return function (hook) {
    const optionsDefault = {
      schema: {},
      checkPermissions: () => true,
      profile: false
    };

    if (hook.params._populate === 'skip') { // this service call made from another populate
      return hook;
    }

    return Promise.resolve()
      .then(() => {
        // 'options.schema' resolves to { permissions: '...', include: [ ... ] }

        const items = getItems(hook);
        const options1 = Object.assign({}, optionsDefault, options);
        const { schema, checkPermissions } = options1;
        const schema1 = typeof schema === 'function' ? schema(hook, options1) : schema;
        const permissions = schema1.permissions || null;
        const baseService = schema1.service;

        if (typeof checkPermissions !== 'function') {
          throw new errors.BadRequest('Permissions param is not a function. (populate)');
        }

        if (baseService && baseService !== hook.path) {
          throw new errors.BadRequest(`Schema is for ${baseService} not ${hook.path}. (populate)`);
        }

        if (permissions && !checkPermissions(hook, hook.path, permissions, 0)) {
          throw new errors.BadRequest('Permissions do not allow this populate. (populate)');
        }

        if (typeof schema1 !== 'object') {
          throw new errors.BadRequest('Schema does not resolve to an object. (populate)');
        }

        const include = [].concat(schema1.include || []);
        return !include.length ? items : populateItemArray(options1, hook, items, include, 0);
      })
      .then(items => {
        replaceItems(hook, items);
        return hook;
      });
  };
}

function populateItemArray (options, hook, items, includeSchema, depth) {
  // 'items' is an item or an array of items
  // 'includeSchema' is like [ { nameAs: 'author', ... }, { nameAs: 'readers', ... } ]

  if (!Array.isArray(items)) {
    return populateItem(options, hook, items, includeSchema, depth + 1);
  }

  return Promise.all(
    items.map(item => populateItem(options, hook, item, includeSchema, depth + 1))
  );
}

function populateItem (options, hook, item, includeSchema, depth) {
  // 'item' is one item
  // 'includeSchema' is like [ { nameAs: 'author', ... }, { nameAs: 'readers', ... } ]

  const elapsed = {};
  const startAtAllIncludes = process.hrtime();
  const include = [].concat(includeSchema || []);
  item._include = [];

  return Promise.all(
    include.map(childSchema => {
      const startAtThisInclude = process.hrtime();
      return populateAddChild(options, hook, item, childSchema, depth)
        .then(result => {
          const nameAs = childSchema.nameAs || childSchema.service;
          elapsed[nameAs] = getElapsed(options, startAtThisInclude, depth);

          return result;
        });
    })
  )
    .then(children => {
      // 'children' is like
      //   [{ nameAs: 'authorInfo', items: {...} }, { nameAs: readersInfo, items: [{...}, {...}] }]
      if (options.profile !== false) {
        elapsed.total = getElapsed(options, startAtAllIncludes, depth);
        item._elapsed = elapsed;
      }

      children.forEach(({ nameAs, items }) => {
        setByDot(item, nameAs, items);
      });

      return item;
    });
}

function populateAddChild (options, hook, parentItem, childSchema, depth) {
  /*
  @params
    'parentItem' is the item we are adding children to
    'childSchema' is like
      { service: 'comments',
        permissions: '...',
        nameAs: 'comments',
        asArray: true,
        parentField: 'id',
        childField: 'postId',
        query: { $limit: 5, $select: ['title', 'content', 'postId'], $sort: { createdAt: -1 } },
        select: (hook, parent, depth) => ({ something: { $exists: false }}),
        paginate: false,
        include: [ ... ] }
  @returns { nameAs: string, items: array }
  */

  // note: parentField & childField are req'd, plus parentItem[parentField} !== undefined .
  // childSchema.select may override their relationship but some relationship must be given.
  if (!childSchema.service || !childSchema.parentField || !childSchema.childField) {
    throw new errors.BadRequest('Child schema is missing a required property. (populate)');
  }

  if (childSchema.permissions &&
    !options.checkPermissions(hook, childSchema.service, childSchema.permissions, depth)
  ) {
    throw new errors.BadRequest(
      `Permissions for ${childSchema.service} do not allow include. (populate)`
    );
  }

  const nameAs = childSchema.nameAs || childSchema.service;
  parentItem._include.push(nameAs);

  let promise = Promise.resolve()
    .then(() => (childSchema.select ? childSchema.select(hook, parentItem, depth) : {}))
    .then(selectQuery => {
      const parentVal = getByDot(parentItem, childSchema.parentField);

      if (parentVal === undefined) {
        throw new errors.BadRequest(
          `ParentField ${childSchema.parentField} for ${nameAs} depth ${depth} is undefined. (populate)`
        );
      }

      const query = Object.assign({},
        childSchema.query,
        { [childSchema.childField]: Array.isArray(parentVal) ? { $in: parentVal } : parentVal },
        selectQuery // dynamic options override static ones
      );

      const serviceHandle = hook.app.service(childSchema.service);

      if (!serviceHandle) {
        throw new errors.BadRequest(`Service ${childSchema.service} is not configured. (populate)`);
      }

      let paginate = { paginate: false };
      const paginateOption = childSchema.paginate;
      if (paginateOption === true) { paginate = null; }
      if (typeof paginateOption === 'number') { paginate = { paginate: { default: paginateOption } }; }

      const params = Object.assign({}, hook.params, paginate, { query, _populate: 'skip' });
      return serviceHandle.find(params);
    })
    .then(result => {
      result = result.data || result;

      if (result.length === 1 && !childSchema.asArray) {
        result = result[0];
      }

      return result;
    });

  if (childSchema.include) {
    promise = promise
      .then(items => populateItemArray(options, hook, items, childSchema.include, depth));
  }

  return promise
    .then(items => ({ nameAs, items }));
}

// Helpers

function getElapsed (options, startHrtime, depth) {
  if (options.profile === true) {
    const elapsed = process.hrtime(startHrtime);
    return elapsed[0] * 1e9 + elapsed[1];
  } else if (options.profile !== false) {
    return depth; // for testing _elapsed
  }
}
<|endoftext|>


<commit_before>/**
 * @since 0.0.1
 * @copyright 2015 Spatial Vision, Inc. http://spatialvision.com.au
 * @license The MIT License
 * @author Spatial Vision
 * @version 0.1.0
 */

'use strict';

/**
 * nutrientCalculator/nutrientCalculatorSession singleton
 * @private-module nutrientCalculator/nutrientCalculatorSession
 */
angular.module('farmbuild.nutrientCalculator')
  .factory('nutrientCalculatorSession',
  function ($log, farmdata, validations) {

    var nutrientCalculatorSession = {},
      _isDefined = validations.isDefined;

//    function findInSessionStorage() {
//      var root = farmdata.session.find();
//      return root.nutrientCalculator.milkSold;
//    };
//
//    function saveInSessionStorage(result) {
//      var farmData = farmdata.session.find();
//      farmData.dateLastUpdated = new Date();
//      farmData.nutrientCalculator.milkSold = result;
//      farmdata.session.save(farmData);
//    };

    function load() {
      var root = farmdata.session.find();

      if(!_isDefined(root)) {
        return undefined;
      }

      return root.nutrientCalculator;
    }
    //nutrientCalculatorSession

    /**
     * Saves the farmData.nutrientCalculator into the sessionStorage
     * @method saveSection
     * @param {!object} farmData.nutrientCalculator
     * @returns {Object} farmData.nutrientCalculator
     * @private
     * @static
     */
    nutrientCalculatorSession.saveSection = function(section, value) {
      var loaded = load();

      if(!_isDefined(loaded)) {
        $log.error('Unable to find an existing farmData! please create then save.');
        return nutrientCalculatorSession
      }

      loaded[section] = value;

      return save(loaded);
    }

    /**
     * Saves the farmData.nutrientCalculator into the sessionStorage
     * @method save
     * @param {!object} farmData.nutrientCalculator
     * @returns {Object} farmData.nutrientCalculator
     * @public
     * @static
     */
    function save(toSave) {
      var farmData = farmdata.session.find();

      if(!_isDefined(farmData)) {
        $log.error('Unable to find the farmData in the session!');
        return undefined;
      }

      farmData.dateLastUpdated = new Date();

      farmData.nutrientCalculator = toSave;
      farmdata.session.save(farmData);

      return toSave;
    }
    nutrientCalculatorSession.save = save;

    /**
     * Returns a section of the farmData as an object from the sessionStorage
     * @method loadSection
     * @returns {Object} the farmdata, null, if not found.
     * @public
     * @static
     */
    nutrientCalculatorSession.loadSection = function(section) {
      var loaded = load();
      return loaded?loaded[section]:null;
    }

    /**
     * Returns true if the location.search has ?load=true, false otherwise
     * @method isLoadFlagSet
     * @param {object} location instance
     * @returns {boolean} Returns true if the location.search has ?load=true, false otherwise
     * @public
     * @static
     */
    nutrientCalculatorSession.isLoadFlagSet = function(location) {
      var load = false;

      if(location.href.split('?').length > 1 &&
        location.href.split('?')[1].indexOf('load') === 0){
        load = (location.href.split('?')[1].split('=')[1] === 'true');
      }

      return load;
    }

    nutrientCalculatorSession.find = function() {
      return farmdata.session.find();
    }

    return nutrientCalculatorSession;

  });
<commit_msg>cleaned<commit_after>/**
 * @since 0.0.1
 * @copyright 2015 Spatial Vision, Inc. http://spatialvision.com.au
 * @license The MIT License
 * @author Spatial Vision
 * @version 0.1.0
 */

'use strict';

/**
 * nutrientCalculator/nutrientCalculatorSession singleton
 * @private-module nutrientCalculator/nutrientCalculatorSession
 */
angular.module('farmbuild.nutrientCalculator')
  .factory('nutrientCalculatorSession',
  function ($log, farmdata, validations) {

    var nutrientCalculatorSession = {},
      _isDefined = validations.isDefined;

    function load() {
      var root = farmdata.session.find();

      if(!_isDefined(root)) {
        return undefined;
      }

      return root.nutrientCalculator;
    }

    /**
     * Saves the farmData.nutrientCalculator into the sessionStorage
     * @method saveSection
     * @param {!object} farmData.nutrientCalculator
     * @returns {Object} farmData.nutrientCalculator
     * @private
     * @static
     */
    nutrientCalculatorSession.saveSection = function(section, value) {
      var loaded = load();

      if(!_isDefined(loaded)) {
        $log.error('Unable to find an existing farmData! please create then save.');
        return nutrientCalculatorSession
      }

      loaded[section] = value;

      return save(loaded);
    }

    /**
     * Saves the farmData.nutrientCalculator into the sessionStorage
     * @method save
     * @param {!object} farmData.nutrientCalculator
     * @returns {Object} farmData.nutrientCalculator
     * @public
     * @static
     */
    function save(toSave) {
      var farmData = farmdata.session.find();

      if(!_isDefined(farmData)) {
        $log.error('Unable to find the farmData in the session!');
        return undefined;
      }

      farmData.dateLastUpdated = new Date();

      farmData.nutrientCalculator = toSave;
      farmdata.session.save(farmData);

      return toSave;
    }
    nutrientCalculatorSession.save = save;

    /**
     * Returns a section of the farmData as an object from the sessionStorage
     * @method loadSection
     * @returns {Object} the farmdata, null, if not found.
     * @public
     * @static
     */
    nutrientCalculatorSession.loadSection = function(section) {
      var loaded = load();
      return loaded?loaded[section]:null;
    }

    /**
     * Returns true if the location.search has ?load=true, false otherwise
     * @method isLoadFlagSet
     * @param {object} location instance
     * @returns {boolean} Returns true if the location.search has ?load=true, false otherwise
     * @public
     * @static
     */
    nutrientCalculatorSession.isLoadFlagSet = function(location) {
      var load = false;

      if(location.href.split('?').length > 1 &&
        location.href.split('?')[1].indexOf('load') === 0){
        load = (location.href.split('?')[1].split('=')[1] === 'true');
      }

      return load;
    }

    nutrientCalculatorSession.find = function() {
      return farmdata.session.find();
    }

    return nutrientCalculatorSession;

  });
<|endoftext|>


<commit_before>/*
Copyright 2017 Travis Ralston

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import {_td} from '../languageHandler';

// These are just a bunch of helper arrays to avoid copy/pasting a bunch of times
const LEVELS_ROOM_SETTINGS = ['device', 'room-device', 'room-account', 'account', 'config'];
const LEVELS_ROOM_SETTINGS_WITH_ROOM = ['device', 'room-device', 'room-account', 'account', 'config', 'room'];
const LEVELS_ACCOUNT_SETTINGS = ['device', 'account', 'config'];
const LEVELS_FEATURE = ['device', 'config'];
const LEVELS_DEVICE_ONLY_SETTINGS = ['device'];
const LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG = ['device', 'config'];

export const SETTINGS = {
    // EXAMPLE SETTING:
    // "my-setting": {
    //     // Required by features, optional otherwise
    //     isFeature: false,
    //
    //     // Recommended.
    //     displayName: _td("Cool Name"),
    //
    //     // Required.
    //     supportedLevels: [
    //         // The order does not matter.
    //
    //         "device",        // Affects the current device only
    //         "room-device",   // Affects the current room on the current device
    //         "room-account",  // Affects the current room for the current account
    //         "account",       // Affects the current account
    //         "room",          // Affects the current room (controlled by room admins)
    //         "config",        // Affects the current application
    //
    //         // "default" is always supported and does not get listed here.
    //     ],
    //
    //     // Optional. Any data type.
    //     default: {
    //         your: "value",
    //     },
    // },
    "feature_groups": {
        isFeature: true,
        displayName: _td("Communities"),
        supportedLevels: LEVELS_FEATURE,
    },
    "feature_pinning": {
        isFeature: true,
        displayName: _td("Message Pinning"),
        supportedLevels: LEVELS_FEATURE,
    },
    "MessageComposerInput.dontSuggestEmoji": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        displayName: _td('Disable Emoji suggestions while typing'),
        default: false,
    },
    "useCompactLayout": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        displayName: _td('Use compact timeline layout'),
        default: false,
    },
    "hideRedactions": {
        supportedLevels: LEVELS_ROOM_SETTINGS_WITH_ROOM,
        displayName: _td('Hide removed messages'),
        default: false,
    },
    "hideJoinLeaves": {
        supportedLevels: LEVELS_ROOM_SETTINGS_WITH_ROOM,
        displayName: _td('Hide join/leave messages (invites/kicks/bans unaffected)'),
        default: false,
    },
    "hideAvatarDisplaynameChanges": {
        supportedLevels: LEVELS_ROOM_SETTINGS_WITH_ROOM,
        displayName: _td('Hide avatar and display name changes'),
        default: false,
    },
    "hideReadReceipts": {
        supportedLevels: LEVELS_ROOM_SETTINGS,
        displayName: _td('Hide read receipts'),
        default: false,
    },
    "showTwelveHourTimestamps": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        displayName: _td('Show timestamps in 12 hour format (e.g. 2:30pm)'),
        default: false,
    },
    "alwaysShowTimestamps": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        displayName: _td('Always show message timestamps'),
        default: false,
    },
    "autoplayGifsAndVideos": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        displayName: _td('Autoplay GIFs and videos'),
        default: false,
    },
    "enableSyntaxHighlightLanguageDetection": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        displayName: _td('Enable automatic language detection for syntax highlighting'),
        default: false,
    },
    "Pill.shouldHidePillAvatar": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        displayName: _td('Hide avatars in user and room mentions'),
        default: false,
    },
    "TextualBody.disableBigEmoji": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        displayName: _td('Disable big emoji in chat'),
        default: false,
    },
    "MessageComposerInput.isRichTextEnabled": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        default: false,
    },
    "MessageComposer.showFormatting": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        default: false,
    },
    "dontSendTypingNotifications": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        displayName: _td("Don't send typing notifications"),
        default: false,
    },
    "MessageComposerInput.autoReplaceEmoji": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        displayName: _td('Automatically replace plain text Emoji'),
        default: false,
    },
    "VideoView.flipVideoHorizontally": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        displayName: _td('Mirror local video feed'),
        default: false,
    },
    "theme": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        default: "light",
    },
    "webRtcForceTURN": {
        supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
        displayName: _td('Disable Peer-to-Peer for 1:1 calls'),
        default: false,
    },
    "webrtc_audioinput": {
        supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
    },
    "webrtc_videoinput": {
        supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
    },
    "language": {
        supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
        default: "en"
    },
    "analyticsOptOut": {
        supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
        displayName: _td('Opt out of analytics'),
        default: false,
    },
    "autocompleteDelay": {
        supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
        default: 200,
    },
    "blacklistUnverifiedDevicesPerRoom": {
        // TODO: {Travis} Write a migration path to support blacklistUnverifiedDevices
        supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
        displayName: _td('Never send encrypted messages to unverified devices from this device'),
        default: {},
    },
    "blacklistUnverifiedDevices": {
        // TODO: {Travis} Write a migration path to support blacklistUnverifiedDevices
        supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
        displayName: _td('Never send encrypted messages to unverified devices from this device'),
        default: false,
    },
    "urlPreviewsEnabled": {
        supportedLevels: LEVELS_ROOM_SETTINGS_WITH_ROOM,
        displayName: {
            "default": _td('Enable inline URL previews by default'),
            "room-account": _td("Enable URL previews for this room (only affects you)"),
            "room": _td("Enable URL previews by default for participants in this room"),
        },
        default: true,
    },
};<commit_msg>Improve documentation of settings<commit_after>/*
Copyright 2017 Travis Ralston

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import {_td} from '../languageHandler';

// These are just a bunch of helper arrays to avoid copy/pasting a bunch of times
const LEVELS_ROOM_SETTINGS = ['device', 'room-device', 'room-account', 'account', 'config'];
const LEVELS_ROOM_SETTINGS_WITH_ROOM = ['device', 'room-device', 'room-account', 'account', 'config', 'room'];
const LEVELS_ACCOUNT_SETTINGS = ['device', 'account', 'config'];
const LEVELS_FEATURE = ['device', 'config'];
const LEVELS_DEVICE_ONLY_SETTINGS = ['device'];
const LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG = ['device', 'config'];

export const SETTINGS = {
    // EXAMPLE SETTING:
    // "my-setting": {
    //     // Must be set to true for features. Default is 'false'.
    //     isFeature: false,
    //
    //     // Display names are strongly recommended for clarity.
    //     displayName: _td("Cool Name"),
    //
    //     // Display name can also be an object for different levels.
    //     //displayName: {
    //     //    "device": _td("Name for when the setting is used at 'device'"),
    //     //    "room": _td("Name for when the setting is used at 'room'"),
    //     //    "default": _td("The name for all other levels"),
    //     //}
    //
    //     // The supported levels are required. Preferably, use the preset arrays
    //     // at the top of this file to define this rather than a custom array.
    //     supportedLevels: [
    //         // The order does not matter.
    //
    //         "device",        // Affects the current device only
    //         "room-device",   // Affects the current room on the current device
    //         "room-account",  // Affects the current room for the current account
    //         "account",       // Affects the current account
    //         "room",          // Affects the current room (controlled by room admins)
    //         "config",        // Affects the current application
    //
    //         // "default" is always supported and does not get listed here.
    //     ],
    //
    //     // Required. Can be any data type. The value specified here should match
    //     // the data being stored (ie: if a boolean is used, the setting should
    //     // represent a boolean).
    //     default: {
    //         your: "value",
    //     },
    // },
    "feature_groups": {
        isFeature: true,
        displayName: _td("Communities"),
        supportedLevels: LEVELS_FEATURE,
        default: false,
    },
    "feature_pinning": {
        isFeature: true,
        displayName: _td("Message Pinning"),
        supportedLevels: LEVELS_FEATURE,
        default: false,
    },
    "MessageComposerInput.dontSuggestEmoji": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        displayName: _td('Disable Emoji suggestions while typing'),
        default: false,
    },
    "useCompactLayout": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        displayName: _td('Use compact timeline layout'),
        default: false,
    },
    "hideRedactions": {
        supportedLevels: LEVELS_ROOM_SETTINGS_WITH_ROOM,
        displayName: _td('Hide removed messages'),
        default: false,
    },
    "hideJoinLeaves": {
        supportedLevels: LEVELS_ROOM_SETTINGS_WITH_ROOM,
        displayName: _td('Hide join/leave messages (invites/kicks/bans unaffected)'),
        default: false,
    },
    "hideAvatarDisplaynameChanges": {
        supportedLevels: LEVELS_ROOM_SETTINGS_WITH_ROOM,
        displayName: _td('Hide avatar and display name changes'),
        default: false,
    },
    "hideReadReceipts": {
        supportedLevels: LEVELS_ROOM_SETTINGS,
        displayName: _td('Hide read receipts'),
        default: false,
    },
    "showTwelveHourTimestamps": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        displayName: _td('Show timestamps in 12 hour format (e.g. 2:30pm)'),
        default: false,
    },
    "alwaysShowTimestamps": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        displayName: _td('Always show message timestamps'),
        default: false,
    },
    "autoplayGifsAndVideos": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        displayName: _td('Autoplay GIFs and videos'),
        default: false,
    },
    "enableSyntaxHighlightLanguageDetection": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        displayName: _td('Enable automatic language detection for syntax highlighting'),
        default: false,
    },
    "Pill.shouldHidePillAvatar": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        displayName: _td('Hide avatars in user and room mentions'),
        default: false,
    },
    "TextualBody.disableBigEmoji": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        displayName: _td('Disable big emoji in chat'),
        default: false,
    },
    "MessageComposerInput.isRichTextEnabled": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        default: false,
    },
    "MessageComposer.showFormatting": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        default: false,
    },
    "dontSendTypingNotifications": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        displayName: _td("Don't send typing notifications"),
        default: false,
    },
    "MessageComposerInput.autoReplaceEmoji": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        displayName: _td('Automatically replace plain text Emoji'),
        default: false,
    },
    "VideoView.flipVideoHorizontally": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        displayName: _td('Mirror local video feed'),
        default: false,
    },
    "theme": {
        supportedLevels: LEVELS_ACCOUNT_SETTINGS,
        default: "light",
    },
    "webRtcForceTURN": {
        supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
        displayName: _td('Disable Peer-to-Peer for 1:1 calls'),
        default: false,
    },
    "webrtc_audioinput": {
        supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
        default: null,
    },
    "webrtc_videoinput": {
        supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
        default: null,
    },
    "language": {
        supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
        default: "en"
    },
    "analyticsOptOut": {
        supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
        displayName: _td('Opt out of analytics'),
        default: false,
    },
    "autocompleteDelay": {
        supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
        default: 200,
    },
    "blacklistUnverifiedDevicesPerRoom": {
        // TODO: {Travis} Write a migration path to support blacklistUnverifiedDevices
        supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
        displayName: _td('Never send encrypted messages to unverified devices from this device'),
        default: {},
    },
    "blacklistUnverifiedDevices": {
        // TODO: {Travis} Write a migration path to support blacklistUnverifiedDevices
        supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
        displayName: _td('Never send encrypted messages to unverified devices from this device'),
        default: false,
    },
    "urlPreviewsEnabled": {
        supportedLevels: LEVELS_ROOM_SETTINGS_WITH_ROOM,
        displayName: {
            "default": _td('Enable inline URL previews by default'),
            "room-account": _td("Enable URL previews for this room (only affects you)"),
            "room": _td("Enable URL previews by default for participants in this room"),
        },
        default: true,
    },
};<|endoftext|>


<commit_before>import assign from 'lodash/assign'
import filter from 'lodash/filter'
import forEach from 'lodash/forEach'
import humanFormat from 'human-format'
import isArray from 'lodash/isArray'
import isFunction from 'lodash/isFunction'
import mapValues from 'lodash/mapValues'
import pick from 'lodash/fp/pick'
import { connect } from 'react-redux'
import { createSelector } from 'reselect'
import { PropTypes } from 'react'

import * as actions from '../../store/actions'

// ===================================================================

const _normalizeMapStateToProps = (mapper) => {
  if (isFunction(mapper)) {
    return mapper
  }

  if (isArray(mapper)) {
    return pick(mapper)
  }

  mapper = mapValues(mapper, _normalizeMapStateToProps)
  return (state, props) => mapValues(mapper, (fn) => fn(state, props))
}

export const connectStore = (mapStateToProps) => connect(
  _normalizeMapStateToProps(mapStateToProps),
  actions
)

// -------------------------------------------------------------------

export const createFilter = (
  collectionSelector,
  filterSelector,
  valueGetter
) => createSelector(
  collectionSelector,
  filterSelector,
  (collection, filter_) => {
    if (!filter_) {
      return collection
    }

    filter_ = String(filter_).toLowerCase()
    return filter(
      collection,
      (item) => String(valueGetter(item)).toLowerCase().indexOf(filter_) !== -1
    )
  }
)

// -------------------------------------------------------------------

const osToFamily = (function (osByFamily) {
  const osToFamily = Object.create(null)

  forEach(osByFamily, (list, family) => {
    forEach(list, (os) => {
      osToFamily[os] = family
    })
  })

  return osToFamily
})({
  linux: [
    'coreos'
  ],
  centos: [
    'centos'
  ],
  debian: [
    'debian'
  ],
  fedora: [
    'fedora'
  ],
  gentoo: [
    'gentoo'
  ],
  oracle: [
    'oracle'
  ],
  redhat: [
    'redhat',
    'rhel'
  ],
  ubuntu: [
    'ubuntu'
  ],
  solaris: [
    'solaris'
  ],
  freebsd: [
    'freebsd'
  ],
  netbsd: [
    'netbsd'
  ],
  osx: [
    'osx'
  ],
  'linux-mint': [
    'linux-mint'
  ],
  suse: [
    'sles',
    'suse'
  ],
  windows: [
    'windows'
  ]
})

export const osFamily = (osName) => osToFamily[osName.toLowerCase()] || 'other'

// -------------------------------------------------------------------

// Invoke a function and returns it result.
// All parameters are forwarded.
//
// Why using `invoke()`?
// - avoid tedious IIFE syntax
// - avoid declaring variables in the common scope
// - monkey-patching
//
// ```js
// const sum = invoke(1, 2, (a, b) => a + b)
//
// eventEmitter.emit = invoke(eventEmitter.emit, (emit) => function (event) {
//   if (event === 'foo') {
//     throw new Error('event foo is disabled')
//   }
//
//   return emit.apply(this, arguments)
// })
// ```
export function invoke () {
  const n = arguments.length - 1
  const fn = arguments[n]
  const args = new Array(n)
  for (let i = 0; i < n; ++i) {
    args[i] = arguments[i]
  }

  return fn.apply(undefined, args)
}

// -------------------------------------------------------------------

export const propTypes = (types) => (target) => {
  target.propTypes = types

  return target
}
assign(propTypes, PropTypes)

// -------------------------------------------------------------------

export const formatSize = (bytes) => humanFormat(bytes, { scale: 'binary', unit: 'B' })

export const parseSize = (size) => {
  let bytes = humanFormat.parse.raw(size, { scale: 'binary' })
  if (bytes.unit && bytes.unit !== 'B') {
    bytes = humanFormat.parse.raw(size)

    if (bytes.unit && bytes.unit !== 'B') {
      throw new Error('invalid size: ' + size)
    }
  }
  return Math.floor(bytes.value * bytes.factor)
}

// -------------------------------------------------------------------

export const normalizeXenToolsStatus = (status) => {
  if (status === false) {
    return 'not-installed'
  }
  if (status === undefined) {
    return 'unknown'
  }
  if (status === 'up to date') {
    return 'up-to-date'
  }
  return 'out-of-date'
}
<commit_msg>Do not declare global variable osToFamily in utils.<commit_after>import assign from 'lodash/assign'
import filter from 'lodash/filter'
import forEach from 'lodash/forEach'
import humanFormat from 'human-format'
import isArray from 'lodash/isArray'
import isFunction from 'lodash/isFunction'
import mapValues from 'lodash/mapValues'
import pick from 'lodash/fp/pick'
import { connect } from 'react-redux'
import { createSelector } from 'reselect'
import { PropTypes } from 'react'

import * as actions from '../../store/actions'

// ===================================================================

const _normalizeMapStateToProps = (mapper) => {
  if (isFunction(mapper)) {
    return mapper
  }

  if (isArray(mapper)) {
    return pick(mapper)
  }

  mapper = mapValues(mapper, _normalizeMapStateToProps)
  return (state, props) => mapValues(mapper, (fn) => fn(state, props))
}

export const connectStore = (mapStateToProps) => connect(
  _normalizeMapStateToProps(mapStateToProps),
  actions
)

// -------------------------------------------------------------------

export const createFilter = (
  collectionSelector,
  filterSelector,
  valueGetter
) => createSelector(
  collectionSelector,
  filterSelector,
  (collection, filter_) => {
    if (!filter_) {
      return collection
    }

    filter_ = String(filter_).toLowerCase()
    return filter(
      collection,
      (item) => String(valueGetter(item)).toLowerCase().indexOf(filter_) !== -1
    )
  }
)

// -------------------------------------------------------------------

export const osFamily = invoke({
  linux: [
    'coreos'
  ],
  centos: [
    'centos'
  ],
  debian: [
    'debian'
  ],
  fedora: [
    'fedora'
  ],
  gentoo: [
    'gentoo'
  ],
  oracle: [
    'oracle'
  ],
  redhat: [
    'redhat',
    'rhel'
  ],
  ubuntu: [
    'ubuntu'
  ],
  solaris: [
    'solaris'
  ],
  freebsd: [
    'freebsd'
  ],
  netbsd: [
    'netbsd'
  ],
  osx: [
    'osx'
  ],
  'linux-mint': [
    'linux-mint'
  ],
  suse: [
    'sles',
    'suse'
  ],
  windows: [
    'windows'
  ]
}, (osByFamily) => {
  const osToFamily = Object.create(null)
  forEach(osByFamily, (list, family) => {
    forEach(list, (os) => {
      osToFamily[os] = family
    })
  })

  return (osName) => osToFamily[osName.toLowerCase()] || 'other'
})

// -------------------------------------------------------------------

// Invoke a function and returns it result.
// All parameters are forwarded.
//
// Why using `invoke()`?
// - avoid tedious IIFE syntax
// - avoid declaring variables in the common scope
// - monkey-patching
//
// ```js
// const sum = invoke(1, 2, (a, b) => a + b)
//
// eventEmitter.emit = invoke(eventEmitter.emit, (emit) => function (event) {
//   if (event === 'foo') {
//     throw new Error('event foo is disabled')
//   }
//
//   return emit.apply(this, arguments)
// })
// ```
export function invoke () {
  const n = arguments.length - 1
  const fn = arguments[n]
  const args = new Array(n)
  for (let i = 0; i < n; ++i) {
    args[i] = arguments[i]
  }

  return fn.apply(undefined, args)
}

// -------------------------------------------------------------------

export const propTypes = (types) => (target) => {
  target.propTypes = types

  return target
}
assign(propTypes, PropTypes)

// -------------------------------------------------------------------

export const formatSize = (bytes) => humanFormat(bytes, { scale: 'binary', unit: 'B' })

export const parseSize = (size) => {
  let bytes = humanFormat.parse.raw(size, { scale: 'binary' })
  if (bytes.unit && bytes.unit !== 'B') {
    bytes = humanFormat.parse.raw(size)

    if (bytes.unit && bytes.unit !== 'B') {
      throw new Error('invalid size: ' + size)
    }
  }
  return Math.floor(bytes.value * bytes.factor)
}

// -------------------------------------------------------------------

export const normalizeXenToolsStatus = (status) => {
  if (status === false) {
    return 'not-installed'
  }
  if (status === undefined) {
    return 'unknown'
  }
  if (status === 'up to date') {
    return 'up-to-date'
  }
  return 'out-of-date'
}
<|endoftext|>


<commit_before>// extends src/nodes/image.js which extends src/node-box-native-view.js

$(function(){

  var template =
    '<canvas id="canvas-<%= id %>" class="preview" style="max-width:100%"></canvas>'+
    '
frame /
'+
    '
'+
      '<button class="play">play</button>'+
      '<button class="pause">pause</button>'+
      '<button class="prev">prev</button>'+
      '<button class="next">next</button>'+
      '<button class="deleteframe">deleteframe</button>

'+
      '<input type="checkbox" class="pingpong" <%= (get("state").pingpong ? "checked" : "") %> />pingpong (loop back and forth)

'+
      '<button class="make-gif">make gif</button>'+
      '<button class="make-spritesheet">make spritesheet</button>'+
      // '<button class="import">import</button>'+
      // '<form class="importform" style="display:none;">'+
      // '</form>'+
    '
'+
    '

'+
    '

';

  Iframework.NativeNodes["variable-animation"] = Iframework.NativeNodes["variable"].extend({

    template: _.template(template),
    info: {
      title: "animation",
      description: "holds a stack of canvases to use as an animation"
    },
    events: {
      "click .play"  : "inputplay",
      "click .pause" : "inputpause",
      "click .prev"  : "inputprev",
      "click .next"  : "inputnext",
      "click .deleteframe"  : "deleteFrame",
      "change .pingpong": "clickPingpong",
      "click .make-spritesheet"  : "makeSpritesheet",
      "click .make-gif"  : "makeGif"
    },
    initializeModule: function(){
      this._animation = {
        width: 0,
        height: 0,
        fps: 10,
        frames: [],
        length: 0
      };
      this.canvas = this.$(".preview")[0];
      this.context = this.canvas.getContext('2d');

      // Setup droppable
      // Add drop indicator (shown in CSS)
      this.$el.append('

add image

');

      // Make droppable
      this.$el.droppable({
        accept: ".canvas, .meemoo-plugin-images-thumbnail",
        tolerance: "pointer",
        hoverClass: "drop-hover",
        activeClass: "drop-active",
        // Don't also drop on graph
        greedy: true
      });
      this.$el.on("drop", {"self": this, "inputName": "push"}, Iframework.util.imageDrop);
    },
    inputpush: function(image){
      var frame = document.createElement("canvas");
      frame.width = image.width;
      frame.height = image.height;
      frame.getContext('2d').drawImage(image, 0, 0);
      this._animation.frames.push(frame);
      if (this._length && this._animation.frames.length>this._length) {
        var shifted = this._animation.frames.shift();
        this.send("shift", shifted);
      }
      this._animation.length = this._animation.frames.length;
      this._animation.width = image.width;
      this._animation.height = image.height;

      // Info
      this.$(".length").text(this._animation.length);

      // Preview
      if (this.canvas.width !== image.width){
        this.canvas.width = image.width;
      }
      if (this.canvas.height !== image.height){
        this.canvas.height = image.height;
      }
      if (!this._play){
        this.showFrame(this._animation.length-1);
      }

      this.inputsend();
    },
    showFrame: function(i) {
      var frame = this._animation.frames[i];
      if (frame) {
        this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
        this.context.drawImage(frame, 0, 0);
        this._previewFrame = i;
        this.$(".index").text(i+1);
        this.send("image", this.canvas);
      }
    },
    _ms: 1000/12,
    inputfps: function(fps){
      this._animation.fps = fps;
      this._ms = 1000/fps;

      this.inputsend();
    },
    inputlength: function(i){
      if (i >= 0) {
        this._length = i;
        if (this._length > 0 && this._animation.frames.length > this._length) {
          this._animation.frames.splice(this._length, this._animation.frames.length);
          this._animation.length = this._animation.frames.length;
          this.$(".length").text(this._animation.length);
          if (this._previewFrame >= this._animation.length) {
            // Show last
            this.showFrame(this._animation.length-1);
          }
        }
      }
    },
    deleteFrame: function(){
      this.inputpause();
      this._animation.frames.splice(this._previewFrame, 1);
      this._animation.length = this._animation.frames.length;
      this.$(".length").text(this._animation.length);
      // Show next preview
      if (this._animation.length <= 0) {
        // No frames, make blank
        this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
        this.$(".index").text("0");
      } else if (this._previewFrame >= this._animation.length) {
        // Show last
        this.showFrame(this._animation.length-1);
      } else {
        // Show next
        this.showFrame(this._previewFrame);
      }
    },
    inputclear: function () {
      this._animation.frames = [];
      this._animation.length = 0;
      this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
      this.$(".index").text("0");
      this.$(".length").text("0");
    },
    _pingpong: false,
    _reverse: false,
    clickPingpong: function(event){
      if (event.target.checked) {
        this._pingpong = true;
        this.set("pingpong", true);
      } else {
        this._pingpong = false;
        this._reverse = false;
        this.set("pingpong", false);
      }
    },
    inputpingpong: function(boo){
      this._pingpong = boo;
      if (!boo) {
        // Keeps it from looping backwards
        this._reverse = false;
      }
      this.$(".pingpong")[0].checked = boo;
    },
    _play: false,
    inputplay: function(){
      this._play = true;
    },
    inputpause: function(){
      this._play = false;
    },
    _previewFrame: 0,
    inputprev: function(){
      // Pause
      this.inputpause();
      // Show prev or loop back
      if (this._previewFrame > 0) {
        this.showFrame(this._previewFrame-1);
      } else {
        this.showFrame(this._animation.frames.length-1);
      }
    },
    inputnext: function(){
      this.inputpause();
      // Show next or loop
      if (this._previewFrame < this._animation.frames.length-1) {
        this.showFrame(this._previewFrame+1);
      } else {
        this.showFrame(0);
      }
    },
    makeSpritesheet: function(){
      if (this._animation.length < 1) { return; }

      var image = document.createElement("canvas");
      var imageContext = image.getContext("2d");
      image.width = this._animation.width * this._animation.length;
      image.height = this._animation.height;
      var x = 0;
      for (var i=0; i<this._animation.length; i++){
        imageContext.drawImage(this._animation.frames[i], x, 0);
        x += this._animation.width;
      }
      var img = '';
      self.$(".exports").prepend( img );
    },
    copyDragImage: function (helper) {
      var img = helper.data("meemoo-source-image");
      var imageCopy = document.createElement("img");
      imageCopy.src = img.src;
      helper.append(imageCopy);
    },
    makeImageDraggable: function (img) {
      var self = this;
      $(img)
        .attr("title", "drag to images menu to save")
        .draggable({
          cursor: "pointer",
          cursorAt: { top: -10, left: -10 },
          helper: function( event ) {
            var helper = $( '

Save this

' )
              .data({
                "meemoo-drag-type": "image",
                "meemoo-source-image": img[0]
              });
            $(document.body).append(helper);
            _.delay(function(){
              self.copyDragImage(helper);
            }, 100);
            return helper;
          }
        });
    },
    _matte: [255, 255, 255],
    _transparent: [0, 255, 0],
    makeGif: function(stackUp){
      if (this._animation.length <= 0) { return false; }

      if (stackUp!==false) { stackUp = true; }

      // Spawn worker
      this.$(".status").text("Setting up GIF...");
      this.$(".make-gif").prop("disabled", true).text("make gif (busy...)");
      var gifWorker = new Worker("libs/omggif/omggif-worker.js");

      // Setup listeners
      var self = this;
      gifWorker.addEventListener('message', function (e) {
        if (e.data.type === "progress") {
          self.$(".status").text("GIF " + Math.round(e.data.data*100) + "% encoded...");
        } else if (e.data.type === "gif") {
          var gifurl = "data:image/gif;base64,"+window.btoa(e.data.data);
          if (stackUp) {
            var img = $('')
              .attr({
                src: gifurl,
                style: "max-width:100%"
              });
            // Format file size
            var fileSize = Math.round(e.data.data.length / 1024);
            var fileSizeUnit = "kb";
            if (fileSize >= 1024) {
              fileSize = Math.round(fileSize / 1024 * 10) / 10;
              fileSizeUnit = "mb";
            }
            var encodeTime = Math.round( e.data.encodeTime / 100 ) / 10;
            self.$(".exports").prepend( "
" + e.data.frameCount + " frames ("+fileSize+fileSizeUnit+") encoded in " + encodeTime + "s
" );
            self.$(".exports").prepend( img );

            // Make draggable
            self.makeImageDraggable(img);
          }
          self.$(".status").text("");
          self.$(".make-gif").prop("disabled", false).text("make gif");

          // Send data url
          self.send("gif", gifurl);
        }
      }, false);
      gifWorker.addEventListener('error', function (e) {
        self.$(".status").text("GIF encoding error :-(");
        self.$(".make-gif").prop("disabled", false).text("make gif");
        gifWorker.terminate();
      }, false);

      // Send image data
      var frames = [];
      for (var i = 0; i<this._animation.length; i++) {
        var imageData = this._animation.frames[i].getContext('2d').getImageData(0, 0, this._animation.width, this._animation.height);
        frames[i] = imageData;
        if (this._pingpong && i>0 && i<this._animation.length-1) {
          frames[this._animation.length * 2 - 2 - i] = imageData;
        }
      }
      gifWorker.postMessage({
        frames: frames,
        delay: this._ms,
        matte: this._matte,
        transparent: this._transparent
      });

    },
    inputsendGif: function() {
      if ( self.$(".make-gif")[0].disabled ) { return false; }

      this.makeGif(false);
    },
    inputsend: function(){
      this.send("animation", this._animation);
    },
    redraw: function(timestamp){
    },
    renderAnimationFrame: function (timestamp) {
      if (this._play && timestamp-this._lastRedraw>=this._ms) {
        if (this._reverse) {
          this._previewFrame--;
          if (this._previewFrame < 0) {
            // Loop
            if (this._pingpong) {
              this._reverse = false;
              this._previewFrame = 1;
            } else {
              this._previewFrame = this._animation.frames.length - 1;
            }
          }
        } else {
          this._previewFrame++;
          if (this._previewFrame >= this._animation.frames.length) {
            // Loop
            if (this._pingpong) {
              this._reverse = true;
              this._previewFrame = Math.max(this._animation.frames.length - 2, 0);
            } else {
              this._previewFrame = 0;
            }
          }
        }
        this.showFrame(this._previewFrame);

        this._lastRedraw = timestamp;
      }
    },
    inputs: {
      push: {
        type: "image",
        description: "adds image to end of array"
      },
      length: {
        type: "int",
        description: "max length of array"
      },
      fps: {
        type: "float",
        description: "frames per second to animate",
        "default": 12
      },
      pingpong: {
        type: "boolean",
        description: "loop animation back and forth",
        "default": false
      },
      play: {
        type: "bang",
        description: "start the preview animation"
      },
      pause: {
        type: "bang",
        description: "stop the preview animation"
      },
      prev: {
        type: "bang",
        description: "when paused, show the prev frame"
      },
      next: {
        type: "bang",
        description: "when paused, show the next frame"
      },
      clear: {
        type: "bang",
        description: "delete all frames in the animation"
      },
      sendOne: {
        type: "int",
        description: "sends canvas with this index"
      },
      // sendGif: {
      //   type: "bang",
      //   description: "encodes and sends gif as data url"
      // },
      send: {
        type: "bang",
        description: "sends animation object"
      }
    },
    outputs: {
      image: {
        type: "image",
        description: "the preview canvas"
      },
      animation: {
        type: "animation",
        description: "animation object has width, height, fps, frames (array of canvases), and length (frames.length)"
      }//,
      // gif: {
      //   type: "data:image/gif",
      //   description: "completed gif"
      // }
    }

  });

});
<commit_msg>openart pub<commit_after>// extends src/nodes/image.js which extends src/node-box-native-view.js

$(function(){

  var template =
    '<canvas id="canvas-<%= id %>" class="preview" style="max-width:100%"></canvas>'+
    '
frame /
'+
    '
'+
      '<button class="play">play</button>'+
      '<button class="pause">pause</button>'+
      '<button class="prev">prev</button>'+
      '<button class="next">next</button>'+
      '<button class="deleteframe">deleteframe</button>

'+
      '<input type="checkbox" class="pingpong" <%= (get("state").pingpong ? "checked" : "") %> />pingpong (loop back and forth)

'+
      '<button class="make-gif">make gif</button>'+
      '<button class="make-spritesheet">make spritesheet</button>'+
      // '<button class="import">import</button>'+
      // '<form class="importform" style="display:none;">'+
      // '</form>'+
    '
'+
    '

'+
    '

';

  Iframework.NativeNodes["variable-animation"] = Iframework.NativeNodes["variable"].extend({

    template: _.template(template),
    info: {
      title: "animation",
      description: "holds a stack of canvases to use as an animation"
    },
    events: {
      "click .play"  : "inputplay",
      "click .pause" : "inputpause",
      "click .prev"  : "inputprev",
      "click .next"  : "inputnext",
      "click .deleteframe"  : "deleteFrame",
      "change .pingpong": "clickPingpong",
      "click .make-spritesheet"  : "makeSpritesheet",
      "click .make-gif"  : "makeGif"
    },
    initializeModule: function(){
      this._animation = {
        width: 0,
        height: 0,
        fps: 10,
        frames: [],
        length: 0
      };
      this.canvas = this.$(".preview")[0];
      this.context = this.canvas.getContext('2d');

      // Setup droppable
      // Add drop indicator (shown in CSS)
      this.$el.append('

add image

');

      // Make droppable
      this.$el.droppable({
        accept: ".canvas, .meemoo-plugin-images-thumbnail",
        tolerance: "pointer",
        hoverClass: "drop-hover",
        activeClass: "drop-active",
        // Don't also drop on graph
        greedy: true
      });
      this.$el.on("drop", {"self": this, "inputName": "push"}, Iframework.util.imageDrop);
    },
    inputpush: function(image){
      var frame = document.createElement("canvas");
      frame.width = image.width;
      frame.height = image.height;
      frame.getContext('2d').drawImage(image, 0, 0);
      this._animation.frames.push(frame);
      if (this._length && this._animation.frames.length>this._length) {
        var shifted = this._animation.frames.shift();
        this.send("shift", shifted);
      }
      this._animation.length = this._animation.frames.length;
      this._animation.width = image.width;
      this._animation.height = image.height;

      // Info
      this.$(".length").text(this._animation.length);

      // Preview
      if (this.canvas.width !== image.width){
        this.canvas.width = image.width;
      }
      if (this.canvas.height !== image.height){
        this.canvas.height = image.height;
      }
      if (!this._play){
        this.showFrame(this._animation.length-1);
      }

      this.inputsend();
    },
    showFrame: function(i) {
      var frame = this._animation.frames[i];
      if (frame) {
        this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
        this.context.drawImage(frame, 0, 0);
        this._previewFrame = i;
        this.$(".index").text(i+1);
        this.send("image", this.canvas);
      }
    },
    _ms: 1000/12,
    inputfps: function(fps){
      this._animation.fps = fps;
      this._ms = 1000/fps;

      this.inputsend();
    },
    inputlength: function(i){
      if (i >= 0) {
        this._length = i;
        if (this._length > 0 && this._animation.frames.length > this._length) {
          this._animation.frames.splice(this._length, this._animation.frames.length);
          this._animation.length = this._animation.frames.length;
          this.$(".length").text(this._animation.length);
          if (this._previewFrame >= this._animation.length) {
            // Show last
            this.showFrame(this._animation.length-1);
          }
        }
      }
    },
    deleteFrame: function(){
      this.inputpause();
      this._animation.frames.splice(this._previewFrame, 1);
      this._animation.length = this._animation.frames.length;
      this.$(".length").text(this._animation.length);
      // Show next preview
      if (this._animation.length <= 0) {
        // No frames, make blank
        this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
        this.$(".index").text("0");
      } else if (this._previewFrame >= this._animation.length) {
        // Show last
        this.showFrame(this._animation.length-1);
      } else {
        // Show next
        this.showFrame(this._previewFrame);
      }
    },
    inputclear: function () {
      this._animation.frames = [];
      this._animation.length = 0;
      this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
      this.$(".index").text("0");
      this.$(".length").text("0");
    },
    _pingpong: false,
    _reverse: false,
    clickPingpong: function(event){
      if (event.target.checked) {
        this._pingpong = true;
        this.set("pingpong", true);
      } else {
        this._pingpong = false;
        this._reverse = false;
        this.set("pingpong", false);
      }
    },
    inputpingpong: function(boo){
      this._pingpong = boo;
      if (!boo) {
        // Keeps it from looping backwards
        this._reverse = false;
      }
      this.$(".pingpong")[0].checked = boo;
    },
    _play: false,
    inputplay: function(){
      this._play = true;
    },
    inputpause: function(){
      this._play = false;
    },
    _previewFrame: 0,
    inputprev: function(){
      // Pause
      this.inputpause();
      // Show prev or loop back
      if (this._previewFrame > 0) {
        this.showFrame(this._previewFrame-1);
      } else {
        this.showFrame(this._animation.frames.length-1);
      }
    },
    inputnext: function(){
      this.inputpause();
      // Show next or loop
      if (this._previewFrame < this._animation.frames.length-1) {
        this.showFrame(this._previewFrame+1);
      } else {
        this.showFrame(0);
      }
    },
    makeSpritesheet: function(){
      if (this._animation.length < 1) { return; }

      var image = document.createElement("canvas");
      var imageContext = image.getContext("2d");
      image.width = this._animation.width * this._animation.length;
      image.height = this._animation.height;
      var x = 0;
      for (var i=0; i<this._animation.length; i++){
        imageContext.drawImage(this._animation.frames[i], x, 0);
        x += this._animation.width;
      }
      var img = '';
      self.$(".exports").prepend( img );
    },
    copyDragImage: function (helper) {
      var img = helper.data("meemoo-source-image");
      var imageCopy = document.createElement("img");
      imageCopy.src = img.src;
      helper.append(imageCopy);
    },
    makeImageDraggable: function (img) {
      var self = this;
      $(img)
        .attr("title", "drag to images menu to save")
        .draggable({
          cursor: "pointer",
          cursorAt: { top: -10, left: -10 },
          helper: function( event ) {
            var helper = $( '

Save this

' )
              .data({
                "meemoo-drag-type": "image",
                "meemoo-source-image": img[0]
              });
            $(document.body).append(helper);
            _.delay(function(){
              self.copyDragImage(helper);
            }, 100);
            return helper;
          }
        });
    },
    _matte: [255, 255, 255],
    _transparent: [0, 255, 0],
    makeGif: function(stackUp){
      if (this._animation.length <= 0) { return false; }

      if (stackUp!==false) { stackUp = true; }

      // Spawn worker
      this.$(".status").text("Setting up GIF...");
      this.$(".make-gif").prop("disabled", true).text("make gif (busy...)");
      var gifWorker = new Worker("libs/omggif/omggif-worker.js");

      // Setup listeners
      var self = this;
      gifWorker.addEventListener('message', function (e) {
        if (e.data.type === "progress") {
          self.$(".status").text("GIF " + Math.round(e.data.data*100) + "% encoded...");
        } else if (e.data.type === "gif") {
          var gifurl = "data:image/gif;base64,"+window.btoa(e.data.data);
          if (stackUp) {
            var img = $('')
              .attr({
                src: gifurl,
                style: "max-width:100%"
              });
            // Format file size
            var fileSize = Math.round(e.data.data.length / 1024);
            var fileSizeUnit = "kb";
            if (fileSize >= 1024) {
              fileSize = Math.round(fileSize / 1024 * 10) / 10;
              fileSizeUnit = "mb";
            }
            var encodeTime = Math.round( e.data.encodeTime / 100 ) / 10;
            self.$(".exports").prepend( "
" + e.data.frameCount + " frames ("+fileSize+fileSizeUnit+") encoded in " + encodeTime + "s
" );
            self.$(".exports").prepend( img );

            // Make draggable
            self.makeImageDraggable(img);
          }
          self.$(".status").text("");
          self.$(".make-gif").prop("disabled", false).text("make gif");

          // Send data url
          self.send("gif", gifurl);
        }
      }, false);
      gifWorker.addEventListener('error', function (e) {
        self.$(".status").text("GIF encoding error :-(");
        self.$(".make-gif").prop("disabled", false).text("make gif");
        gifWorker.terminate();
      }, false);

      // Send image data
      var frames = [];
      for (var i = 0; i<this._animation.length; i++) {
        var imageData = this._animation.frames[i].getContext('2d').getImageData(0, 0, this._animation.width, this._animation.height);
        frames[i] = imageData;
        if (this._pingpong && i>0 && i<this._animation.length-1) {
          frames[this._animation.length * 2 - 2 - i] = imageData;
        }
      }
      gifWorker.postMessage({
        frames: frames,
        delay: this._ms,
        matte: this._matte,
        transparent: this._transparent
      });

    },
    inputsendGif: function() {
      if ( self.$(".make-gif")[0].disabled ) { return false; }

      this.makeGif(false);
    },
    inputsend: function(){
      this.send("animation", this._animation);
    },
    redraw: function(timestamp){
    },
    renderAnimationFrame: function (timestamp) {
      if (this._play && timestamp-this._lastRedraw>=this._ms) {
        if (this._reverse) {
          this._previewFrame--;
          if (this._previewFrame < 0) {
            // Loop
            if (this._pingpong) {
              this._reverse = false;
              this._previewFrame = 1;
            } else {
              this._previewFrame = this._animation.frames.length - 1;
            }
          }
        } else {
          this._previewFrame++;
          if (this._previewFrame >= this._animation.frames.length) {
            // Loop
            if (this._pingpong) {
              this._reverse = true;
              this._previewFrame = Math.max(this._animation.frames.length - 2, 0);
            } else {
              this._previewFrame = 0;
            }
          }
        }
        this.showFrame(this._previewFrame);

        this._lastRedraw = timestamp;
      }
    },
    inputs: {
      push: {
        type: "image",
        description: "adds image to end of array"
      },
      length: {
        type: "int",
        description: "max length of array"
      },
      fps: {
        type: "float",
        description: "frames per second to animate",
        "default": 12
      },
      pingpong: {
        type: "boolean",
        description: "loop animation back and forth",
        "default": false
      },
      play: {
        type: "bang",
        description: "start the preview animation"
      },
      pause: {
        type: "bang",
        description: "stop the preview animation"
      },
      prev: {
        type: "bang",
        description: "when paused, show the prev frame"
      },
      next: {
        type: "bang",
        description: "when paused, show the next frame"
      },
      clear: {
        type: "bang",
        description: "delete all frames in the animation"
      },
      sendOne: {
        type: "int",
        description: "sends canvas with this index"
      },
      sendGif: {
        type: "bang",
        description: "encodes and sends gif as data url"
      },
      send: {
        type: "bang",
        description: "sends animation object"
      }
    },
    outputs: {
      image: {
        type: "image",
        description: "the preview canvas"
      },
      animation: {
        type: "animation",
        description: "animation object has width, height, fps, frames (array of canvases), and length (frames.length)"
      },
      gif: {
        type: "data:image/gif",
        description: "completed gif as dataurl"
      }
    }

  });

});
<|endoftext|>


<commit_before>import SwipeElement from "../lib/SwipeElement";
import SwipePage from "../lib/SwipePage";
import SwipeBook from "../lib/SwipeBook";

const chai = require('chai');
const assert = chai.assert;

var jsdom = require('jsdom');
const { JSDOM } = jsdom;
const { window } = new JSDOM();
const { document } = (new JSDOM('')).window;
global.document = document;
global.window = window;

global.$ = require('./jquery-2.2.3.min.js')(window);;

const myFunc = () => {
  return 3;
}
describe('swipe element test', function () {
  it('swipe element: image', function(){
    const info = { "img":"http://satoshi.blogs.com/swipe/hirano-manga/koma002/haikei.png" };
    const page_id = "page_id";
    const element_id = "element_id";
    const swipe_element = new SwipeElement(info, page_id, element_id);
    console.log(swipe_element.html());
  });
});

describe('swipe page test', function () {
  it('swipe element: image', function(){
    const info = {elements: [{ "img":"http://satoshi.blogs.com/swipe/hirano-manga/koma002/haikei.png" }]};
    const page_id = "page_id";
    const element_id = "element_id";

    const swipe_page = new SwipePage(info, {}, 1);
    swipe_page.loadElement();
    console.log(swipe_page.getHtml());
  });

});

describe('swipe page test', function () {
  it('swipe element: image', function(){
    const info = {elements: [{ "img":"http://satoshi.blogs.com/swipe/hirano-manga/koma002/haikei.png" }]};
    const page_id = "page_id";
    const element_id = "element_id";

    console.log(SwipeBook);
    const swipe_book = new SwipeBook(info, {}, 1);
    // swipe_book.loadElement();
    // console.log(swipe_page.getHtml());
  });

});
<commit_msg>update test<commit_after>import SwipeElement from "../lib/SwipeElement";
import SwipePage from "../lib/SwipePage";
import SwipeBook from "../lib/SwipeBook";

const chai = require('chai');
const assert = chai.assert;

var jsdom = require('jsdom');
const { JSDOM } = jsdom;
const { window } = new JSDOM();
const { document } = (new JSDOM('')).window;
global.document = document;
global.window = window;

global.$ = require('./jquery-2.2.3.min.js')(window);;

const myFunc = () => {
  return 3;
}
describe('swipe element test', function () {
  it('swipe element: image', function(){
    const info = { "img":"http://satoshi.blogs.com/swipe/hirano-manga/koma002/haikei.png" };
    const page_id = "page_id";
    const element_id = "element_id";
    const swipe_element = new SwipeElement(info, page_id, element_id);
    const html = swipe_element.html();

    assert.equal(html, "

");
  });
});

describe('swipe page test', function () {
  it('swipe element: image', function(){
    const info = {elements: [{ "img":"http://satoshi.blogs.com/swipe/hirano-manga/koma002/haikei.png" }]};
    const page_id = "page_id";
    const element_id = "element_id";

    const swipe_page = new SwipePage(info, {}, 1);
    swipe_page.loadElement();

    const html = swipe_page.getHtml();

    assert.equal(html, "

");
  });

});

describe('swipe page test', function () {
  it('swipe element: image', function(){
    const info = {elements: [{ "img":"http://satoshi.blogs.com/swipe/hirano-manga/koma002/haikei.png" }]};
    const page_id = "page_id";
    const element_id = "element_id";

    console.log(SwipeBook);
    const swipe_book = new SwipeBook(info, {}, 1);
    // swipe_book.loadElement();
    // console.log(swipe_page.getHtml());
  });

});
<|endoftext|>


<commit_before>'use strict'

import init from './component/init'
import create from './element/create'
import insert from './component/insert'

import emitter from './module/emitter'
import css from './module/css'
import attach from './module/attach'

var defaults = {
  prefix: 'material',
  class: 'textfield',
  type: 'control',
  tag: 'div',
  modules: [emitter, attach, insert],
  events: [
    // 'change': '_onChange',
    ['input.focus', '_onInputFocus'],
    ['input.blur', '_onInputBlur'],
    // ['input.keypress', '_onInputKeyPress',
    ['input.keyup', '_onInputKeyPress'],
    ['input.change', '_onChange']
    // 'input.keydown': '_onInputKeyPress'

  ]
}

/**
 * Field class
 * @class
 * @extends {Control}
 */
class Textfield {
  /**
   * Constructor
   * @param  {Object} options - Component options
   * @return {Object} Class instance
   */
  constructor(options) {
    this.options = Object.assign({}, defaults, options || {})

    this.init(options)
    this.build()
    this.attach()

    return this
  }

  /**
   * init
   * @param  {Object} options The class options
   * @return {Object} The class instance
   */
  init() {
    init(this)
    // init options and merge options to defaults

    this.value = this.options.value

    return this
  }

  /**
   * [build description]
   * @return {Object} The class instance
   */
  build() {
    // create a new div as input element
    var tag = this.this.options.tag || 'div'
    this.wrapper = create(tag, this.options.prefix + '-' + this.options.class)

    this.buildLabel()
    this.buildInput()
    this.buildUnderline()

    if (this.disabled) {
      css.add(this.wrapper, 'is-disabled')
    }

    // insert if container this.options is given
    if (this.options.container) {
      // console.log(this.name, opts.container);
      this.insert(this.options.container)
    }
  }

  buildLabel() {
    this.label = create('label', this.options.class + '-label')
    this.insertElement(this.label, this.wrapper)

    if (this.options.label !== false) {
      this.setLabel()
    }
  }

  /**
   * [_initInput description]
   * @return {Object} The class instance
   */
  buildInput() {
    this.input = create('input', this.options.class + '-input')
    this.input.setAttribute('type', 'text')
    this.insertElement(this.input, this.wrapper)

    if (!this.options.value) {
      css.add(this.wrapper, 'is-empty')
    }

    if (this.readonly) {
      this.input.setAttribute('readonly', 'readonly')
      this.input.setAttribute('tabindex', '-1')
    }

    return this.input
  }

  /**
   * _initUnderline
   * @return {Object} The class instance
   */
  buildUnderline() {
    this.underline = create('span', this.options.class + '-underline')
    this.insertElement(this.underline, this.wrapper)
  }

  /**
   * Setter
   * @param {string} prop
   * @param {string} value
   */
  set(prop, value) {
    switch (prop) {
      case 'value':
        this.setValue(value)
        break
      case 'label':
        this.setLabel(value)
        break
      default:
        this.setValue(prop)
    }

    return this
  }

  /**
   * [buildLabel description]
   * @return {Object} The class instance
   */
  setLabel(label) {
    label = label || this.options.label
    var text

    if (label === null || label === false) {
      text = ''
    } else if (this.options.label) {
      text = label
    } else {
      text = this.options.name
    }

    this.label.textContent = text
  }

  /**
   * Getter
   * @param {string} prop
   * @param {string} value
   */
  get(prop) {
    var value

    switch (prop) {
      case 'value':
        value = this.getValue()
        break
      case 'name':
        value = this.name
        break
      default:
        return this.getValue()
    }

    return value
  }

  /**
   * [getValue description]
   * @return {Object} The class instance
   */
  getValue() {
    // console.log('getValue', this);
    return this.input.value
  }

  /**
   * [setValue description]
   * @param {string} value [description]
   */
  setValue(value) {
    this.input.value = value

    if (value) {
      css.remove(this.wrapper, 'is-empty')
    } else {
      css.add(this.wrapper, 'is-empty')
    }

    this.emit('change', value)
  }

  /**
   * Setter for the state of the component
   * @param {string} state active/disable etc...
   */
  setState(state) {
    if (this.state) {
      css.remove(this.wrapper, 'state-' + this.state)
    }

    if (state) {
      css.add(this.wrapper, 'state-' + state)
    }

    this.state = state
    this.emit('state', state)

    return this
  }

  /**
   * [_initValue description]
   * @return {Object} The class instance
   */
  _initValue() {
    var opts = this.options

    // create a new div as input element
    if (opts.value) {
      this.setValue(opts.value)
    }
  }

  /**
   * [_onFocus description]
   * @return {Object} The class instance
   */
  _onInputFocus(e) {
    // console.log('_onInputFocus');
    if (this.readonly) return
    this.setState('focus')
  }

  /**
   * [_onBlur description]
   * @return {Object} The class instance
   */
  _onInputBlur() {
    // console.log('_onInputBlur', this.readonly);
    if (this.readonly) return
    this.setState(null)
  }

  /**
   * [_onFocus description]
   * @return {Object} The class instance
   */
  _onInputKeyPress(e) {
    // console.log('_onInputKeyPress', e);

    if (this.get('value') === '') {
      css.add(this.wrapper, 'is-empty')
    } else {
      css.remove(this.wrapper, 'is-empty')
    }

    this.emit('change', this.getValue())
  }

  /**
   * [setError description]
   * @param {string} error Error description
   */
  setError(error) {
    if (error) {
      this.addClass('field-error')
      if (this.error) { this.error.set('html', error) }
    } else {
      if (this.error) { this.removeClass('field-error') }
      if (this.error) { this.error.set('html', '') }
    }
  }
}

export default Textfield<commit_msg>use insert element<commit_after>'use strict'

import create from './element/create'
import insert from './element/insert'

import emitter from './module/emitter'
import css from './module/css'
import attach from './module/attach'

var defaults = {
  prefix: 'material',
  class: 'textfield',
  type: 'control',
  tag: 'div',
  events: [
    // 'change': '_onChange',
    ['input.focus', '_onInputFocus'],
    ['input.blur', '_onInputBlur'],
    // ['input.keypress', '_onInputKeyPress',
    ['input.keyup', '_onInputKeyPress'],
    ['input.change', '_onChange']
    // 'input.keydown': '_onInputKeyPress'

  ]
}

/**
 * Field class
 * @class
 * @extends {Control}
 */
class Textfield {
  /**
   * Constructor
   * @param  {Object} options - Component options
   * @return {Object} Class instance
   */
  constructor(options) {
    this.options = Object.assign({}, defaults, options || {})

    this.init()
    this.build()
    this.attach()

    return this
  }

  /**
   * init
   * @param  {Object} options The class options
   * @return {Object} The class instance
   */
  init() {
    Object.assign(this, emitter, attach)

    this.value = this.options.value

    return this
  }

  /**
   * [build description]
   * @return {Object} The class instance
   */
  build() {
    // create a new div as input element
    var tag = this.options.tag || 'div'
    this.wrapper = create(tag, this.options.prefix + '-' + this.options.class)

    this.buildLabel()
    this.buildInput()
    this.buildUnderline()

    if (this.disabled) {
      css.add(this.wrapper, 'is-disabled')
    }

    // insert if container this.options is given
    if (this.options.container) {
      // console.log(this.name, opts.container);
      insert(this.wrapper, this.options.container)
    }
  }

  buildLabel() {
    this.label = create('label', this.options.class + '-label')
    insert(this.label, this.wrapper)

    if (this.options.label !== false) {
      this.setLabel()
    }
  }

  /**
   * [_initInput description]
   * @return {Object} The class instance
   */
  buildInput() {
    this.input = create('input', this.options.class + '-input')
    this.input.setAttribute('type', 'text')
    insert(this.input, this.wrapper)

    if (!this.options.value) {
      css.add(this.wrapper, 'is-empty')
    }

    if (this.readonly) {
      this.input.setAttribute('readonly', 'readonly')
      this.input.setAttribute('tabindex', '-1')
    }

    return this.input
  }

  /**
   * _initUnderline
   * @return {Object} The class instance
   */
  buildUnderline() {
    this.underline = create('span', this.options.class + '-underline')
    insert(this.underline, this.wrapper)
  }

  /**
   * Setter
   * @param {string} prop
   * @param {string} value
   */
  set(prop, value) {
    switch (prop) {
      case 'value':
        this.setValue(value)
        break
      case 'label':
        this.setLabel(value)
        break
      default:
        this.setValue(prop)
    }

    return this
  }

  /**
   * [buildLabel description]
   * @return {Object} The class instance
   */
  setLabel(label) {
    label = label || this.options.label
    var text

    if (label === null || label === false) {
      text = ''
    } else if (this.options.label) {
      text = label
    } else {
      text = this.options.name
    }

    this.label.textContent = text
  }

  /**
   * Getter
   * @param {string} prop
   * @param {string} value
   */
  get(prop) {
    var value

    switch (prop) {
      case 'value':
        value = this.getValue()
        break
      case 'name':
        value = this.name
        break
      default:
        return this.getValue()
    }

    return value
  }

  /**
   * [getValue description]
   * @return {Object} The class instance
   */
  getValue() {
    // console.log('getValue', this);
    return this.input.value
  }

  /**
   * [setValue description]
   * @param {string} value [description]
   */
  setValue(value) {
    this.input.value = value

    if (value) {
      css.remove(this.wrapper, 'is-empty')
    } else {
      css.add(this.wrapper, 'is-empty')
    }

    this.emit('change', value)
  }

  /**
   * Setter for the state of the component
   * @param {string} state active/disable etc...
   */
  setState(state) {
    if (this.state) {
      css.remove(this.wrapper, 'state-' + this.state)
    }

    if (state) {
      css.add(this.wrapper, 'state-' + state)
    }

    this.state = state
    this.emit('state', state)

    return this
  }

  /**
   * [_initValue description]
   * @return {Object} The class instance
   */
  _initValue() {
    var opts = this.options

    // create a new div as input element
    if (opts.value) {
      this.setValue(opts.value)
    }
  }

  /**
   * [_onFocus description]
   * @return {Object} The class instance
   */
  _onInputFocus(e) {
    // console.log('_onInputFocus');
    if (this.readonly) return
    this.setState('focus')
  }

  /**
   * [_onBlur description]
   * @return {Object} The class instance
   */
  _onInputBlur() {
    // console.log('_onInputBlur', this.readonly);
    if (this.readonly) return
    this.setState(null)
  }

  /**
   * [_onFocus description]
   * @return {Object} The class instance
   */
  _onInputKeyPress(e) {
    // console.log('_onInputKeyPress', e);

    if (this.get('value') === '') {
      css.add(this.wrapper, 'is-empty')
    } else {
      css.remove(this.wrapper, 'is-empty')
    }

    this.emit('change', this.getValue())
  }

  /**
   * [setError description]
   * @param {string} error Error description
   */
  setError(error) {
    if (error) {
      this.addClass('field-error')
      if (this.error) { this.error.set('html', error) }
    } else {
      if (this.error) { this.removeClass('field-error') }
      if (this.error) { this.error.set('html', '') }
    }
  }

  insert(container, context) {
    insert(this.wrapper, container, context)
  }
}

export default Textfield<|endoftext|>


<commit_before>// node-thumbnail
// (c) 2012-2017 Honza Pokorny
// Licensed under BSD
// https://github.com/honza/node-thumbnail

var fs = require('fs');
var path = require('path');
var crypto = require('crypto');
var os = require('os');

var jimp = require('jimp');
var async = require('async');
var _ = require('lodash');

var options,
  queue,
  defaults,
  done,
  extensions,
  createQueue,
  run,
  resizer,
  isValidFilename;

defaults = {
  prefix: '',
  suffix: '_thumb',
  digest: false,
  hashingType: 'sha1',
  width: 800,
  concurrency: os.cpus().length,
  quiet: false,
  overwrite: false,
  ignore: false, // Ignore unsupported format
  logger: function(message) {
    console.log(message); // eslint-disable-line no-console
  }
};

extensions = ['.jpg', '.jpeg', '.png'];

resizer = function(options, callback) {
  jimp.read(options.srcPath, function(err, file) {
    if (err) {
      throw err;
    }

    file.resize(options.width, jimp.AUTO);
    file.write(options.dstPath, callback);
  });
};

isValidFilename = function(file) {
  return extensions.includes(path.extname(file).toLowerCase());
};

evalCustomExtension = function(customExtension, path) {
  if (extensions.includes(customExtension)) {
    return customExtension;
  }

  return path.extname(srcPath);
};

createQueue = function(settings, resolve, reject) {
  var finished = [];

  queue = async.queue(function(task, callback) {
    if (settings.digest) {
      var hash = crypto.createHash(settings.hashingType);
      var stream = fs.ReadStream(task.options.srcPath);

      stream.on('data', function(d) {
        hash.update(d);
      });

      stream.on('end', function() {
        var d = hash.digest('hex');

        task.options.dstPath = path.join(
          settings.destination,
          d +
            '_' +
            settings.width +
            evalCustomExtension(settings.extension, task.options.srcPath)
        );

        if (settings.overwrite || !fs.existsSync(task.options.dstPath)) {
          resizer(task.options, function() {
            finished.push(task.options);
            callback();
          });
        }
      });
    } else {
      var name = task.options.srcPath;
      var ext = path.extname(name);
      var base = task.options.basename || path.basename(name, ext);

      task.options.dstPath = path.join(
        settings.destination,
        settings.prefix +
          base +
          settings.suffix +
          evalCustomExtension(settings.extension, name)
      );

      if (settings.overwrite || !fs.existsSync(task.options.dstPath)) {
        resizer(task.options, function() {
          finished.push(task.options);
          callback();
        });
      }
    }
  }, settings.concurrency);

  queue.drain = function(hi) {
    if (done) {
      done(finished, null);
    }

    resolve(finished, null);

    if (!settings.quiet) {
      settings.logger('All items have been processed.');
    }
  };
};

run = function(settings, resolve, reject) {
  var images;

  if (fs.statSync(settings.source).isFile()) {
    images = [path.basename(settings.source)];
    settings.source = path.dirname(settings.source);
  } else {
    images = fs.readdirSync(settings.source);
  }

  var containsInvalidFilenames = _.some(images, _.negate(isValidFilename));

  if (containsInvalidFilenames && !settings.ignore) {
    throw new Error('Unsupported file format.');
  }

  createQueue(settings, resolve, reject);

  _.each(images, function(image) {
    if (!isValidFilename(image) && !settings.ignore) {
      return true;
    }

    options = {
      srcPath: path.join(settings.source, image),
      width: settings.width,
      basename: settings.basename
    };

    queue.push({ options: options }, function() {
      if (!settings.quiet) {
        settings.logger(image);
      }
    });
  });
};

exports.thumb = function(options, callback) {
  return new Promise(function(resolve, reject) {
    var settings = _.defaults(options, defaults);

    // options.args is present if run through the command line
    if (options.args) {
      if (options.args.length !== 2) {
        options.logger('Please provide a source and destination directories.');
        return;
      }

      options.source = options.args[0];
      options.destination = options.args[1];
    }

    settings.width = parseInt(settings.width, 10);

    var sourceExists = fs.existsSync(options.source);
    var destExists = fs.existsSync(options.destination);
    var errorMessage;

    if (sourceExists && !destExists) {
      errorMessage =
        "Destination '" + options.destination + "' does not exist.";
    } else if (destExists && !sourceExists) {
      errorMessage = "Source '" + options.source + "' does not exist.";
    } else if (!sourceExists && !destExists) {
      errorMessage =
        "Source '" +
        options.source +
        "' and destination '" +
        options.destination +
        "' do not exist.";
    }

    if (errorMessage) {
      options.logger(errorMessage);

      if (callback) {
        callback(null, new Error(errorMessage));
      }

      reject(null, new Error(errorMessage));
    }

    if (callback) {
      done = callback;
    }

    run(settings, resolve, reject);
  });
};
<commit_msg>Fix function arguments for "evalCustomExtension"<commit_after>// node-thumbnail
// (c) 2012-2017 Honza Pokorny
// Licensed under BSD
// https://github.com/honza/node-thumbnail

var fs = require('fs');
var path = require('path');
var crypto = require('crypto');
var os = require('os');

var jimp = require('jimp');
var async = require('async');
var _ = require('lodash');

var options,
  queue,
  defaults,
  done,
  extensions,
  createQueue,
  run,
  resizer,
  isValidFilename;

defaults = {
  prefix: '',
  suffix: '_thumb',
  digest: false,
  hashingType: 'sha1',
  width: 800,
  concurrency: os.cpus().length,
  quiet: false,
  overwrite: false,
  ignore: false, // Ignore unsupported format
  logger: function(message) {
    console.log(message); // eslint-disable-line no-console
  }
};

extensions = ['.jpg', '.jpeg', '.png'];

resizer = function(options, callback) {
  jimp.read(options.srcPath, function(err, file) {
    if (err) {
      throw err;
    }

    file.resize(options.width, jimp.AUTO);
    file.write(options.dstPath, callback);
  });
};

isValidFilename = function(file) {
  return extensions.includes(path.extname(file).toLowerCase());
};

evalCustomExtension = function(customExtension, srcPath) {
  if (extensions.includes(customExtension)) {
    return customExtension;
  }

  return path.extname(srcPath);
};

createQueue = function(settings, resolve, reject) {
  var finished = [];

  queue = async.queue(function(task, callback) {
    if (settings.digest) {
      var hash = crypto.createHash(settings.hashingType);
      var stream = fs.ReadStream(task.options.srcPath);

      stream.on('data', function(d) {
        hash.update(d);
      });

      stream.on('end', function() {
        var d = hash.digest('hex');

        task.options.dstPath = path.join(
          settings.destination,
          d +
            '_' +
            settings.width +
            evalCustomExtension(settings.extension, task.options.srcPath)
        );

        if (settings.overwrite || !fs.existsSync(task.options.dstPath)) {
          resizer(task.options, function() {
            finished.push(task.options);
            callback();
          });
        }
      });
    } else {
      var name = task.options.srcPath;
      var ext = path.extname(name);
      var base = task.options.basename || path.basename(name, ext);

      task.options.dstPath = path.join(
        settings.destination,
        settings.prefix +
          base +
          settings.suffix +
          evalCustomExtension(settings.extension, name)
      );

      if (settings.overwrite || !fs.existsSync(task.options.dstPath)) {
        resizer(task.options, function() {
          finished.push(task.options);
          callback();
        });
      }
    }
  }, settings.concurrency);

  queue.drain = function(hi) {
    if (done) {
      done(finished, null);
    }

    resolve(finished, null);

    if (!settings.quiet) {
      settings.logger('All items have been processed.');
    }
  };
};

run = function(settings, resolve, reject) {
  var images;

  if (fs.statSync(settings.source).isFile()) {
    images = [path.basename(settings.source)];
    settings.source = path.dirname(settings.source);
  } else {
    images = fs.readdirSync(settings.source);
  }

  var containsInvalidFilenames = _.some(images, _.negate(isValidFilename));

  if (containsInvalidFilenames && !settings.ignore) {
    throw new Error('Unsupported file format.');
  }

  createQueue(settings, resolve, reject);

  _.each(images, function(image) {
    if (!isValidFilename(image) && !settings.ignore) {
      return true;
    }

    options = {
      srcPath: path.join(settings.source, image),
      width: settings.width,
      basename: settings.basename
    };

    queue.push({ options: options }, function() {
      if (!settings.quiet) {
        settings.logger(image);
      }
    });
  });
};

exports.thumb = function(options, callback) {
  return new Promise(function(resolve, reject) {
    var settings = _.defaults(options, defaults);

    // options.args is present if run through the command line
    if (options.args) {
      if (options.args.length !== 2) {
        options.logger('Please provide a source and destination directories.');
        return;
      }

      options.source = options.args[0];
      options.destination = options.args[1];
    }

    settings.width = parseInt(settings.width, 10);

    var sourceExists = fs.existsSync(options.source);
    var destExists = fs.existsSync(options.destination);
    var errorMessage;

    if (sourceExists && !destExists) {
      errorMessage =
        "Destination '" + options.destination + "' does not exist.";
    } else if (destExists && !sourceExists) {
      errorMessage = "Source '" + options.source + "' does not exist.";
    } else if (!sourceExists && !destExists) {
      errorMessage =
        "Source '" +
        options.source +
        "' and destination '" +
        options.destination +
        "' do not exist.";
    }

    if (errorMessage) {
      options.logger(errorMessage);

      if (callback) {
        callback(null, new Error(errorMessage));
      }

      reject(null, new Error(errorMessage));
    }

    if (callback) {
      done = callback;
    }

    run(settings, resolve, reject);
  });
};
<|endoftext|>