<commit_before>import Users from 'meteor/vulcan:users';
import { Utils, getCollection } from 'meteor/vulcan:core';
import moment from 'moment';
import { foreignKeyField, resolverOnlyField } from '../../modules/utils/schemaUtils'
import { schemaDefaultValue } from '../../collectionUtils';
import { PostRelations } from "../postRelations/collection.js"

const formGroups = {
  // TODO - Figure out why properly moving this from custom_fields to schema was producing weird errors and then fix it
  adminOptions: {
    name: "adminOptions",
    order: 25,
    label: "Admin Options",
    startCollapsed: true,
  },
  visibleOptions: {
    name: "visibleOptions",
    order: 30,
    defaultStyle: true,
  }
};

const schema = {
  // ID
  _id: {
    type: String,
    optional: true,
    viewableBy: ['guests'],
  },
  // Timestamp of post creation
  createdAt: {
    type: Date,
    optional: true,
    viewableBy: ['admins'],
    onInsert: () => new Date(),
  },
  // Timestamp of post first appearing on the site (i.e. being approved)
  postedAt: {
    type: Date,
    optional: true,
    viewableBy: ['guests'],
    insertableBy: ['admins'],
    editableBy: ['admins'],
    control: 'datetime',
    group: formGroups.adminOptions,
    onInsert: (post, currentUser) => {
      // Set the post's postedAt if it's going to be approved
      if (!post.postedAt && getCollection('Posts').getDefaultStatus(currentUser) === getCollection('Posts').config.STATUS_APPROVED) {
        return new Date();
      }
    },
    onEdit: (modifier, post) => {
      // Set the post's postedAt if it's going to be approved
      if (!post.postedAt && modifier.$set.status === getCollection('Posts').config.STATUS_APPROVED) {
        return new Date();
      }
    }
  },
  // URL
  url: {
    type: String,
    optional: true,
    max: 500,
    viewableBy: ['guests'],
    insertableBy: ['members'],
    editableBy: [Users.owns, 'sunshineRegiment', 'admins'],
    control: 'url',
    order: 10,
    searchable: true,
    query: `
      SiteData{
        logoUrl
        title
      }
    `,
  },
  // Title
  title: {
    type: String,
    optional: false,
    max: 500,
    viewableBy: ['guests'],
    insertableBy: ['members'],
    editableBy: [Users.owns, 'sunshineRegiment', 'admins'],
    control: 'text',
    order: 20,
    searchable: true
  },
  // Slug
  slug: {
    type: String,
    optional: true,
    viewableBy: ['guests'],
    onInsert: (post) => {
      return Utils.getUnusedSlugByCollectionName("Posts", Utils.slugify(post.title))
    },
    onEdit: (modifier, post) => {
      if (modifier.$set.title) {
        return Utils.getUnusedSlugByCollectionName("Posts", Utils.slugify(modifier.$set.title), false, post._id)
      }
    }
  },
  // Post Excerpt
  excerpt: {
    type: String,
    optional: true,
    viewableBy: ['guests'],
    searchable: true,
  },
  // Count of how many times the post's page was viewed
  viewCount: {
    type: Number,
    optional: true,
    viewableBy: ['admins'],
    defaultValue: 0
  },
  // Timestamp of the last comment
  lastCommentedAt: {
    type: Date,
    denormalized: true,
    optional: true,
    viewableBy: ['guests'],
  },
  // Count of how many times the post's link was clicked
  clickCount: {
    type: Number,
    optional: true,
    viewableBy: ['admins'],
    defaultValue: 0
  },

  deletedDraft: {
    type: Boolean,
    optional: true,
    ...schemaDefaultValue(false),
    viewableBy: ['guests'],
    editableBy: ['members'],
    hidden: true,
  },

  // The post's status. One of pending (`1`), approved (`2`), rejected (`3`), spam (`4`) or deleted (`5`)
  status: {
    type: Number,
    optional: true,
    viewableBy: ['guests'],
    insertableBy: ['admins'],
    editableBy: ['admins'],
    control: 'select',
    onInsert: (document, currentUser) => {
      if (!document.status) {
        return getCollection('Posts').getDefaultStatus(currentUser);
      }
    },
    onEdit: (modifier, document, currentUser) => {
      // if for some reason post status has been removed, give it default status
      if (modifier.$unset && modifier.$unset.status) {
        return getCollection('Posts').getDefaultStatus(currentUser);
      }
    },
    options: () => getCollection('Posts').statuses,
    group: formGroups.adminOptions
  },
  // Whether a post is scheduled in the future or not
  isFuture: {
    type: Boolean,
    optional: true,
    viewableBy: ['guests'],
    onInsert: (post) => {
      // Set the post's isFuture to true if necessary
      if (post.postedAt) {
        const postTime = new Date(post.postedAt).getTime();
        const currentTime = new Date().getTime() + 1000;
        return postTime > currentTime; // round up to the second
      } else {
        return false;
      }
    },
    onEdit: (modifier, post) => {
      // Set the post's isFuture to true if necessary
      if (modifier.$set.postedAt) {
        const postTime = new Date(modifier.$set.postedAt).getTime();
        const currentTime = new Date().getTime() + 1000;
        if (postTime > currentTime) {
          // if a post's postedAt date is in the future, set isFuture to true
          return true;
        } else if (post.isFuture) {
          // else if a post has isFuture to true but its date is in the past, set isFuture to false
          return false;
        }
      }
    }
  },
  // Whether the post is sticky (pinned to the top of posts lists)
  sticky: {
    type: Boolean,
    optional: true,
    ...schemaDefaultValue(false),
    viewableBy: ['guests'],
    insertableBy: ['admins'],
    editableBy: ['admins'],
    control: 'checkbox',
    group: formGroups.adminOptions,
    onInsert: (post) => {
      if(!post.sticky) {
        return false;
      }
    },
    onEdit: (modifier, post) => {
      if (!modifier.$set.sticky) {
        return false;
      }
    }
  },
  // Save info for later spam checking on a post. We will use this for the akismet package
  userIP: {
    type: String,
    optional: true,
    viewableBy: ['admins'],
  },
  userAgent: {
    type: String,
    optional: true,
    viewableBy: ['admins'],
  },
  referrer: {
    type: String,
    optional: true,
    viewableBy: ['admins'],
  },
  // The post author's name
  author: {
    type: String,
    denormalized: true,
    optional: true,
    viewableBy: ['guests'],
    onEdit: (modifier, document, currentUser) => {
      // if userId is changing, change the author name too
      if (modifier.$set && modifier.$set.userId) {
        return Users.getDisplayNameById(modifier.$set.userId)
      }
    }
  },
  // The post author's `_id`.
  userId: {
    ...foreignKeyField({
      idFieldName: "userId",
      resolverName: "user",
      collectionName: "Users",
      type: "User"
    }),
    optional: true,
    control: 'select',
    viewableBy: ['guests'],
    insertableBy: ['members'],
    hidden: true,
  },

  // Used to keep track of when a post has been included in a newsletter
  scheduledAt: {
    type: Date,
    optional: true,
    viewableBy: ['admins'],
  },

  // GraphQL-only fields

  domain: resolverOnlyField({
    type: String,
    viewableBy: ['guests'],
    resolver: (post, args, context) => Utils.getDomain(post.url),
  }),

  pageUrl: resolverOnlyField({
    type: String,
    viewableBy: ['guests'],
    resolver: (post, args, {Posts}) => Posts.getPageUrl(post, true),
  }),

  pageUrlRelative: resolverOnlyField({
    type: String,
    viewableBy: ['guests'],
    resolver: (post, args, {Posts}) => Posts.getPageUrl(post, false),
  }),

  linkUrl: resolverOnlyField({
    type: String,
    viewableBy: ['guests'],
    resolver: (post, args, { Posts }) => {
      return post.url ? Utils.getOutgoingUrl(post.url) : Posts.getPageUrl(post, true);
    },
  }),

  postedAtFormatted: resolverOnlyField({
    type: String,
    viewableBy: ['guests'],
    resolver: (post, args, context) => {
      return moment(post.postedAt).format('dddd, MMMM Do YYYY');
    }
  }),

  emailShareUrl: resolverOnlyField({
    type: String,
    viewableBy: ['guests'],
    resolver: (post, args, { Posts }) => Posts.getEmailShareUrl(post),
  }),

  twitterShareUrl: resolverOnlyField({
    type: String,
    viewableBy: ['guests'],
    resolver: (post, args, { Posts }) => Posts.getTwitterShareUrl(post),
  }),

  facebookShareUrl: resolverOnlyField({
    type: String,
    viewableBy: ['guests'],
    resolver: (post, args, { Posts }) => Posts.getFacebookShareUrl(post),
  }),

  question: {
    type: Boolean,
    optional: true,
    ...schemaDefaultValue(false),
    viewableBy: ['guests'],
    insertableBy: ['members'],
    hidden: true,
  },

  authorIsUnreviewed: {
    type: Boolean,
    optional: true,
    denormalized: true,
    ...schemaDefaultValue(false),
    viewableBy: ['guests'],
    insertableBy: ['admins', 'sunshineRegiment'],
    editableBy: ['admins', 'sunshineRegiment'],
    group: formGroups.adminOptions,
  },

  // DEPRECATED field for GreaterWrong backwards compatibility
  wordCount: resolverOnlyField({
    type: Number,
    viewableBy: ['guests'],
    resolver: (post, args, { Posts }) => {
      const contents = post.contents;
      if (!contents) return 0;
      return contents.wordCount;
    }
  }),
  // DEPRECATED field for GreaterWrong backwards compatibility
  htmlBody: resolverOnlyField({
    type: String,
    viewableBy: ['guests'],
    resolver: (post, args, { Posts }) => {
      const contents = post.contents;
      if (!contents) return "";
      return contents.html;
    }
  }),

  submitToFrontpage: {
    type: Boolean,
    viewableBy: ['guests'],
    insertableBy: ['members'],
    editableBy: [Users.owns, 'admins', 'sunshineRegiment'],
    optional: true,
    hidden: true,
    ...schemaDefaultValue(true),
    onCreate: ({newDocument}) => {
      if (newDocument.isEvent) return false
      if ('submitToFrontpage' in newDocument) return newDocument.submitToFrontpage
      return true
    },
    onUpdate: ({data, document}) => {
      // Not actually the real new document, but good enough for checking the two fields we care about
      const newDocument= {...document, ...data}
      const updatedDocIsEvent = ('isEvent' in newDocument) ? newDocument.isEvent : false
      if (updatedDocIsEvent) return false
      return ('submitToFrontpage' in newDocument) ? newDocument.submitToFrontpage : true
    }
  },

  hiddenRelatedQuestion: {
    type: Boolean,
    viewableBy: ['guests'],
    insertableBy: ['members'],
    editableBy: [Users.owns, 'admins', 'sunshineRegiment'],
    order: 120,
    hidden: true,
    optional: true,
    ...schemaDefaultValue(false),
  },

  originalPostRelationSourceId: {
    type: String,
    optional: true,
    viewableBy: ['guests'],
    insertableBy: ['members'],
    hidden: true,
  },

  sourcePostRelations: resolverOnlyField({
    type: Array,
    graphQLtype: '[PostRelation!]',
    viewableBy: ['guests'],
    resolver: async (post, args, { Posts }) => {
      return await PostRelations.find({targetPostId: post._id}).fetch()
    }
  }),
  'sourcePostRelations.$': {
    type: String,
    optional: true,
  },

  targetPostRelations: resolverOnlyField({
    type: Array,
    graphQLtype: '[PostRelation!]',
    viewableBy: ['guests'],
    resolver: async (post, args, { Posts }) => {
      const postRelations = await Posts.rawCollection().aggregate([
        { $match: { _id: post._id }},
        { $graphLookup: {
            from: "postrelations",
            as: "relatedQuestions",
            startWith: post._id,
            connectFromField: "targetPostId",
            connectToField: "sourcePostId",
            maxDepth: 3
          }
        },
        {
          $project: {
            relatedQuestions: 1
          }
        },
        {
          $unwind: "$relatedQuestions"
        },
        {
          $replaceRoot: {
            newRoot: "$relatedQuestions"
          }
        }
     ]).toArray()
     if (!postRelations || postRelations.length < 1) return []
     return postRelations
    }
  }),
  'targetPostRelations.$': {
    type: String,
    optional: true,
  }
};

export default schema;
<commit_msg>remove cruft<commit_after>import Users from 'meteor/vulcan:users';
import { Utils, getCollection } from 'meteor/vulcan:core';
import moment from 'moment';
import { foreignKeyField, resolverOnlyField } from '../../modules/utils/schemaUtils'
import { schemaDefaultValue } from '../../collectionUtils';
import { PostRelations } from "../postRelations/collection.js"

const formGroups = {
  // TODO - Figure out why properly moving this from custom_fields to schema was producing weird errors and then fix it
  adminOptions: {
    name: "adminOptions",
    order: 25,
    label: "Admin Options",
    startCollapsed: true,
  },
  visibleOptions: {
    name: "visibleOptions",
    order: 30,
    defaultStyle: true,
  }
};

const schema = {
  // ID
  _id: {
    type: String,
    optional: true,
    viewableBy: ['guests'],
  },
  // Timestamp of post creation
  createdAt: {
    type: Date,
    optional: true,
    viewableBy: ['admins'],
    onInsert: () => new Date(),
  },
  // Timestamp of post first appearing on the site (i.e. being approved)
  postedAt: {
    type: Date,
    optional: true,
    viewableBy: ['guests'],
    insertableBy: ['admins'],
    editableBy: ['admins'],
    control: 'datetime',
    group: formGroups.adminOptions,
    onInsert: (post, currentUser) => {
      // Set the post's postedAt if it's going to be approved
      if (!post.postedAt && getCollection('Posts').getDefaultStatus(currentUser) === getCollection('Posts').config.STATUS_APPROVED) {
        return new Date();
      }
    },
    onEdit: (modifier, post) => {
      // Set the post's postedAt if it's going to be approved
      if (!post.postedAt && modifier.$set.status === getCollection('Posts').config.STATUS_APPROVED) {
        return new Date();
      }
    }
  },
  // URL
  url: {
    type: String,
    optional: true,
    max: 500,
    viewableBy: ['guests'],
    insertableBy: ['members'],
    editableBy: [Users.owns, 'sunshineRegiment', 'admins'],
    control: 'url',
    order: 10,
    searchable: true,
    query: `
      SiteData{
        logoUrl
        title
      }
    `,
  },
  // Title
  title: {
    type: String,
    optional: false,
    max: 500,
    viewableBy: ['guests'],
    insertableBy: ['members'],
    editableBy: [Users.owns, 'sunshineRegiment', 'admins'],
    control: 'text',
    order: 20,
    searchable: true
  },
  // Slug
  slug: {
    type: String,
    optional: true,
    viewableBy: ['guests'],
    onInsert: (post) => {
      return Utils.getUnusedSlugByCollectionName("Posts", Utils.slugify(post.title))
    },
    onEdit: (modifier, post) => {
      if (modifier.$set.title) {
        return Utils.getUnusedSlugByCollectionName("Posts", Utils.slugify(modifier.$set.title), false, post._id)
      }
    }
  },
  // Post Excerpt
  excerpt: {
    type: String,
    optional: true,
    viewableBy: ['guests'],
    searchable: true,
  },
  // Count of how many times the post's page was viewed
  viewCount: {
    type: Number,
    optional: true,
    viewableBy: ['admins'],
    defaultValue: 0
  },
  // Timestamp of the last comment
  lastCommentedAt: {
    type: Date,
    denormalized: true,
    optional: true,
    viewableBy: ['guests'],
  },
  // Count of how many times the post's link was clicked
  clickCount: {
    type: Number,
    optional: true,
    viewableBy: ['admins'],
    defaultValue: 0
  },

  deletedDraft: {
    type: Boolean,
    optional: true,
    ...schemaDefaultValue(false),
    viewableBy: ['guests'],
    editableBy: ['members'],
    hidden: true,
  },

  // The post's status. One of pending (`1`), approved (`2`), rejected (`3`), spam (`4`) or deleted (`5`)
  status: {
    type: Number,
    optional: true,
    viewableBy: ['guests'],
    insertableBy: ['admins'],
    editableBy: ['admins'],
    control: 'select',
    onInsert: (document, currentUser) => {
      if (!document.status) {
        return getCollection('Posts').getDefaultStatus(currentUser);
      }
    },
    onEdit: (modifier, document, currentUser) => {
      // if for some reason post status has been removed, give it default status
      if (modifier.$unset && modifier.$unset.status) {
        return getCollection('Posts').getDefaultStatus(currentUser);
      }
    },
    options: () => getCollection('Posts').statuses,
    group: formGroups.adminOptions
  },
  // Whether a post is scheduled in the future or not
  isFuture: {
    type: Boolean,
    optional: true,
    viewableBy: ['guests'],
    onInsert: (post) => {
      // Set the post's isFuture to true if necessary
      if (post.postedAt) {
        const postTime = new Date(post.postedAt).getTime();
        const currentTime = new Date().getTime() + 1000;
        return postTime > currentTime; // round up to the second
      } else {
        return false;
      }
    },
    onEdit: (modifier, post) => {
      // Set the post's isFuture to true if necessary
      if (modifier.$set.postedAt) {
        const postTime = new Date(modifier.$set.postedAt).getTime();
        const currentTime = new Date().getTime() + 1000;
        if (postTime > currentTime) {
          // if a post's postedAt date is in the future, set isFuture to true
          return true;
        } else if (post.isFuture) {
          // else if a post has isFuture to true but its date is in the past, set isFuture to false
          return false;
        }
      }
    }
  },
  // Whether the post is sticky (pinned to the top of posts lists)
  sticky: {
    type: Boolean,
    optional: true,
    ...schemaDefaultValue(false),
    viewableBy: ['guests'],
    insertableBy: ['admins'],
    editableBy: ['admins'],
    control: 'checkbox',
    group: formGroups.adminOptions,
    onInsert: (post) => {
      if(!post.sticky) {
        return false;
      }
    },
    onEdit: (modifier, post) => {
      if (!modifier.$set.sticky) {
        return false;
      }
    }
  },
  // Save info for later spam checking on a post. We will use this for the akismet package
  userIP: {
    type: String,
    optional: true,
    viewableBy: ['admins'],
  },
  userAgent: {
    type: String,
    optional: true,
    viewableBy: ['admins'],
  },
  referrer: {
    type: String,
    optional: true,
    viewableBy: ['admins'],
  },
  // The post author's name
  author: {
    type: String,
    denormalized: true,
    optional: true,
    viewableBy: ['guests'],
    onEdit: (modifier, document, currentUser) => {
      // if userId is changing, change the author name too
      if (modifier.$set && modifier.$set.userId) {
        return Users.getDisplayNameById(modifier.$set.userId)
      }
    }
  },
  // The post author's `_id`.
  userId: {
    ...foreignKeyField({
      idFieldName: "userId",
      resolverName: "user",
      collectionName: "Users",
      type: "User"
    }),
    optional: true,
    control: 'select',
    viewableBy: ['guests'],
    insertableBy: ['members'],
    hidden: true,
  },

  // Used to keep track of when a post has been included in a newsletter
  scheduledAt: {
    type: Date,
    optional: true,
    viewableBy: ['admins'],
  },

  // GraphQL-only fields

  domain: resolverOnlyField({
    type: String,
    viewableBy: ['guests'],
    resolver: (post, args, context) => Utils.getDomain(post.url),
  }),

  pageUrl: resolverOnlyField({
    type: String,
    viewableBy: ['guests'],
    resolver: (post, args, {Posts}) => Posts.getPageUrl(post, true),
  }),

  pageUrlRelative: resolverOnlyField({
    type: String,
    viewableBy: ['guests'],
    resolver: (post, args, {Posts}) => Posts.getPageUrl(post, false),
  }),

  linkUrl: resolverOnlyField({
    type: String,
    viewableBy: ['guests'],
    resolver: (post, args, { Posts }) => {
      return post.url ? Utils.getOutgoingUrl(post.url) : Posts.getPageUrl(post, true);
    },
  }),

  postedAtFormatted: resolverOnlyField({
    type: String,
    viewableBy: ['guests'],
    resolver: (post, args, context) => {
      return moment(post.postedAt).format('dddd, MMMM Do YYYY');
    }
  }),

  emailShareUrl: resolverOnlyField({
    type: String,
    viewableBy: ['guests'],
    resolver: (post, args, { Posts }) => Posts.getEmailShareUrl(post),
  }),

  twitterShareUrl: resolverOnlyField({
    type: String,
    viewableBy: ['guests'],
    resolver: (post, args, { Posts }) => Posts.getTwitterShareUrl(post),
  }),

  facebookShareUrl: resolverOnlyField({
    type: String,
    viewableBy: ['guests'],
    resolver: (post, args, { Posts }) => Posts.getFacebookShareUrl(post),
  }),

  question: {
    type: Boolean,
    optional: true,
    ...schemaDefaultValue(false),
    viewableBy: ['guests'],
    insertableBy: ['members'],
    hidden: true,
  },

  authorIsUnreviewed: {
    type: Boolean,
    optional: true,
    denormalized: true,
    ...schemaDefaultValue(false),
    viewableBy: ['guests'],
    insertableBy: ['admins', 'sunshineRegiment'],
    editableBy: ['admins', 'sunshineRegiment'],
    group: formGroups.adminOptions,
  },

  // DEPRECATED field for GreaterWrong backwards compatibility
  wordCount: resolverOnlyField({
    type: Number,
    viewableBy: ['guests'],
    resolver: (post, args, { Posts }) => {
      const contents = post.contents;
      if (!contents) return 0;
      return contents.wordCount;
    }
  }),
  // DEPRECATED field for GreaterWrong backwards compatibility
  htmlBody: resolverOnlyField({
    type: String,
    viewableBy: ['guests'],
    resolver: (post, args, { Posts }) => {
      const contents = post.contents;
      if (!contents) return "";
      return contents.html;
    }
  }),

  submitToFrontpage: {
    type: Boolean,
    viewableBy: ['guests'],
    insertableBy: ['members'],
    editableBy: [Users.owns, 'admins', 'sunshineRegiment'],
    optional: true,
    hidden: true,
    ...schemaDefaultValue(true),
    onCreate: ({newDocument}) => {
      if (newDocument.isEvent) return false
      if ('submitToFrontpage' in newDocument) return newDocument.submitToFrontpage
      return true
    },
    onUpdate: ({data, document}) => {
      // Not actually the real new document, but good enough for checking the two fields we care about
      const newDocument= {...document, ...data}
      const updatedDocIsEvent = ('isEvent' in newDocument) ? newDocument.isEvent : false
      if (updatedDocIsEvent) return false
      return ('submitToFrontpage' in newDocument) ? newDocument.submitToFrontpage : true
    }
  },

  hiddenRelatedQuestion: {
    type: Boolean,
    viewableBy: ['guests'],
    insertableBy: ['members'],
    editableBy: [Users.owns, 'admins', 'sunshineRegiment'],
    hidden: true,
    optional: true,
    ...schemaDefaultValue(false),
  },

  originalPostRelationSourceId: {
    type: String,
    optional: true,
    viewableBy: ['guests'],
    insertableBy: ['members'],
    hidden: true,
  },

  sourcePostRelations: resolverOnlyField({
    type: Array,
    graphQLtype: '[PostRelation!]',
    viewableBy: ['guests'],
    resolver: async (post, args, { Posts }) => {
      return await PostRelations.find({targetPostId: post._id}).fetch()
    }
  }),
  'sourcePostRelations.$': {
    type: String,
    optional: true,
  },

  targetPostRelations: resolverOnlyField({
    type: Array,
    graphQLtype: '[PostRelation!]',
    viewableBy: ['guests'],
    resolver: async (post, args, { Posts }) => {
      const postRelations = await Posts.rawCollection().aggregate([
        { $match: { _id: post._id }},
        { $graphLookup: {
            from: "postrelations",
            as: "relatedQuestions",
            startWith: post._id,
            connectFromField: "targetPostId",
            connectToField: "sourcePostId",
            maxDepth: 3
          }
        },
        {
          $project: {
            relatedQuestions: 1
          }
        },
        {
          $unwind: "$relatedQuestions"
        },
        {
          $replaceRoot: {
            newRoot: "$relatedQuestions"
          }
        }
     ]).toArray()
     if (!postRelations || postRelations.length < 1) return []
     return postRelations
    }
  }),
  'targetPostRelations.$': {
    type: String,
    optional: true,
  }
};

export default schema;
<|endoftext|>


<commit_before>//  This file containts opportunities bussines logic
//  function definitions section
lx.contact.renderContactDuplicates = function (duplicates) {
    console.warn("duplicados devueltos:", duplicates);
    $(".contact_duplicates ul li").remove();
    if (duplicates.data.length) {
        duplicates.data.forEach(function (element) {
            primary_address = {};
            element.emails.forEach(function (element) {
                if (element.primary_address === "1") {
                    primary_address.id = element.id;
                    primary_address.email_address = element.email_address;
                }
            });
            duplicate_detail = '<li id="' + element.id;
            duplicate_detail += '" data-account_id="' + element.accounts[0].id;
            duplicate_detail += '" data-cedula_c="' + element.cedula_c;
            duplicate_detail += '" data-first_name="' + element.first_name;
            duplicate_detail += '" data-last_name="' + element.last_name;
            duplicate_detail += '" data-lastname2_c="' + element.lastname2_c;
            duplicate_detail += '" data-phone_mobile="' + element.phone_mobile;
            duplicate_detail += '" data-phone_work="' + element.phone_work;
            duplicate_detail += '" data-title="' + element.title;
            duplicate_detail +=
                '" data-primary_email_address_id="' + primary_address.id;
            duplicate_detail +=
                '" data-primary_email_address="' +
                primary_address.email_address;
            duplicate_detail += '">';
            duplicate_detail +=
                element.first_name +
                " " +
                element.last_name +
                " " +
                element.lastname2_c +
                " " +
                element.cedula_c +
                " ";
            element.emails.forEach(function (element) {
                duplicate_detail += element.email_address + " ";
            });
            duplicate_detail += element.phone_mobile + " ";
            duplicate_detail += element.phone_work;
            // Client specific only fields
            // duplicate_detail += ' - ' + element.{fieldname};
            // Client specific only fields
            duplicate_detail += "
";
            $(".contact_duplicates ul").append(duplicate_detail);
        });
        newh = duplicates.data.length * 20 + 18; // last 18px bit is for duplicates title
        $(
            "#contact_duplicates_" + duplicates.fieldname + " .yui-ac-content"
        ).css("height", newh + "px");
        $(
            "#contact_duplicates_" + duplicates.fieldname + " .yui-ac-content"
        ).show(500);
        $(".contact_duplicates li").off("mouseover.contact_duplicates_list");
        $(".contact_duplicates li").on(
            "mouseover.contact_duplicates_list",
            function () {
                $(this).toggleClass("yui-ac-highlight");
                $(this).siblings("li").removeClass("yui-ac-highlight");
            }
        );
        $(".contact_duplicates li").off("click.contact_duplicates_list");
        $(".contact_duplicates li").on(
            "click.contact_duplicates_list",
            function () {
                $(".contact_duplicates .yui-ac-content").hide(500);
                console.warn(
                    "Redirecting to contacts Editview: - " +
                        window.location.origin +
                        window.location.pathname +
                        "?module=Contacts&return_module=Contacts&action=EditView&record=" +
                        this.id
                );
                toastr["info"](
                    "Redireccionando a " + $(this).data("first_name") + "...",
                    "Posibles duplicados encontrados",
                    {
                        positionClass: "toast-bottom-center",
                        showDuration: "0",
                        hideDuration: "0",
                        timeOut: "4000",
                        extendedTimeOut: "0",
                        progressBar: true,
                    }
                );
                window.location.href =
                    window.location.origin +
                    window.location.pathname +
                    "?module=Contacts&return_module=Contacts&action=EditView&record=" +
                    this.id;
            }
        );
    }
};

lx.contact.resultsSearchTSECRHandler = function (forceCheck) {
    try {
        if (forceCheck) {
            lx.lionixCRM.config.modules = undefined;
            $("#cedula_c_lxajaxed").remove();
        }
        if ($("#cedula_c_lxajaxed").length == 0) {
            keyup_status = "disabled";
            $("#cedula_c").off("keyup.results_search_tsecr");
            if (lx.lionixCRM.config.modules.contacts.results_search_tsecr) {
                keyup_status = "enabled";
                $("#cedula_c").on(
                    "keyup.results_search_tsecr",
                    async function () {
                        if (lx.lionixCRM.config.debuglx) {
                            console.warn(
                                "key up ->",
                                String.fromCharCode(event.which)
                            );
                        }
                        if ($(this).val().length != 9) {
                            $("#first_name").val("");
                            $("#last_name").val("");
                            $("#lastname2_c").val("");
                        } else {
                            typed_cedula = $(this).val();
                            tsecrlist = await lx.lionixCRM.getTSECRData({
                                // Infoticos must be on same database server this SuiteCRM instance.
                                focusfieldname: this.id,
                                cedula_c: typed_cedula,
                            });
                            console.warn(
                                "After query to lx.lionixCRM.getTSECRData:",
                                tsecrlist
                            );
                            $("#cedula_c").val(tsecrlist.data[0].cedula_c);
                            $("#first_name").val(tsecrlist.data[0].first_name);
                            $("#last_name").val(tsecrlist.data[0].last_name);
                            $("#lastname2_c").val(
                                tsecrlist.data[0].lastname2_c
                            );
                            $("#first_name").focus();
                            $("#first_name").trigger("keypress");
                        }
                    }
                );
            }
            $("#cedula_c").append(
                '
'
            );
            console.warn(
                "keyup.results_search_tsecr on #cedula_c " + keyup_status
            );
        } else {
            if (lx.lionixCRM.config.debuglx) {
                console.warn(
                    "cedula_c_lxajaxed div indicator already exists and it's [" +
                        $("#cedula_c_lxajaxed").data("results_search_tsecr") +
                        "]."
                );
            }
        }
    } catch (error) {
        console.warn("Modules[contacts] properties are not present!");
        console.warn("Retrieving modules[contacts] properties...");
        lx.lionixCRM.getConfigOption("modules").then(function (data) {
            console.warn("Modules[contacts] successfully retrieved", data);
            lx.contact.resultsSearchTSECRHandler(false);
        });
    }
};

lx.contact.resultsListDuplicatesHandler = function (forceCheck) {
    try {
        if (forceCheck) {
            lx.lionixCRM.config.modules = undefined;
            $("#full_name_lxajaxed").remove();
        }
        if ($("#full_name_lxajaxed").length == 0) {
            keyup_status = "disabled";
            $("#first_name, #last_name, #lastname2_c").off(
                "focusout.results_list_duplicates"
            );
            if (lx.lionixCRM.config.modules.contacts.results_list_duplicates) {
                keyup_status = "enabled";
                $("#first_name, #last_name, #lastname2_c").on(
                    "keypress.results_list_duplicates",
                    function () {
                        ffn = $("#first_name");
                        fln = $("#last_name");
                        fln2 = $("#lastname2_c");
                        // femail = $('#Contacts0emailAddress0');
                        // fced = $('#cedula_c');
                        if (
                            $(ffn).val().length > 2 &&
                            ($(fln).val().length > 2 ||
                                $(fln2).val().length > 2)
                        ) {
                            if (
                                $("#contact_duplicates_" + this.id).length == 0
                            ) {
                                // width: 750px can be changed in each Client
                                width = lx.lionixCRM.config.modules.contacts
                                    .results_list_duplicates_width
                                    ? lx.lionixCRM.config.modules.contacts
                                          .results_list_duplicates_width
                                    : 750;
                                $(this).before(
                                    '

Posibles duplicados encontrados

'
                                );
                            }
                            $("#first_name, #last_name, #lastname2_c").off(
                                "focusout.results_list_duplicates"
                            );
                            $("#first_name, #last_name, #lastname2_c").on(
                                "focusout.results_list_duplicates",
                                function () {
                                    $(
                                        ".contact_duplicates .yui-ac-content"
                                    ).hide(500);
                                }
                            );
                            lx.lionixCRM
                                .getContactDuplicates({
                                    fieldname: this.id,
                                    first_name: $(ffn).val(),
                                    last_name: $(fln).val(),
                                    lastname2_c: $(fln2).val(),
                                    // "cedula_c": $(fced).val(),
                                    // "email_address": $(femail).val()
                                })
                                .then(function (duplicates) {
                                    lx.contact.renderContactDuplicates(
                                        duplicates
                                    );
                                });
                        }
                    }
                );
            }
            $("#cedula_c").append(
                '
'
            );
            console.warn(
                "keypress.results_list_duplicates on #first_name, #last_name and #lastname2_c " +
                    keyup_status
            );
        } else {
            if (lx.lionixCRM.config.debuglx) {
                console.warn(
                    "full_name_lxajaxed div indicator already exists and it's [" +
                        $("#full_name_lxajaxed").data(
                            "results_list_duplicates"
                        ) +
                        "]."
                );
            }
        }
    } catch (error) {
        console.warn("Modules[contacts] properties are not present!");
        console.warn("Retrieving modules[contacts] properties...");
        lx.lionixCRM.getConfigOption("modules").then(function (data) {
            console.warn("Modules[contacts] successfully retrieved", data);
            lx.contact.resultsListDuplicatesHandler(false);
        });
    }
};

//Self-Invoking Anonymous Function Notation
// !function(){}();  easy to read, the result is unimportant.
// (function(){})();  like above but more parens.
// (function(){}());  Douglas Crockford's style when you need function results.
// Further reading: http://javascript.crockford.com/code.html then search for invoked immediately
!(function () {
    // create an observer instance
    // https://developer.mozilla.org/en/docs/Web/API/MutationObserver
    var observer = new MutationObserver(function (mutations) {
        if (mutations) {
            var crmEditView = document.forms["EditView"];
            if (crmEditView && !!crmEditView.module) {
                if (crmEditView.module.value == "Contacts") {
                    if (lx.lionixCRM.config.debuglx) {
                        console.warn(
                            "Bussines logic observer '%s' '%s' '%s' '%s'",
                            "contacts",
                            "lx-cedula-c.js",
                            "!function()",
                            "initial"
                        );
                    }
                    lx.contact.resultsSearchTSECRHandler(false);
                    lx.contact.resultsListDuplicatesHandler(false);
                }
            }
            // if needed only once, you can stop observing with observer.disconnect();
            // observer.disconnect();
        }
    });
    // Observer target
    var target = document.querySelector("#content");
    if (target) {
        // configuration of the observer:
        // NOTE: At the very least, childList, attributes, or characterData must be set to true. Otherwise, "An invalid or illegal string was specified" error is thrown.
        var config = {
            attributes: true,
            childList: true,
            // characterData: true,
            subtree: true,
        };
        // pass in the target node, as well as the observer options
        observer.observe(target, config);
    }
    // end observer
})();
//eof
<commit_msg>refactor: template string on warn [ch225]<commit_after>//  This file containts opportunities bussines logic
//  function definitions section
lx.contact.renderContactDuplicates = function (duplicates) {
    console.warn("duplicados devueltos:", duplicates);
    $(".contact_duplicates ul li").remove();
    if (duplicates.data.length) {
        duplicates.data.forEach(function (element) {
            primary_address = {};
            element.emails.forEach(function (element) {
                if (element.primary_address === "1") {
                    primary_address.id = element.id;
                    primary_address.email_address = element.email_address;
                }
            });
            duplicate_detail = '<li id="' + element.id;
            duplicate_detail += '" data-account_id="' + element.accounts[0].id;
            duplicate_detail += '" data-cedula_c="' + element.cedula_c;
            duplicate_detail += '" data-first_name="' + element.first_name;
            duplicate_detail += '" data-last_name="' + element.last_name;
            duplicate_detail += '" data-lastname2_c="' + element.lastname2_c;
            duplicate_detail += '" data-phone_mobile="' + element.phone_mobile;
            duplicate_detail += '" data-phone_work="' + element.phone_work;
            duplicate_detail += '" data-title="' + element.title;
            duplicate_detail +=
                '" data-primary_email_address_id="' + primary_address.id;
            duplicate_detail +=
                '" data-primary_email_address="' +
                primary_address.email_address;
            duplicate_detail += '">';
            duplicate_detail +=
                element.first_name +
                " " +
                element.last_name +
                " " +
                element.lastname2_c +
                " " +
                element.cedula_c +
                " ";
            element.emails.forEach(function (element) {
                duplicate_detail += element.email_address + " ";
            });
            duplicate_detail += element.phone_mobile + " ";
            duplicate_detail += element.phone_work;
            // Client specific only fields
            // duplicate_detail += ' - ' + element.{fieldname};
            // Client specific only fields
            duplicate_detail += "
";
            $(".contact_duplicates ul").append(duplicate_detail);
        });
        newh = duplicates.data.length * 20 + 18; // last 18px bit is for duplicates title
        $(
            "#contact_duplicates_" + duplicates.fieldname + " .yui-ac-content"
        ).css("height", newh + "px");
        $(
            "#contact_duplicates_" + duplicates.fieldname + " .yui-ac-content"
        ).show(500);
        $(".contact_duplicates li").off("mouseover.contact_duplicates_list");
        $(".contact_duplicates li").on(
            "mouseover.contact_duplicates_list",
            function () {
                $(this).toggleClass("yui-ac-highlight");
                $(this).siblings("li").removeClass("yui-ac-highlight");
            }
        );
        $(".contact_duplicates li").off("click.contact_duplicates_list");
        $(".contact_duplicates li").on(
            "click.contact_duplicates_list",
            function () {
                $(".contact_duplicates .yui-ac-content").hide(500);
                console.warn(
                    "Redirecting to contacts Editview: - " +
                        window.location.origin +
                        window.location.pathname +
                        "?module=Contacts&return_module=Contacts&action=EditView&record=" +
                        this.id
                );
                toastr["info"](
                    "Redireccionando a " + $(this).data("first_name") + "...",
                    "Posibles duplicados encontrados",
                    {
                        positionClass: "toast-bottom-center",
                        showDuration: "0",
                        hideDuration: "0",
                        timeOut: "4000",
                        extendedTimeOut: "0",
                        progressBar: true,
                    }
                );
                window.location.href =
                    window.location.origin +
                    window.location.pathname +
                    "?module=Contacts&return_module=Contacts&action=EditView&record=" +
                    this.id;
            }
        );
    }
};

lx.contact.resultsSearchTSECRHandler = function (forceCheck) {
    try {
        if (forceCheck) {
            lx.lionixCRM.config.modules = undefined;
            $("#cedula_c_lxajaxed").remove();
        }
        if ($("#cedula_c_lxajaxed").length == 0) {
            keyup_status = "disabled";
            $("#cedula_c").off("keyup.results_search_tsecr");
            if (lx.lionixCRM.config.modules.contacts.results_search_tsecr) {
                keyup_status = "enabled";
                $("#cedula_c").on(
                    "keyup.results_search_tsecr",
                    async function () {
                        if (lx.lionixCRM.config.debuglx) {
                            console.warn(
                                "key up ->",
                                String.fromCharCode(event.which)
                            );
                        }
                        if ($(this).val().length != 9) {
                            $("#first_name").val("");
                            $("#last_name").val("");
                            $("#lastname2_c").val("");
                        } else {
                            typed_cedula = $(this).val();
                            tsecrlist = await lx.lionixCRM.getTSECRData({
                                // Infoticos must be on same database server this SuiteCRM instance.
                                focusfieldname: this.id,
                                cedula_c: typed_cedula,
                            });
                            console.warn(
                                "After query to lx.lionixCRM.getTSECRData:",
                                tsecrlist
                            );
                            $("#cedula_c").val(tsecrlist.data[0].cedula_c);
                            $("#first_name").val(tsecrlist.data[0].first_name);
                            $("#last_name").val(tsecrlist.data[0].last_name);
                            $("#lastname2_c").val(
                                tsecrlist.data[0].lastname2_c
                            );
                            $("#first_name").focus();
                            $("#first_name").trigger("keypress");
                        }
                    }
                );
            }
            $("#cedula_c").append(
                '
'
            );
            console.warn(
                "keyup.results_search_tsecr on #cedula_c " + keyup_status
            );
        } else {
            if (lx.lionixCRM.config.debuglx) {
                console.warn(
                    `cedula_c_lxajaxed div indicator already exists and it's [${$(
                        "#cedula_c_lxajaxed"
                    ).data("results_search_tsecr")}].`
                );
            }
        }
    } catch (error) {
        console.warn("Modules[contacts] properties are not present!");
        console.warn("Retrieving modules[contacts] properties...");
        lx.lionixCRM.getConfigOption("modules").then(function (data) {
            console.warn("Modules[contacts] successfully retrieved", data);
            lx.contact.resultsSearchTSECRHandler(false);
        });
    }
};

lx.contact.resultsListDuplicatesHandler = function (forceCheck) {
    try {
        if (forceCheck) {
            lx.lionixCRM.config.modules = undefined;
            $("#full_name_lxajaxed").remove();
        }
        if ($("#full_name_lxajaxed").length == 0) {
            keyup_status = "disabled";
            $("#first_name, #last_name, #lastname2_c").off(
                "focusout.results_list_duplicates"
            );
            if (lx.lionixCRM.config.modules.contacts.results_list_duplicates) {
                keyup_status = "enabled";
                $("#first_name, #last_name, #lastname2_c").on(
                    "keypress.results_list_duplicates",
                    function () {
                        ffn = $("#first_name");
                        fln = $("#last_name");
                        fln2 = $("#lastname2_c");
                        // femail = $('#Contacts0emailAddress0');
                        // fced = $('#cedula_c');
                        if (
                            $(ffn).val().length > 2 &&
                            ($(fln).val().length > 2 ||
                                $(fln2).val().length > 2)
                        ) {
                            if (
                                $("#contact_duplicates_" + this.id).length == 0
                            ) {
                                // width: 750px can be changed in each Client
                                width = lx.lionixCRM.config.modules.contacts
                                    .results_list_duplicates_width
                                    ? lx.lionixCRM.config.modules.contacts
                                          .results_list_duplicates_width
                                    : 750;
                                $(this).before(
                                    '

Posibles duplicados encontrados

'
                                );
                            }
                            $("#first_name, #last_name, #lastname2_c").off(
                                "focusout.results_list_duplicates"
                            );
                            $("#first_name, #last_name, #lastname2_c").on(
                                "focusout.results_list_duplicates",
                                function () {
                                    $(
                                        ".contact_duplicates .yui-ac-content"
                                    ).hide(500);
                                }
                            );
                            lx.lionixCRM
                                .getContactDuplicates({
                                    fieldname: this.id,
                                    first_name: $(ffn).val(),
                                    last_name: $(fln).val(),
                                    lastname2_c: $(fln2).val(),
                                    // "cedula_c": $(fced).val(),
                                    // "email_address": $(femail).val()
                                })
                                .then(function (duplicates) {
                                    lx.contact.renderContactDuplicates(
                                        duplicates
                                    );
                                });
                        }
                    }
                );
            }
            $("#cedula_c").append(
                '
'
            );
            console.warn(
                "keypress.results_list_duplicates on #first_name, #last_name and #lastname2_c " +
                    keyup_status
            );
        } else {
            if (lx.lionixCRM.config.debuglx) {
                console.warn(
                    "full_name_lxajaxed div indicator already exists and it's [" +
                        $("#full_name_lxajaxed").data(
                            "results_list_duplicates"
                        ) +
                        "]."
                );
            }
        }
    } catch (error) {
        console.warn("Modules[contacts] properties are not present!");
        console.warn("Retrieving modules[contacts] properties...");
        lx.lionixCRM.getConfigOption("modules").then(function (data) {
            console.warn("Modules[contacts] successfully retrieved", data);
            lx.contact.resultsListDuplicatesHandler(false);
        });
    }
};

//Self-Invoking Anonymous Function Notation
// !function(){}();  easy to read, the result is unimportant.
// (function(){})();  like above but more parens.
// (function(){}());  Douglas Crockford's style when you need function results.
// Further reading: http://javascript.crockford.com/code.html then search for invoked immediately
!(function () {
    // create an observer instance
    // https://developer.mozilla.org/en/docs/Web/API/MutationObserver
    var observer = new MutationObserver(function (mutations) {
        if (mutations) {
            var crmEditView = document.forms["EditView"];
            if (crmEditView && !!crmEditView.module) {
                if (crmEditView.module.value == "Contacts") {
                    if (lx.lionixCRM.config.debuglx) {
                        console.warn(
                            "Bussines logic observer '%s' '%s' '%s' '%s'",
                            "contacts",
                            "lx-cedula-c.js",
                            "!function()",
                            "initial"
                        );
                    }
                    lx.contact.resultsSearchTSECRHandler(false);
                    lx.contact.resultsListDuplicatesHandler(false);
                }
            }
            // if needed only once, you can stop observing with observer.disconnect();
            // observer.disconnect();
        }
    });
    // Observer target
    var target = document.querySelector("#content");
    if (target) {
        // configuration of the observer:
        // NOTE: At the very least, childList, attributes, or characterData must be set to true. Otherwise, "An invalid or illegal string was specified" error is thrown.
        var config = {
            attributes: true,
            childList: true,
            // characterData: true,
            subtree: true,
        };
        // pass in the target node, as well as the observer options
        observer.observe(target, config);
    }
    // end observer
})();
//eof
<|endoftext|>


<commit_before>var jsonEditor = require('json-editor');
var _ = require('underscore');
var backbone = require('backbone');
var deepEmpty = require('deep-empty');
var Goodtables = require('goodtables');
var omitEmpty = require('omit-empty');
var Promise = require('bluebird');
var JSONEditorView = JSONEditor;

JSONEditorView.prototype.init = _.wrap(JSONEditorView.prototype.init, function(init) {
  init.apply(this, _.rest(arguments));

  this.on('ready', (function() {
    // Remove Top-level collapse button
    $(this.root.toggle_button).remove();

    // Detecting changes
    this.changed = false;

    // If on the previous form was entered values try to apply it to new form
    if(this.options.initialData)
      this.setValue(_.extend({}, this.getValue(this.options.initialData), this.options.initialData));

    // After `ready` event fired, editor fire `change` event regarding to the initial changes
    this.on('change', _.after(3, (function() {
      var resources = this.getEditor('root.resources');
      var resourcesLength = _.result(resources.rows, 'length');

      this.changed = true;

      // Expand resources section if there are any resources, collapse if row is empty
      if(resourcesLength && resources.collapsed || !resourcesLength && !resources.collapsed)
        $(resources.toggle_button).trigger('click');

      // Do not allow changing schema field type — disable type selectbox
      $('[data-schemapath]:not([data-schematype]) select.form-control', this.element).prop('hidden', true);
    }).bind(this)));

    // Collapse editor and add empty item if it has no value
    _.each($('[data-schemapath^="root."]:has(.json-editor-btn-collapse)', this.element), function(E) {
      var editor = this.getEditor($(E).data('schemapath'));
      var isEmpty = _.isEmpty(editor.getValue());

      // Empty array data should have one empty item
      if(_.contains(['resources'], $(E).data('schemapath').replace('root.', '')) && !editor.rows.length)
        editor.addRow();

      if(isEmpty && !editor.collapsed)
        $(editor.toggle_button).trigger('click');
    }, this);

    // Looks like previous loop is somehow async
    setTimeout((function() { $('#json-code').prop('hidden', false); }).bind(this), 300);

    // Remove collapse button on add new item in collection
    _.each($('[data-schemapath][data-schemapath!=root]:has(.json-editor-btn-add)', this.element), function(E) {
      var
        editor = this.getEditor($(E).data('schemapath'));

      $(_.pluck(editor.rows, 'toggle_button')).remove();

      $(editor.add_row_button).click((function() {
        $(_.last(this.rows).toggle_button).remove();
      }).bind(editor));
    }, this);
  }).bind(this));
});

  // Omit empty properties and "0" values of object properties put into array.
  // Stick with that complex solution because "0" is the default value
JSONEditorView.prototype.getCleanValue = function () { return deepEmpty(this.getValue(), function(O) {
  return !_.isEmpty(O) && !_.every(O, function(I) { return _.isEmpty(omitEmpty(I, true)); })
}); };

// Validate resources and show errors message at header of Schema row
JSONEditorView.prototype.validateResources = function () {
  var rows = this.getEditor('root.resources').rows;

  return Promise.each(rows, (function(R) {
    if(_.isUndefined(R.resourceIsValid))
      return (new Goodtables({method: 'post', report_type: 'grouped'}))
        .run(R.dataSource.data, JSON.stringify(R.dataSource.schema))
        .then(function(E) { R.resourceIsValid = !Boolean(E.errors.length); });
    else
      return new Promise(function(RS, RJ) { RS(true); })
  }).bind(this)).then(function() {
    // Show error in resource section if it is invalid
    rows.forEach(function(R) {
      if(R.resourceIsValid)
        return true;

      R.editors.schema.showValidationErrors([{
        message: 'Resource is invalid',
        path: R.editors.schema.path
      }]);
    })
  });
}

// Proper representation of all form buttons. Avoid changing each newly
// rendered button by rewriting the .getButton()
JSONEditor.defaults.themes.bootstrap3.prototype.getButton = function(text, icon, title) {
  var el = document.createElement('button');

  el.type = 'button';
  el.className += 'btn btn-info btn-sm';
  this.setButtonText(el,text,icon,title);
  return el;
};

module.exports.JSONEditorView = JSONEditorView;
<commit_msg>Fixed indentation<commit_after>var jsonEditor = require('json-editor');
var _ = require('underscore');
var backbone = require('backbone');
var deepEmpty = require('deep-empty');
var Goodtables = require('goodtables');
var omitEmpty = require('omit-empty');
var Promise = require('bluebird');
var JSONEditorView = JSONEditor;

JSONEditorView.prototype.init = _.wrap(JSONEditorView.prototype.init, function(init) {
  init.apply(this, _.rest(arguments));

  this.on('ready', (function() {
    // Remove Top-level collapse button
    $(this.root.toggle_button).remove();

    // Detecting changes
    this.changed = false;

    // If on the previous form was entered values try to apply it to new form
    if(this.options.initialData)
      this.setValue(_.extend({}, this.getValue(this.options.initialData), this.options.initialData));

    // After `ready` event fired, editor fire `change` event regarding to the initial changes
    this.on('change', _.after(3, (function() {
      var resources = this.getEditor('root.resources');
      var resourcesLength = _.result(resources.rows, 'length');

      this.changed = true;

      // Expand resources section if there are any resources, collapse if row is empty
      if(resourcesLength && resources.collapsed || !resourcesLength && !resources.collapsed)
        $(resources.toggle_button).trigger('click');

      // Do not allow changing schema field type — disable type selectbox
      $('[data-schemapath]:not([data-schematype]) select.form-control', this.element).prop('hidden', true);
    }).bind(this)));

    // Collapse editor and add empty item if it has no value
    _.each($('[data-schemapath^="root."]:has(.json-editor-btn-collapse)', this.element), function(E) {
      var editor = this.getEditor($(E).data('schemapath'));
      var isEmpty = _.isEmpty(editor.getValue());

      // Empty array data should have one empty item
      if(_.contains(['resources'], $(E).data('schemapath').replace('root.', '')) && !editor.rows.length)
        editor.addRow();

      if(isEmpty && !editor.collapsed)
        $(editor.toggle_button).trigger('click');
    }, this);

    // Looks like previous loop is somehow async
    setTimeout((function() { $('#json-code').prop('hidden', false); }).bind(this), 300);

    // Remove collapse button on add new item in collection
    _.each($('[data-schemapath][data-schemapath!=root]:has(.json-editor-btn-add)', this.element), function(E) {
      var
        editor = this.getEditor($(E).data('schemapath'));

      $(_.pluck(editor.rows, 'toggle_button')).remove();

      $(editor.add_row_button).click((function() {
        $(_.last(this.rows).toggle_button).remove();
      }).bind(editor));
    }, this);
  }).bind(this));
});

// Omit empty properties and "0" values of object properties put into array.
// Stick with that complex solution because "0" is the default value.
JSONEditorView.prototype.getCleanValue = function () { return deepEmpty(this.getValue(), function(O) {
  return !_.isEmpty(O) && !_.every(O, function(I) { return _.isEmpty(omitEmpty(I, true)); })
}); };

// Validate resources and show errors message at header of Schema row
JSONEditorView.prototype.validateResources = function () {
  var rows = this.getEditor('root.resources').rows;

  return Promise.each(rows, (function(R) {
    if(_.isUndefined(R.resourceIsValid))
      return (new Goodtables({method: 'post', report_type: 'grouped'}))
        .run(R.dataSource.data, JSON.stringify(R.dataSource.schema))
        .then(function(E) { R.resourceIsValid = !Boolean(E.errors.length); });
    else
      return new Promise(function(RS, RJ) { RS(true); })
  }).bind(this)).then(function() {
    // Show error in resource section if it is invalid
    rows.forEach(function(R) {
      if(R.resourceIsValid)
        return true;

      R.editors.schema.showValidationErrors([{
        message: 'Resource is invalid',
        path: R.editors.schema.path
      }]);
    })
  });
}

// Proper representation of all form buttons. Avoid changing each newly
// rendered button by rewriting the .getButton()
JSONEditor.defaults.themes.bootstrap3.prototype.getButton = function(text, icon, title) {
  var el = document.createElement('button');

  el.type = 'button';
  el.className += 'btn btn-info btn-sm';
  this.setButtonText(el,text,icon,title);
  return el;
};

module.exports.JSONEditorView = JSONEditorView;
<|endoftext|>


<commit_before>define(function(require, exports, module) {
  "use strict";

  var cc = require("../cc");

  cc.ugen.specs.Integrator = {
    ar: {
      defaults: "in=0,coef=1,mul=1,add=0",
      ctor: function(_in, coef, mul, add) {
        return this.init(C.AUDIO, _in, coef).mad(mul, add);
      }
    },
    kr: {
      defaults: "in=0,coef=1,mul=1,add=0",
      ctor: function(_in, coef, mul, add) {
        return this.init(C.CONTROL, _in, coef).mad(mul, add);
      }
    },
  };

  cc.ugen.specs.Decay = {
    ar: {
      defaults: "in=0,decayTime=1,mul=1,add=0",
      ctor: function(_in, decayTime, mul, add) {
        return this.init(C.AUDIO, _in, decayTime).mad(mul, add);
      }
    },
    kr: {
      defaults: "in=0,decayTime=1,mul=1,add=0",
      ctor: function(_in, decayTime, mul, add) {
        return this.init(C.CONTROL, _in, decayTime).mad(mul, add);
      }
    },
  };

  cc.ugen.specs.Decay2 = {
    ar: {
      defaults: "in=0,attackTime=0.01,decayTime=1,mul=1,add=0",
      ctor: function(_in, attackTime, decayTime, mul, add) {
        return this.init(C.AUDIO, _in, attackTime, decayTime).mad(mul, add);
      }
    },
    kr: {
      defaults: "in=0,attackTime=0.01,decayTime=1,mul=1,add=0",
      ctor: function(_in, attackTime, decayTime, mul, add) {
        return this.init(C.CONTROL, _in, attackTime, decayTime).mad(mul, add);
      }
    },
  };

  module.exports = {};

});
<commit_msg>Fixed<commit_after>define(function(require, exports, module) {
  "use strict";

  var cc = require("../cc");

  cc.ugen.specs.Integrator = {
    ar: {
      defaults: "in=0,coef=1,mul=1,add=0",
      ctor: function(_in, coef, mul, add) {
        return this.init(C.AUDIO, _in, coef).madd(mul, add);
      }
    },
    kr: {
      defaults: "in=0,coef=1,mul=1,add=0",
      ctor: function(_in, coef, mul, add) {
        return this.init(C.CONTROL, _in, coef).madd(mul, add);
      }
    },
  };

  cc.ugen.specs.Decay = {
    ar: {
      defaults: "in=0,decayTime=1,mul=1,add=0",
      ctor: function(_in, decayTime, mul, add) {
        return this.init(C.AUDIO, _in, decayTime).madd(mul, add);
      }
    },
    kr: {
      defaults: "in=0,decayTime=1,mul=1,add=0",
      ctor: function(_in, decayTime, mul, add) {
        return this.init(C.CONTROL, _in, decayTime).madd(mul, add);
      }
    },
  };

  cc.ugen.specs.Decay2 = {
    ar: {
      defaults: "in=0,attackTime=0.01,decayTime=1,mul=1,add=0",
      ctor: function(_in, attackTime, decayTime, mul, add) {
        return this.init(C.AUDIO, _in, attackTime, decayTime).madd(mul, add);
      }
    },
    kr: {
      defaults: "in=0,attackTime=0.01,decayTime=1,mul=1,add=0",
      ctor: function(_in, attackTime, decayTime, mul, add) {
        return this.init(C.CONTROL, _in, attackTime, decayTime).madd(mul, add);
      }
    },
  };

  module.exports = {};

});
<|endoftext|>


<commit_before>import Signal from './Signal';
import Activity from './Activity';
import serviceManager from './serviceManager';
import viewManager from './viewManager';
import socket from './socket';
import defaultViewContent from '../config/defaultViewContent';
import defaultViewTemplates from '../config/defaultViewTemplates';
import viewport from '../views/viewport';

/**
 * Client side entry point for a `soundworks` application.
 *
 * This object host general informations about the user, as well as methods
 * to initialize and start the application.
 *
 * @memberof module:soundworks/client
 * @namespace
 *
 * @example
 * import * as soundworks from 'soundworks/client';
 * import MyExperience from './MyExperience';
 *
 * soundworks.client.init('player');
 * const myExperience = new MyExperience();
 * soundworks.client.start();
 */
const client = {
  /**
   * Unique id of the client, generated and retrieved by the server.
   *
   * @type {Number}
   */
  uuid: null,

  /**
   * The type of the client, this can generally be considered as the role of the
   * client in the application. This value is defined as argument of the
   * [`client.init`]{@link module:soundworks/client.client.init} method and
   * defaults to `'player'`.
   *
   * @type {String}
   */
  type: null,

  /**
   * Configuration informations from the server configuration if any.
   *
   * @type {Object}
   * @see {@link module:soundworks/client.SharedConfig}
   */
  config: null,

  /**
   * Array of optionnal parameters passed through the url
   *
   * @type {Array}
   */
   urlParams: null,

  /**
   * Information about the client platform. The properties are set by the
   * [`platform`]{@link module:soundworks/client.Platform} service.
   *
   * @type {Object}
   * @property {String} os - Operating system.
   * @property {Boolean} isMobile - Indicates whether the client is running on a
   *  mobile platform or not.
   * @property {String} audioFileExt - Audio file extension to use, depending on
   *  the platform.
   *
   * @see {@link module:soundworks/client.Platform}
   */
  platform: {
    os: null,
    isMobile: null,
    audioFileExt: '',
  },

  /**
   * Defines whether the user's device is compatible with the application
   * requirements.
   *
   * @type {Boolean}
   * @see {@link module:soundworks/client.Platform}
   */
  compatible: null,

  /**
   * Index (if any) given by a [`placer`]{@link module:soundworks/client.Placer}
   * or [`checkin`]{@link module:soundworks/client.Checkin} service.
   *
   * @type {Number}
   * @see {@link module:soundworks/client.Checkin}
   * @see {@link module:soundworks/client.Placer}
   */
  index: null,

  /**
   * Ticket label (if any) given by a [`placer`]{@link module:soundworks/client.Placer}
   * or [`checkin`]{@link module:soundworks/client.Checkin} service.
   *
   * @type {String}
   * @see {@link module:soundworks/client.Checkin}
   * @see {@link module:soundworks/client.Placer}
   */
  label: null,

  /**
   * Client coordinates (if any) given by a
   * [`locator`]{@link module:soundworks/client.Locator},
   * [`placer`]{@link module:soundworks/client.Placer} or
   * [`checkin`]{@link module:soundworks/client.Checkin} service.
   * (Format: `[x:Number, y:Number]`.)
   *
   * @type {Array<Number>}
   * @see {@link module:soundworks/client.Checkin}
   * @see {@link module:soundworks/client.Locator}
   * @see {@link module:soundworks/client.Placer}
   */
  coordinates: null,

  /**
   * Socket object that handle communications with the server, if any.
   * This object is automatically created if the experience requires any service
   * having a server-side counterpart.
   *
   * @type {module:soundworks/client.socket}
   * @private
   */
  socket: null,

  /**
   * Initialize the application.
   *
   * @param {String} [clientType='player'] - The type of the client, defines the
   *  socket connection namespace. Should match a client type defined server side.
   * @param {Object} [config={}]
   * @param {Object} [config.appContainer='#container'] - A css selector
   *  matching a DOM element where the views should be inserted.
   * @param {Object} [config.socketIO.url=''] - The url where the socket should
   *  connect _(unstable)_.
   * @param {Object} [config.socketIO.transports=['websocket']] - The transport
   *  used to create the url (overrides default socket.io mecanism) _(unstable)_.
   */
  init(clientType = 'player', config = {}) {
    this.type = clientType;

    // retrieve
    this._parseUrlParams();
    // if socket config given, mix it with defaults
    const socketIO = Object.assign({
      url: '',
      transports: ['websocket']
    }, config.socketIO);

    // mix all other config and override with defined socket config
    this.config = Object.assign({
      appContainer: '#container',
    }, config, { socketIO });

    serviceManager.init();

    this._initViews();
  },

  /**
   * Start the application.
   */
  start() {
    if (socket.required)
      this._initSocket();
    else
      serviceManager.start();
  },

  /**
   * Returns a service configured with the given options.
   * @param {String} id - Identifier of the service.
   * @param {Object} options - Options to configure the service.
   */
  require(id, options) {
    return serviceManager.require(id, options);
  },

  /**
   * Retrieve an array of optionnal parameters from the url excluding the client type
   * and store it in `this.config.urlParameters`.
   * Parameters can be defined in two ways :
   * - as a regular route (ex: `/player/param1/param2`)
   * - as a hash (ex: `/player#param1-param2`)
   * The parameters are send along with the socket connection
   *
   * @see {@link module:soundworks/client.socket}
   * @private
   * @todo - When handshake implemented, define if these informations should be part of it
   */
  _parseUrlParams() {
    // let params = null;
    // handle path name first
    let pathname = window.location.pathname;
    // sanitize
    pathname = pathname
      .replace(/^\//, '') // leading slash
      .replace(new RegExp('^' + this.type + '/?'), '') // clientType
      .replace(/\/$/, '');    // trailing slashe

    if (pathname.length > 0)
      this.urlParams = pathname.split('/');
    // } else {
    //   let hash = window.location.hash
    //   hash = hash.substr(1); // remove leading '#'
    //   params = hash.split('-'); // how to handle from server side config
    // }
    // this.urlParams = params;
  },

  /**
   * Initialize socket connection and perform handshake with the server.
   * @todo - refactor handshake.
   * @private
   */
  _initSocket() {
    this.socket = socket.initialize(this.type, this.config.socketIO);
    // send `urlParams` throught handshake to not polute the socket.io api
    // and eventually be able to modify the transport system
    this.socket.send('handshake', {
      urlParams: this.urlParams,
    });
    // wait for handshake to mark client as `ready`
    this.socket.receive('client:start', (uuid) => {
      // don't handle server restart for now.
      this.uuid = uuid;
      serviceManager.start();

      // this.comm.receive('reconnect', () => console.info('reconnect'));
      // this.comm.receive('disconnect', () => {
      //   console.info('disconnect')
      //   serviceManager.reset(); // can relaunch serviceManager on reconnection.
      // });
      // this.comm.receive('error', (err) => console.error(err));
    });
  },

  /**
   * Initialize view templates for all activities.
   * @private
   */
  _initViews() {
    viewport.init();
    // initialize views with default view content and templates
    this.viewContent = {};
    this.viewTemplates = {};

    const appName = this.config.appName || defaultViewContent.globals.appName;
    const viewContent = Object.assign(defaultViewContent, { globals: { appName } });

    this.setViewContentDefinitions(viewContent);
    this.setViewTemplateDefinitions(defaultViewTemplates);
    this.setAppContainer(this.config.appContainer);
  },

  /**
   * Extend or override application view contents with the given object.
   * @param {Object} defs - Content to be used by activities.
   */
  setViewContentDefinitions(defs) {
    this.viewContent = Object.assign(this.viewContent, defs);
    Activity.setViewContentDefinitions(this.viewContent);
  },

  /**
   * Extend or override application view templates with the given object.
   * @param {Object} defs - Templates to be used by activities.
   */
  setViewTemplateDefinitions(defs) {
    this.viewTemplates = Object.assign(this.viewTemplates, defs);
    Activity.setViewTemplateDefinitions(this.viewTemplates);
  },

  /**
   * Set the DOM elemnt that will be the container for all views.
   * @private
   * @param {String|Element} el - DOM element (or css selector matching
   *  an existing element) to be used as the container of the application.
   */
  setAppContainer(el) {
    const $container = el instanceof Element ? el : document.querySelector(el);
    viewManager.setViewContainer($container);
  },

};

export default client;
<commit_msg>cosmetics<commit_after>import Signal from './Signal';
import Activity from './Activity';
import serviceManager from './serviceManager';
import viewManager from './viewManager';
import socket from './socket';
import defaultViewContent from '../config/defaultViewContent';
import defaultViewTemplates from '../config/defaultViewTemplates';
import viewport from '../views/viewport';

/**
 * Client side entry point for a `soundworks` application.
 *
 * This object host general informations about the user, as well as methods
 * to initialize and start the application.
 *
 * @memberof module:soundworks/client
 * @namespace
 *
 * @example
 * import * as soundworks from 'soundworks/client';
 * import MyExperience from './MyExperience';
 *
 * soundworks.client.init('player');
 * const myExperience = new MyExperience();
 * soundworks.client.start();
 */
const client = {
  /**
   * Unique id of the client, generated and retrieved by the server.
   *
   * @type {Number}
   */
  uuid: null,

  /**
   * The type of the client, this can generally be considered as the role of the
   * client in the application. This value is defined as argument of the
   * [`client.init`]{@link module:soundworks/client.client.init} method and
   * defaults to `'player'`.
   *
   * @type {String}
   */
  type: null,

  /**
   * Configuration informations from the server configuration if any.
   *
   * @type {Object}
   * @see {@link module:soundworks/client.SharedConfig}
   */
  config: null,

  /**
   * Array of optionnal parameters passed through the url
   *
   * @type {Array}
   */
   urlParams: null,

  /**
   * Information about the client platform. The properties are set by the
   * [`platform`]{@link module:soundworks/client.Platform} service.
   *
   * @type {Object}
   * @property {String} os - Operating system.
   * @property {Boolean} isMobile - Indicates whether the client is running on a
   *  mobile platform or not.
   * @property {String} audioFileExt - Audio file extension to use, depending on
   *  the platform.
   *
   * @see {@link module:soundworks/client.Platform}
   */
  platform: {
    os: null,
    isMobile: null,
    audioFileExt: '',
  },

  /**
   * Defines whether the user's device is compatible with the application
   * requirements.
   *
   * @type {Boolean}
   * @see {@link module:soundworks/client.Platform}
   */
  compatible: null,

  /**
   * Index (if any) given by a [`placer`]{@link module:soundworks/client.Placer}
   * or [`checkin`]{@link module:soundworks/client.Checkin} service.
   *
   * @type {Number}
   * @see {@link module:soundworks/client.Checkin}
   * @see {@link module:soundworks/client.Placer}
   */
  index: null,

  /**
   * Ticket label (if any) given by a [`placer`]{@link module:soundworks/client.Placer}
   * or [`checkin`]{@link module:soundworks/client.Checkin} service.
   *
   * @type {String}
   * @see {@link module:soundworks/client.Checkin}
   * @see {@link module:soundworks/client.Placer}
   */
  label: null,

  /**
   * Client coordinates (if any) given by a
   * [`locator`]{@link module:soundworks/client.Locator},
   * [`placer`]{@link module:soundworks/client.Placer} or
   * [`checkin`]{@link module:soundworks/client.Checkin} service.
   * (Format: `[x:Number, y:Number]`.)
   *
   * @type {Array<Number>}
   * @see {@link module:soundworks/client.Checkin}
   * @see {@link module:soundworks/client.Locator}
   * @see {@link module:soundworks/client.Placer}
   */
  coordinates: null,

  /**
   * Socket object that handle communications with the server, if any.
   * This object is automatically created if the experience requires any service
   * having a server-side counterpart.
   *
   * @type {module:soundworks/client.socket}
   * @private
   */
  socket: null,

  /**
   * Initialize the application.
   *
   * @param {String} [clientType='player'] - The type of the client, defines the
   *  socket connection namespace. Should match a client type defined server side.
   * @param {Object} [config={}]
   * @param {Object} [config.appContainer='#container'] - A css selector
   *  matching a DOM element where the views should be inserted.
   * @param {Object} [config.socketIO.url=''] - The url where the socket should
   *  connect _(unstable)_.
   * @param {Object} [config.socketIO.transports=['websocket']] - The transport
   *  used to create the url (overrides default socket.io mecanism) _(unstable)_.
   */
  init(clientType = 'player', config = {}) {
    this.type = clientType;

    // retrieve
    this._parseUrlParams();
    // if socket config given, mix it with defaults
    const socketIO = Object.assign({
      url: '',
      transports: ['websocket']
    }, config.socketIO);

    // mix all other config and override with defined socket config
    this.config = Object.assign({
      appContainer: '#container',
    }, config, { socketIO });

    serviceManager.init();

    this._initViews();
  },

  /**
   * Start the application.
   */
  start() {
    if (socket.required)
      this._initSocket();
    else
      serviceManager.start();
  },

  /**
   * Returns a service configured with the given options.
   * @param {String} id - Identifier of the service.
   * @param {Object} options - Options to configure the service.
   */
  require(id, options) {
    return serviceManager.require(id, options);
  },

  /**
   * Retrieve an array of optionnal parameters from the url excluding the client type
   * and store it in `this.config.urlParameters`.
   * Parameters can be defined in two ways :
   * - as a regular route (ex: `/player/param1/param2`)
   * - as a hash (ex: `/player#param1-param2`)
   * The parameters are send along with the socket connection
   *
   * @see {@link module:soundworks/client.socket}
   * @private
   * @todo - When handshake implemented, define if these informations should be part of it
   */
  _parseUrlParams() {
    // let params = null;
    // handle path name first
    let pathname = window.location.pathname;
    // sanitize
    pathname = pathname
      .replace(/^\//, '') // leading slash
      .replace(new RegExp('^' + this.type + '/?'), '') // clientType
      .replace(/\/$/, ''); // trailing slashe

    if (pathname.length > 0)
      this.urlParams = pathname.split('/');
    // } else {
    //   let hash = window.location.hash
    //   hash = hash.substr(1); // remove leading '#'
    //   params = hash.split('-'); // how to handle from server side config
    // }
    // this.urlParams = params;
  },

  /**
   * Initialize socket connection and perform handshake with the server.
   * @todo - refactor handshake.
   * @private
   */
  _initSocket() {
    this.socket = socket.initialize(this.type, this.config.socketIO);
    // send `urlParams` throught handshake to not polute the socket.io api
    // and eventually be able to modify the transport system
    this.socket.send('handshake', { urlParams: this.urlParams });
    // wait for handshake to mark client as `ready`
    this.socket.receive('client:start', (uuid) => {
      // don't handle server restart for now.
      this.uuid = uuid;
      serviceManager.start();

      // this.comm.receive('reconnect', () => console.info('reconnect'));
      // this.comm.receive('disconnect', () => {
      //   console.info('disconnect')
      //   serviceManager.reset(); // can relaunch serviceManager on reconnection.
      // });
      // this.comm.receive('error', (err) => console.error(err));
    });
  },

  /**
   * Initialize view templates for all activities.
   * @private
   */
  _initViews() {
    viewport.init();
    // initialize views with default view content and templates
    this.viewContent = {};
    this.viewTemplates = {};

    const appName = this.config.appName || defaultViewContent.globals.appName;
    const viewContent = Object.assign(defaultViewContent, { globals: { appName } });

    this.setViewContentDefinitions(viewContent);
    this.setViewTemplateDefinitions(defaultViewTemplates);
    this.setAppContainer(this.config.appContainer);
  },

  /**
   * Extend or override application view contents with the given object.
   * @param {Object} defs - Content to be used by activities.
   */
  setViewContentDefinitions(defs) {
    this.viewContent = Object.assign(this.viewContent, defs);
    Activity.setViewContentDefinitions(this.viewContent);
  },

  /**
   * Extend or override application view templates with the given object.
   * @param {Object} defs - Templates to be used by activities.
   */
  setViewTemplateDefinitions(defs) {
    this.viewTemplates = Object.assign(this.viewTemplates, defs);
    Activity.setViewTemplateDefinitions(this.viewTemplates);
  },

  /**
   * Set the DOM elemnt that will be the container for all views.
   * @private
   * @param {String|Element} el - DOM element (or css selector matching
   *  an existing element) to be used as the container of the application.
   */
  setAppContainer(el) {
    const $container = el instanceof Element ? el : document.querySelector(el);
    viewManager.setViewContainer($container);
  },

};

export default client;
<|endoftext|>


<commit_before>import Horizon from '@horizon/client';
import { API_BASE, COLLECTIONS } from '../constants';

export const hz = Horizon({ authType: 'token', host: API_BASE, secure: true });
export const hzRooms = hz(COLLECTIONS.ROOMS);
<commit_msg>add user collection<commit_after>import Horizon from '@horizon/client';
import { API_BASE, COLLECTIONS } from '../constants';

export const hz = Horizon({ authType: 'token', host: API_BASE, secure: true });
export const hzRooms = hz(COLLECTIONS.ROOMS);
export const hzUsers = hz(COLLECTIONS.USERS);
<|endoftext|>


<commit_before>import React from 'react';

export default class StorybookControls extends React.Component {
  getKindNames() {
    const { storyStore, selectedKind } = this.props;
    if (!storyStore) {
      return [];
    }

    const kinds = storyStore.map(({ kind }) => kind);
    const selectedKindIndex = kinds.indexOf(selectedKind);

    // add the selected kind to the top of the list
    kinds.splice(selectedKindIndex, 1);
    kinds.unshift(selectedKind);

    return kinds;
  }

  getStories(kind) {
    const { storyStore } = this.props;
    const storiesInfo = storyStore.find(item => item.kind === kind);

    if (!storiesInfo) {
      return [];
    }
    return storiesInfo.stories;
  }

  fireOnKind(kind) {
    const { onKind } = this.props;
    if (onKind) onKind(kind);
  }

  fireOnStory(story) {
    const { onStory } = this.props;
    if (onStory) onStory(story);
  }

  renderStory(story) {
    const { selectedStory } = this.props;
    const storyStyle = {
      fontSize: 13,
      padding: '8px 0px 8px 10px',
      cursor: 'pointer',
    };

    if (story === selectedStory) {
      storyStyle.fontWeight = 'bold';
    }
    return (

        {story}

    );
  }

  renderKind(kind) {
    const kindStyle = {
      fontSize: 15,
      padding: '10px 0px',
      cursor: 'pointer',
      borderBottom: '1px solid #EEE',
    };

    const { selectedKind } = this.props;
    if (kind === selectedKind) {
      const stories = this.getStories(selectedKind);
      kindStyle.fontWeight = 'bold';
      return (

            {kind}

            {stories.map(this.renderStory.bind(this))}

      );
    }

    return (

        {kind}

    );
  }

  render() {
    const kindNames = this.getKindNames();
    const mainStyle = {
      fontFamily: `
        -apple-system, ".SFNSText-Regular", "San Francisco", "Roboto",
        "Segoe UI", "Helvetica Neue", "Lucida Grande", sans-serif
      `,
      padding: '20px 10px 10px 10px',
      color: '#444',
    };

    const h1Style = {
      textTransform: 'uppercase',
      letterSpacing: '3.5px',
      fontSize: '12px',
      fontWeight: 'bolder',
      color: '#828282',
      border: '1px solid #C1C1C1',
      textAlign: 'center',
      borderRadius: '2px',
      padding: '5px',
      margin: '0 0 20px 0',
      cursor: 'default',
    };

    return (

React Storybook

          {kindNames.map(this.renderKind.bind(this))}

    );
  }
}

StorybookControls.propTypes = {
  storyStore: React.PropTypes.array,
  selectedKind: React.PropTypes.string,
  selectedStory: React.PropTypes.string,
  onKind: React.PropTypes.func,
  onStory: React.PropTypes.func,
};
<commit_msg>Remove navigation order hijacking. Earlier, we move the selected kind and stories to the top. It's kind a weird and discussed on #21<commit_after>import React from 'react';

export default class StorybookControls extends React.Component {
  getKindNames() {
    const { storyStore } = this.props;
    if (!storyStore) {
      return [];
    }

    return storyStore.map(({ kind }) => kind);
  }

  getStories(kind) {
    const { storyStore } = this.props;
    const storiesInfo = storyStore.find(item => item.kind === kind);

    if (!storiesInfo) {
      return [];
    }
    return storiesInfo.stories;
  }

  fireOnKind(kind) {
    const { onKind } = this.props;
    if (onKind) onKind(kind);
  }

  fireOnStory(story) {
    const { onStory } = this.props;
    if (onStory) onStory(story);
  }

  renderStory(story) {
    const { selectedStory } = this.props;
    const storyStyle = {
      fontSize: 13,
      padding: '8px 0px 8px 10px',
      cursor: 'pointer',
    };

    if (story === selectedStory) {
      storyStyle.fontWeight = 'bold';
    }
    return (

        {story}

    );
  }

  renderKind(kind) {
    const kindStyle = {
      fontSize: 15,
      padding: '10px 0px',
      cursor: 'pointer',
      borderBottom: '1px solid #EEE',
    };

    const { selectedKind } = this.props;
    if (kind === selectedKind) {
      const stories = this.getStories(selectedKind);
      kindStyle.fontWeight = 'bold';
      return (

            {kind}

            {stories.map(this.renderStory.bind(this))}

      );
    }

    return (

        {kind}

    );
  }

  render() {
    const kindNames = this.getKindNames();
    const mainStyle = {
      fontFamily: `
        -apple-system, ".SFNSText-Regular", "San Francisco", "Roboto",
        "Segoe UI", "Helvetica Neue", "Lucida Grande", sans-serif
      `,
      padding: '20px 10px 10px 10px',
      color: '#444',
    };

    const h1Style = {
      textTransform: 'uppercase',
      letterSpacing: '3.5px',
      fontSize: '12px',
      fontWeight: 'bolder',
      color: '#828282',
      border: '1px solid #C1C1C1',
      textAlign: 'center',
      borderRadius: '2px',
      padding: '5px',
      margin: '0 0 20px 0',
      cursor: 'default',
    };

    return (

React Storybook

          {kindNames.map(this.renderKind.bind(this))}

    );
  }
}

StorybookControls.propTypes = {
  storyStore: React.PropTypes.array,
  selectedKind: React.PropTypes.string,
  selectedStory: React.PropTypes.string,
  onKind: React.PropTypes.func,
  onStory: React.PropTypes.func,
};
<|endoftext|>