<commit_before>import m from 'mithril';
import _ from 'underscore';
import h from '../h';
import ownerMessageContent from './owner-message-content';
import modalBox from './modal-box';

const projectUserCard = {
    controller(args) {
        return {displayModal: h.toggleProp(false, true)};
    },
    view(ctrl, args) {
        const project = args.project;
        const contactModalC = [ownerMessageContent, args.userDetails];
        const userDetail = args.userDetails();

        return m('#user-card', !userDetail ? 'carregando...' : m('.u-marginbottom-30.u-text-center-small-only', [
                (ctrl.displayModal() ? m.component(modalBox, {
                    displayModal: ctrl.displayModal,
                    content: contactModalC
                }) : ''),
                m('.w-row', [
                    m('.w-col.w-col-4', [
                        m('img.thumb.u-marginbottom-30.u-round[width="100"][itemprop="image"][src="' + userDetail.profile_img_thumbnail + '"]')
                    ]),
                    m('.w-col.w-col-8', [
                        m('.fontsize-small.link-hidden.fontweight-semibold.u-marginbottom-10.lineheight-tight[itemprop="name"]', [
                            m('a.link-hidden[href="/users/' + userDetail.id + '"]',{onclick: h.analytics.event({cat: 'project_view',act: 'project_creator_link',lbl: userDetail.id,project: project()})}, userDetail.name)
                        ]),
                        m('.fontsize-smallest', [
                            h.pluralize(userDetail.total_published_projects, ' criado', ' criados'),
                            m.trust('  |  '),
                            h.pluralize(userDetail.total_contributed_projects, ' apoiado', ' apoiados')
                        ]),
                        m('ul.w-hidden-tiny.w-hidden-small.w-list-unstyled.fontsize-smaller.fontweight-semibold.u-margintop-20.u-marginbottom-20', [
                            (!_.isEmpty(userDetail.facebook_link) ? m('li', [
                                m('a.link-hidden[itemprop="url"][href="' + userDetail.facebook_link + '"][target="_blank"]',{onclick: h.analytics.event({cat: 'project_view',act: 'project_creator_fb',lbl: userDetail.facebook_link,project: project()})}, 'Perfil no Facebook')
                            ]) : ''), (!_.isEmpty(userDetail.twitter_username) ? m('li', [
                                m('a.link-hidden[itemprop="url"][href="https://twitter.com/' + userDetail.twitter_username + '"][target="_blank"]',{onclick: h.analytics.event({cat: 'project_view',act: 'project_creator_twitter',lbl: userDetail.twitter_username,project: project()})}, 'Perfil no Twitter')
                            ]) : ''),
                            _.map(userDetail.links, (link) => {
                                var parsedLink = h.parseUrl(link);

                                return (!_.isEmpty(parsedLink.hostname) ? m('li', [
                                    m('a.link-hidden[itemprop="url"][href="' + link + '"][target="_blank"]',{onclick: h.analytics.event({cat: 'project_view',act: 'project_creator_otherlinks',lbl: link,project: project()})}, parsedLink.hostname)
                                ]) : '');
                            })
                        ]),
                        (!_.isEmpty(userDetail) ? [m('a.w-button.btn.btn-terciary.btn-small.btn-inline[href=\'javascript:void(0);\']',{onclick: h.analytics.event({cat: 'project_view',act: 'project_creator_sendmsg',lbl: userDetail.id,project: project()}, ctrl.displayModal.toggle)}, 'Enviar mensagem')] : ''),
                        args.project().is_admin_role ?
                        m('p', userDetail.email) : ''
                    ]),
                ]),
            ])
        );
    }
};

export default projectUserCard;
<commit_msg>use mithril route for user link and fix undefined get<commit_after>import m from 'mithril';
import _ from 'underscore';
import h from '../h';
import ownerMessageContent from './owner-message-content';
import modalBox from './modal-box';

const projectUserCard = {
    controller(args) {
        return {displayModal: h.toggleProp(false, true)};
    },
    view(ctrl, args) {
        const project = args.project;
        const contactModalC = [ownerMessageContent, args.userDetails];
        const userDetail = args.userDetails();

        return m('#user-card', _.isEmpty(userDetail) ? 'carregando...' : m('.u-marginbottom-30.u-text-center-small-only', [
                (ctrl.displayModal() ? m.component(modalBox, {
                    displayModal: ctrl.displayModal,
                    content: contactModalC
                }) : ''),
                m('.w-row', [
                    m('.w-col.w-col-4', [
                        m('img.thumb.u-marginbottom-30.u-round[width="100"][itemprop="image"][src="' + userDetail.profile_img_thumbnail + '"]')
                    ]),
                    m('.w-col.w-col-8', [
                        m('.fontsize-small.link-hidden.fontweight-semibold.u-marginbottom-10.lineheight-tight[itemprop="name"]', [
                            m('a.link-hidden[href="/users/' + userDetail.id + '"]',{config: m.route, onclick: () => {
                              m.route("/users/" + userDetail.id, {user_id: userDetail.id} );
                              h.analytics.event({cat: 'project_view',act: 'project_creator_link',lbl: userDetail.id,project: project()})
                            }}, userDetail.name)
                        ]),
                        m('.fontsize-smallest', [
                            h.pluralize(userDetail.total_published_projects, ' criado', ' criados'),
                            m.trust('  |  '),
                            h.pluralize(userDetail.total_contributed_projects, ' apoiado', ' apoiados')
                        ]),
                        m('ul.w-hidden-tiny.w-hidden-small.w-list-unstyled.fontsize-smaller.fontweight-semibold.u-margintop-20.u-marginbottom-20', [
                            (!_.isEmpty(userDetail.facebook_link) ? m('li', [
                                m('a.link-hidden[itemprop="url"][href="' + userDetail.facebook_link + '"][target="_blank"]',{onclick: h.analytics.event({cat: 'project_view',act: 'project_creator_fb',lbl: userDetail.facebook_link,project: project()})}, 'Perfil no Facebook')
                            ]) : ''), (!_.isEmpty(userDetail.twitter_username) ? m('li', [
                                m('a.link-hidden[itemprop="url"][href="https://twitter.com/' + userDetail.twitter_username + '"][target="_blank"]',{onclick: h.analytics.event({cat: 'project_view',act: 'project_creator_twitter',lbl: userDetail.twitter_username,project: project()})}, 'Perfil no Twitter')
                            ]) : ''),
                            _.map(userDetail.links, (link) => {
                                var parsedLink = h.parseUrl(link);

                                return (!_.isEmpty(parsedLink.hostname) ? m('li', [
                                    m('a.link-hidden[itemprop="url"][href="' + link + '"][target="_blank"]',{onclick: h.analytics.event({cat: 'project_view',act: 'project_creator_otherlinks',lbl: link,project: project()})}, parsedLink.hostname)
                                ]) : '');
                            })
                        ]),
                        (!_.isEmpty(userDetail) ? [m('a.w-button.btn.btn-terciary.btn-small.btn-inline[href=\'javascript:void(0);\']',{onclick: h.analytics.event({cat: 'project_view',act: 'project_creator_sendmsg',lbl: userDetail.id,project: project()}, ctrl.displayModal.toggle)}, 'Enviar mensagem')] : ''),
                        args.project().is_admin_role ?
                        m('p', userDetail.email) : ''
                    ]),
                ]),
            ])
        );
    }
};

export default projectUserCard;
<|endoftext|>


<commit_before>define([
    'app',
    'utils/chartHelpers',
    'utils/duration',
    'utils/escapeHtml',
    'directives/radialProgressBar',
    'directives/timeSince',
    'directives/duration',
    'filters/escape',
    'filters/wordwrap',
    'modules/pagination'], function(app, chartHelpers, duration, escapeHtml) {
  app.controller('buildDetailsCtrl', ['$scope', 'initialData', '$window', '$timeout', '$http', '$routeParams', 'stream', 'pagination', 'flash', function($scope, initialData, $window, $timeout, $http, $routeParams, Stream, Pagination, flash) {
    'use strict';

    var stream, logSources = {},
        entrypoint = '/api/0/builds/' + $routeParams.build_id + '/',
        chart_options = {
          tooltipFormatter: function(item) {
            var content = ''

            content += '
';
            content += escapeHtml(item.name);
            content += '
';
            content += escapeHtml(item.target);
            if (item.author) {
              content += ' — ' + item.author.name;
            }
            content += ''
            content += '
';
            if (item.status.id == 'finished') {
              content += '
Build ' + item.result.name;
              if (item.duration) {
                content += ' in ' + duration(item.duration);
              }
              content += '
';
            } else {
              content += '
' + item.status.name + '
';
            }

            return content;
          }
        };

    function getLogSourceEntrypoint(logSource) {
      return '/api/0/builds/' + $scope.build.id + '/logs/' + logSource.id + '/';
    }

    function getTestStatus() {
      if ($scope.build.status.id == "finished") {
        if ($scope.testGroups.length === 0) {
          return "no-results";
        } else {
          return "has-results";
        }
      }
      return "pending";
    }

    function updateBuild(data){
      $scope.$apply(function() {
        $scope.build = data;
      });
    }

    function updateBuildLog(data) {
      // Angular isn't intelligent enough to optimize this.
      var el = $('#log-' + data.source.id),
          item, source_id = data.source.id;

      if (el.length === 0) {
        // logsource isnt available in viewpane
        return;
      }

      if (!logSources[source_id]) {
        logSources[source_id] = {
          text: '',
          nextOffset: null
        };
      }

      item = logSources[source_id];
      if (data.offset < item.nextOffset) {
        return;
      }

      // TODO(dcramer): we should only remove/append rows, rather
      // than rewriting the entire block
      item.text = (item.text + data.text).substr(-10000);
      item.nextOffset = data.offset + data.size;

      el.empty();
      $.each(item.text.split('\n'), function(_, line){
        el.append($('
' + line + '
'));
      });
      if (el.is(':visible')) {
        $timeout(function(){
          el.scrollTop(el.height(), 1000);
        });
      }
    }

    function updateTestGroup(data) {
      $scope.$apply(function() {
        var updated = false,
            item_id = data.id,
            attr, result, item;

        // TODO(dcramer); we need to refactor all of this logic as its repeated in nealry
        // every stream
        if ($scope.testGroups.length > 0) {
          result = $.grep($scope.testGroups, function(e){ return e.id == item_id; });
          if (result.length > 0) {
            item = result[0];
            for (attr in data) {
              // ignore dateModified as we're updating this frequently and it causes
              // the dirty checking behavior in angular to respond poorly
              if (item[attr] != data[attr] && attr != 'dateModified') {
                updated = true;
                item[attr] = data[attr];
              }
              if (updated) {
                item.dateModified = data.dateModified;
              }
            }
          }
        }
        if (!updated) {
          $scope.testGroups.unshift(data);
        }

        if (data.result.id == 'failed') {
          if ($scope.testFailures.length > 0) {
            result = $.grep($scope.testFailures, function(e){ return e.id == item_id; });
            if (result.length > 0) {
              item = result[0];
              for (attr in data) {
                // ignore dateModified as we're updating this frequently and it causes
                // the dirty checking behavior in angular to respond poorly
                if (item[attr] != data[attr] && attr != 'dateModified') {
                  updated = true;
                  item[attr] = data[attr];
                }
                if (updated) {
                  item.dateModified = data.dateModified;
                }
              }
            }
          }
          if (!updated) {
            $scope.testFailures.unshift(data);
          }
        }
      });
    }

    $scope.retryBuild = function() {
      $http.post('/api/0/builds/' + $scope.build.id + '/retry/')
        .success(function(data){
          $window.location.href = data.build.link;
        })
        .error(function(){
          flash('error', 'There was an error while retrying this build.');
        });
    };

    $scope.build = initialData.data.build;
    $scope.logSources = initialData.data.logs;
    $scope.phases = initialData.data.phase;
    $scope.testFailures = initialData.data.testFailures;
    $scope.testGroups = initialData.data.testGroups;
    $scope.testStatus = getTestStatus();
    $scope.previousRuns = initialData.data.previousRuns
    $scope.chartData = chartHelpers.getChartData($scope.previousRuns, $scope.build, chart_options);

    $scope.$watch("build.status", function(status) {
      $scope.testStatus = getTestStatus();
    });
    $scope.$watch("tests", function(status) {
      $scope.testStatus = getTestStatus();
    });

    $.each($scope.logSources, function(_, logSource){
      $http.get(getLogSourceEntrypoint(logSource))
        .success(function(data){
          $.each(data.chunks, function(_, chunk){
            updateBuildLog(chunk);
          });
        });
    });

    // TODO: we need to support multiple soruces, a real-time stream, and real-time source changes
    stream = Stream($scope, entrypoint);
    stream.subscribe('build.update', updateBuild);
    stream.subscribe('buildlog.update', updateBuildLog);
    stream.subscribe('testgroup.update', updateTestGroup);
  }]);
});
<commit_msg>Correctly apply scrollheight based on total contents<commit_after>define([
    'app',
    'utils/chartHelpers',
    'utils/duration',
    'utils/escapeHtml',
    'directives/radialProgressBar',
    'directives/timeSince',
    'directives/duration',
    'filters/escape',
    'filters/wordwrap',
    'modules/pagination'], function(app, chartHelpers, duration, escapeHtml) {
  app.controller('buildDetailsCtrl', ['$scope', 'initialData', '$window', '$timeout', '$http', '$routeParams', 'stream', 'pagination', 'flash', function($scope, initialData, $window, $timeout, $http, $routeParams, Stream, Pagination, flash) {
    'use strict';

    var stream, logSources = {},
        entrypoint = '/api/0/builds/' + $routeParams.build_id + '/',
        chart_options = {
          tooltipFormatter: function(item) {
            var content = ''

            content += '
';
            content += escapeHtml(item.name);
            content += '
';
            content += escapeHtml(item.target);
            if (item.author) {
              content += ' — ' + item.author.name;
            }
            content += ''
            content += '
';
            if (item.status.id == 'finished') {
              content += '
Build ' + item.result.name;
              if (item.duration) {
                content += ' in ' + duration(item.duration);
              }
              content += '
';
            } else {
              content += '
' + item.status.name + '
';
            }

            return content;
          }
        };

    function getLogSourceEntrypoint(logSource) {
      return '/api/0/builds/' + $scope.build.id + '/logs/' + logSource.id + '/';
    }

    function getTestStatus() {
      if ($scope.build.status.id == "finished") {
        if ($scope.testGroups.length === 0) {
          return "no-results";
        } else {
          return "has-results";
        }
      }
      return "pending";
    }

    function updateBuild(data){
      $scope.$apply(function() {
        $scope.build = data;
      });
    }

    function updateBuildLog(data) {
      // Angular isn't intelligent enough to optimize this.
      var el = $('#log-' + data.source.id),
          item, source_id = data.source.id;

      if (el.length === 0) {
        // logsource isnt available in viewpane
        return;
      } else {
        el = el[0];
      }

      if (!logSources[source_id]) {
        logSources[source_id] = {
          text: '',
          nextOffset: null
        };
      }

      item = logSources[source_id];
      if (data.offset < item.nextOffset) {
        return;
      }

      // TODO(dcramer): we should only remove/append rows, rather
      // than rewriting the entire block
      item.text = (item.text + data.text).substr(-10000);
      item.nextOffset = data.offset + data.size;

      el.empty();
      $.each(item.text.split('\n'), function(_, line){
        el.append($('
' + line + '
'));
      });
      el.scrollTop = Math.max(el.scrollHeight, el.clientHeight) - el.clientHeight;
    }

    function updateTestGroup(data) {
      $scope.$apply(function() {
        var updated = false,
            item_id = data.id,
            attr, result, item;

        // TODO(dcramer); we need to refactor all of this logic as its repeated in nealry
        // every stream
        if ($scope.testGroups.length > 0) {
          result = $.grep($scope.testGroups, function(e){ return e.id == item_id; });
          if (result.length > 0) {
            item = result[0];
            for (attr in data) {
              // ignore dateModified as we're updating this frequently and it causes
              // the dirty checking behavior in angular to respond poorly
              if (item[attr] != data[attr] && attr != 'dateModified') {
                updated = true;
                item[attr] = data[attr];
              }
              if (updated) {
                item.dateModified = data.dateModified;
              }
            }
          }
        }
        if (!updated) {
          $scope.testGroups.unshift(data);
        }

        if (data.result.id == 'failed') {
          if ($scope.testFailures.length > 0) {
            result = $.grep($scope.testFailures, function(e){ return e.id == item_id; });
            if (result.length > 0) {
              item = result[0];
              for (attr in data) {
                // ignore dateModified as we're updating this frequently and it causes
                // the dirty checking behavior in angular to respond poorly
                if (item[attr] != data[attr] && attr != 'dateModified') {
                  updated = true;
                  item[attr] = data[attr];
                }
                if (updated) {
                  item.dateModified = data.dateModified;
                }
              }
            }
          }
          if (!updated) {
            $scope.testFailures.unshift(data);
          }
        }
      });
    }

    $scope.retryBuild = function() {
      $http.post('/api/0/builds/' + $scope.build.id + '/retry/')
        .success(function(data){
          $window.location.href = data.build.link;
        })
        .error(function(){
          flash('error', 'There was an error while retrying this build.');
        });
    };

    $scope.build = initialData.data.build;
    $scope.logSources = initialData.data.logs;
    $scope.phases = initialData.data.phase;
    $scope.testFailures = initialData.data.testFailures;
    $scope.testGroups = initialData.data.testGroups;
    $scope.testStatus = getTestStatus();
    $scope.previousRuns = initialData.data.previousRuns
    $scope.chartData = chartHelpers.getChartData($scope.previousRuns, $scope.build, chart_options);

    $scope.$watch("build.status", function(status) {
      $scope.testStatus = getTestStatus();
    });
    $scope.$watch("tests", function(status) {
      $scope.testStatus = getTestStatus();
    });

    $.each($scope.logSources, function(_, logSource){
      $http.get(getLogSourceEntrypoint(logSource))
        .success(function(data){
          $.each(data.chunks, function(_, chunk){
            updateBuildLog(chunk);
          });
        });
    });

    // TODO: we need to support multiple soruces, a real-time stream, and real-time source changes
    stream = Stream($scope, entrypoint);
    stream.subscribe('build.update', updateBuild);
    stream.subscribe('buildlog.update', updateBuildLog);
    stream.subscribe('testgroup.update', updateTestGroup);
  }]);
});
<|endoftext|>


<commit_before>import resolveFrom from 'resolve-from'
import debug from '../../debug'
import getUserConfig from '../../util/getUserConfig'
import getProjectDefaults from '../../util/getProjectDefaults'
import createProvisionalUser from '../../actions/user/createProvisionalUser'
import createProject from '../../actions/project/createProject'
import login from '../../actions/login/login'
import promptForDatasetName from './promptForDatasetName'
import gatherInput from './gatherInput'
import bootstrapTemplate from './bootstrapTemplate'

export default async function initSanity(args, context) {
  const {output, prompt, workDir, apiClient, yarn, chalk} = context
  output.print('This utility walks you through creating a Sanity installation.')
  output.print('Press ^C at any time to quit.\n')

  // If the user isn't already authenticated, make it so
  const userConfig = getUserConfig()
  const hasToken = userConfig.get('authToken')
  const authType = userConfig.get('authType')
  let isProvisional = authType === 'provisional'
  debug(hasToken ? 'User already has a token' : 'User has no token')

  if (hasToken && isProvisional) {
    output.print("We found some temporary auth credentials in your Sanity config - we're gonna use")
    output.print('those, but make sure to claim your account before it expires!\n')
  } else if (hasToken) {
    output.print('Looks like you already have a Sanity-account. Sweet!\n')
  }

  if (!hasToken) {
    const user = await getOrCreateUser()
    isProvisional = user.isProvisional
  }

  // We're authenticated, now lets create a project
  debug('Prompting user to select or create a project')
  const {projectId, displayName} = await getOrCreateProject()
  const sluggedName = displayName.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9]/g, '')
  debug(`Project with name ${displayName} selected`)

  // Now let's pick or create a dataset
  debug('Prompting user to select or create a dataset')
  const {datasetName} = await getOrCreateDataset({projectId, displayName})
  debug(`Dataset with name ${datasetName} selected`)

  // Gather project defaults based on environment
  const defaults = await getProjectDefaults(workDir, {isPlugin: false})

  // Prompt the user for required information
  const answers = await gatherInput(prompt, defaults, {workDir, sluggedName})

  // Ensure we are using the output path provided by user
  const outputPath = answers.outputPath || workDir

  // Build a full set of resolved options
  const initOptions = {
    template: 'moviedb',
    outputDir: outputPath,
    name: sluggedName,
    displayName: displayName,
    dataset: datasetName,
    projectId: projectId,
    provisionalToken: isProvisional && getUserConfig().get('authToken'),
    ...answers
  }

  // Bootstrap Sanity, creating required project files, manifests etc
  const template = await bootstrapTemplate(initOptions, context)

  // Now for the slow part... installing dependencies
  try {
    await yarn(['install'], {...output, rootDir: outputPath})
  } catch (err) {
    throw err
  }

  // Make sure we have the required configs
  const coreCommands = require(resolveFrom(outputPath, '@sanity/core')).commands
  const configCheckCmd = coreCommands.find(cmd => cmd.name === 'configcheck')
  await configCheckCmd.action({extOptions: {quiet: true}}, Object.assign({}, context, {
    workDir: outputPath
  }))

  // Check if we're currently in the output path, so we can give a better start message
  if (outputPath === process.cwd()) {
    output.print(`\n${chalk.green('Success!')} You can now run "${chalk.cyan('sanity start')}"`)
  } else {
    output.print(`\n${chalk.green('Success!')} You can now change to directory "${chalk.cyan(outputPath)}" and run "${chalk.cyan('sanity start')}"`)
  }

  // See if the template has a success message handler and print it
  const successMessage = template.getSuccessMessage ? template.getSuccessMessage(initOptions, context) : ''
  if (successMessage) {
    output.print(`\n${successMessage}`)
  }

  // Create a provisional user and store the token
  async function getOrCreateUser() {
    output.print("We can't find any auth credentials in your Sanity config - looks like you")
    output.print("haven't used Sanity on this system before?\n")
    output.print("If you're looking to try out Sanity and haven't registered we can just set up")
    output.print('a temp account for testing. You can always claim it later.\n')

    // Provide login options
    const authMethod = await prompt.single({
      message: 'Create temporary account?',
      type: 'list',
      choices: [
        {value: 'provisional', name: 'A temp account sounds great'},
        {value: 'login', name: 'No, I already have an account'},
        {value: 'arrow', name: 'I took an arrow to the knee'}
      ]
    })

    if (authMethod === 'arrow') {
      output.print("Cool story about the arrow, guess you're in a hurry. Here's a temp account.")
    }

    if (authMethod === 'arrow' || authMethod === 'provisional') {
      try {
        const {token} = await createProvisionalUser(apiClient)
        getUserConfig().set({
          authToken: token,
          authType: 'provisional'
        })

        if (!token) {
          throw new Error('No token received from server')
        }
      } catch (err) {
        const userError = new Error('Failed to create provisional user')
        userError.details = err
        throw userError
      }

      output.print("Good stuff, you now have a token for a temporary account. You'll also need")
      output.print('a project to keep your data sets and collaborators safe and snug')

      return Promise.resolve({isProvisional: true})
    }

    // Provide login options (`sanity login`)
    await login(args, context)

    output.print("Good stuff, you're now authenticated. You'll need a project to keep your")
    output.print('data sets and collaborators safe and snug.')
    return Promise.resolve({isProvisional: false})
  }

  async function getOrCreateProject() {
    const projects = await apiClient({requireProject: false}).projects.list()

    if (projects.length === 0) {
      debug('No projects found for user, prompting for name')
      const projectName = await prompt.single({message: 'Informal name for your project'})
      return createProject(apiClient, {displayName: projectName})
    }

    debug(`User has ${projects.length} project(s) already, showing list of choices`)

    const projectChoices = projects.map(project => ({
      value: project.projectId,
      name: `${project.displayName} [${project.projectId}]`
    }))

    const selected = await prompt.single({
      message: 'Select project to use',
      type: 'list',
      choices: [
        {value: 'new', name: 'Create new project'},
        new prompt.Separator(),
        ...projectChoices
      ]
    })

    if (selected === 'new') {
      debug('User wants to create a new project, prompting for name')
      return createProject(apiClient, {
        displayName: await prompt.single({message: 'Informal name for your project'})
      })
    }

    debug(`Returning selected project (${selected})`)
    return {
      projectId: selected,
      displayName: projects.find(proj => proj.projectId === selected).displayName
    }
  }

  async function getOrCreateDataset(opts) {
    const client = apiClient({api: {projectId: opts.projectId}})
    const datasets = await client.datasets.list()

    if (datasets.length === 0) {
      debug('No datasets found for project, prompting for name')
      const name = await promptForDatasetName(prompt, {
        message: 'Name of your first data set:',
        default: 'production'
      })

      await client.datasets.create(name)
      return {datasetName: name}
    }

    debug(`User has ${datasets.length} dataset(s) already, showing list of choices`)
    const datasetChoices = datasets.map(dataset => ({value: dataset.name}))

    const selected = await prompt.single({
      message: 'Select dataset to use',
      type: 'list',
      choices: [
        {value: 'new', name: 'Create new dataset'},
        new prompt.Separator(),
        ...datasetChoices
      ]
    })

    if (selected === 'new') {
      debug('User wants to create a new dataset, prompting for name')
      const newDatasetName = await promptForDatasetName(prompt, {
        message: 'Name of your first data set:',
        default: 'production'
      })
      await client.datasets.create(newDatasetName)
      return {datasetName: newDatasetName}
    }

    debug(`Returning selected dataset (${selected})`)
    return {datasetName: selected}
  }
}
<commit_msg>[cli] Change wording for social login to be more welcoming<commit_after>import resolveFrom from 'resolve-from'
import debug from '../../debug'
import getUserConfig from '../../util/getUserConfig'
import getProjectDefaults from '../../util/getProjectDefaults'
import createProvisionalUser from '../../actions/user/createProvisionalUser'
import createProject from '../../actions/project/createProject'
import login from '../../actions/login/login'
import promptForDatasetName from './promptForDatasetName'
import gatherInput from './gatherInput'
import bootstrapTemplate from './bootstrapTemplate'

export default async function initSanity(args, context) {
  const {output, prompt, workDir, apiClient, yarn, chalk} = context
  output.print('This utility walks you through creating a Sanity installation.')
  output.print('Press ^C at any time to quit.\n')

  // If the user isn't already authenticated, make it so
  const userConfig = getUserConfig()
  const hasToken = userConfig.get('authToken')
  const authType = userConfig.get('authType')
  let isProvisional = authType === 'provisional'
  debug(hasToken ? 'User already has a token' : 'User has no token')

  if (hasToken && isProvisional) {
    output.print("We found some temporary auth credentials in your Sanity config - we're gonna use")
    output.print('those, but make sure to claim your account before it expires!\n')
  } else if (hasToken) {
    output.print('Looks like you already have a Sanity-account. Sweet!\n')
  }

  if (!hasToken) {
    const user = await getOrCreateUser()
    isProvisional = user.isProvisional
  }

  // We're authenticated, now lets create a project
  debug('Prompting user to select or create a project')
  const {projectId, displayName} = await getOrCreateProject()
  const sluggedName = displayName.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9]/g, '')
  debug(`Project with name ${displayName} selected`)

  // Now let's pick or create a dataset
  debug('Prompting user to select or create a dataset')
  const {datasetName} = await getOrCreateDataset({projectId, displayName})
  debug(`Dataset with name ${datasetName} selected`)

  // Gather project defaults based on environment
  const defaults = await getProjectDefaults(workDir, {isPlugin: false})

  // Prompt the user for required information
  const answers = await gatherInput(prompt, defaults, {workDir, sluggedName})

  // Ensure we are using the output path provided by user
  const outputPath = answers.outputPath || workDir

  // Build a full set of resolved options
  const initOptions = {
    template: 'moviedb',
    outputDir: outputPath,
    name: sluggedName,
    displayName: displayName,
    dataset: datasetName,
    projectId: projectId,
    provisionalToken: isProvisional && getUserConfig().get('authToken'),
    ...answers
  }

  // Bootstrap Sanity, creating required project files, manifests etc
  const template = await bootstrapTemplate(initOptions, context)

  // Now for the slow part... installing dependencies
  try {
    await yarn(['install'], {...output, rootDir: outputPath})
  } catch (err) {
    throw err
  }

  // Make sure we have the required configs
  const coreCommands = require(resolveFrom(outputPath, '@sanity/core')).commands
  const configCheckCmd = coreCommands.find(cmd => cmd.name === 'configcheck')
  await configCheckCmd.action({extOptions: {quiet: true}}, Object.assign({}, context, {
    workDir: outputPath
  }))

  // Check if we're currently in the output path, so we can give a better start message
  if (outputPath === process.cwd()) {
    output.print(`\n${chalk.green('Success!')} You can now run "${chalk.cyan('sanity start')}"`)
  } else {
    output.print(`\n${chalk.green('Success!')} You can now change to directory "${chalk.cyan(outputPath)}" and run "${chalk.cyan('sanity start')}"`)
  }

  // See if the template has a success message handler and print it
  const successMessage = template.getSuccessMessage ? template.getSuccessMessage(initOptions, context) : ''
  if (successMessage) {
    output.print(`\n${successMessage}`)
  }

  // Create a provisional user and store the token
  async function getOrCreateUser() {
    output.print("We can't find any auth credentials in your Sanity config - looks like you")
    output.print("haven't used Sanity on this system before?\n")
    output.print("If you're looking to try out Sanity and haven't registered we can just set up")
    output.print('a temp account for testing. You can always claim it later.\n')

    // Provide login options
    const authMethod = await prompt.single({
      message: 'Create temporary account?',
      type: 'list',
      choices: [
        {value: 'provisional', name: 'A temp account sounds great'},
        {value: 'login', name: 'Sign in using a Google or Github account'},
        {value: 'arrow', name: 'I took an arrow to the knee'}
      ]
    })

    if (authMethod === 'arrow') {
      output.print("Cool story about the arrow, guess you're in a hurry. Here's a temp account.")
    }

    if (authMethod === 'arrow' || authMethod === 'provisional') {
      try {
        const {token} = await createProvisionalUser(apiClient)
        getUserConfig().set({
          authToken: token,
          authType: 'provisional'
        })

        if (!token) {
          throw new Error('No token received from server')
        }
      } catch (err) {
        const userError = new Error('Failed to create provisional user')
        userError.details = err
        throw userError
      }

      output.print("Good stuff, you now have a token for a temporary account. You'll also need")
      output.print('a project to keep your data sets and collaborators safe and snug')

      return Promise.resolve({isProvisional: true})
    }

    // Provide login options (`sanity login`)
    await login(args, context)

    output.print("Good stuff, you're now authenticated. You'll need a project to keep your")
    output.print('data sets and collaborators safe and snug.')
    return Promise.resolve({isProvisional: false})
  }

  async function getOrCreateProject() {
    const projects = await apiClient({requireProject: false}).projects.list()

    if (projects.length === 0) {
      debug('No projects found for user, prompting for name')
      const projectName = await prompt.single({message: 'Informal name for your project'})
      return createProject(apiClient, {displayName: projectName})
    }

    debug(`User has ${projects.length} project(s) already, showing list of choices`)

    const projectChoices = projects.map(project => ({
      value: project.projectId,
      name: `${project.displayName} [${project.projectId}]`
    }))

    const selected = await prompt.single({
      message: 'Select project to use',
      type: 'list',
      choices: [
        {value: 'new', name: 'Create new project'},
        new prompt.Separator(),
        ...projectChoices
      ]
    })

    if (selected === 'new') {
      debug('User wants to create a new project, prompting for name')
      return createProject(apiClient, {
        displayName: await prompt.single({message: 'Informal name for your project'})
      })
    }

    debug(`Returning selected project (${selected})`)
    return {
      projectId: selected,
      displayName: projects.find(proj => proj.projectId === selected).displayName
    }
  }

  async function getOrCreateDataset(opts) {
    const client = apiClient({api: {projectId: opts.projectId}})
    const datasets = await client.datasets.list()

    if (datasets.length === 0) {
      debug('No datasets found for project, prompting for name')
      const name = await promptForDatasetName(prompt, {
        message: 'Name of your first data set:',
        default: 'production'
      })

      await client.datasets.create(name)
      return {datasetName: name}
    }

    debug(`User has ${datasets.length} dataset(s) already, showing list of choices`)
    const datasetChoices = datasets.map(dataset => ({value: dataset.name}))

    const selected = await prompt.single({
      message: 'Select dataset to use',
      type: 'list',
      choices: [
        {value: 'new', name: 'Create new dataset'},
        new prompt.Separator(),
        ...datasetChoices
      ]
    })

    if (selected === 'new') {
      debug('User wants to create a new dataset, prompting for name')
      const newDatasetName = await promptForDatasetName(prompt, {
        message: 'Name of your first data set:',
        default: 'production'
      })
      await client.datasets.create(newDatasetName)
      return {datasetName: newDatasetName}
    }

    debug(`Returning selected dataset (${selected})`)
    return {datasetName: selected}
  }
}
<|endoftext|>


<commit_before>/**
 * Copyright 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactTreeTraversal
 */

'use strict';

var { HostComponent } = require('ReactTypeOfWork');

function getParent(inst) {
  if (inst._hostParent !== undefined) {
    return inst._hostParent;
  }
  if (typeof inst.tag === 'number') {
    do {
      inst = inst.return;
      // TODO: If this is a HostRoot we might want to bail out.
      // That is depending on if we want nested subtrees (layers) to bubble
      // events to their parent. We could also go through parentNode on the
      // host node but that wouldn't work for React Native and doesn't let us
      // do the portal feature.
    } while (inst && inst.tag !== HostComponent);
    if (inst) {
      return inst;
    }
  }
  return null;
}

/**
 * Return the lowest common ancestor of A and B, or null if they are in
 * different trees.
 */
function getLowestCommonAncestor(instA, instB) {
  var depthA = 0;
  for (var tempA = instA; tempA; tempA = getParent(tempA)) {
    depthA++;
  }
  var depthB = 0;
  for (var tempB = instB; tempB; tempB = getParent(tempB)) {
    depthB++;
  }

  // If A is deeper, crawl up.
  while (depthA - depthB > 0) {
    instA = getParent(instA);
    depthA--;
  }

  // If B is deeper, crawl up.
  while (depthB - depthA > 0) {
    instB = getParent(instB);
    depthB--;
  }

  // Walk in lockstep until we find a match.
  var depth = depthA;
  while (depth--) {
    if (instA === instB) {
      return instA;
    }
    instA = getParent(instA);
    instB = getParent(instB);
  }
  return null;
}

/**
 * Return if A is an ancestor of B.
 */
function isAncestor(instA, instB) {
  while (instB) {
    if (instB === instA) {
      return true;
    }
    instB = getParent(instB);
  }
  return false;
}

/**
 * Return the parent instance of the passed-in instance.
 */
function getParentInstance(inst) {
  return getParent(inst);
}

/**
 * Simulates the traversal of a two-phase, capture/bubble event dispatch.
 */
function traverseTwoPhase(inst, fn, arg) {
  var path = [];
  while (inst) {
    path.push(inst);
    inst = getParent(inst);
  }
  var i;
  for (i = path.length; i-- > 0;) {
    fn(path[i], 'captured', arg);
  }
  for (i = 0; i < path.length; i++) {
    fn(path[i], 'bubbled', arg);
  }
}

/**
 * Traverses the ID hierarchy and invokes the supplied `cb` on any IDs that
 * should would receive a `mouseEnter` or `mouseLeave` event.
 *
 * Does not invoke the callback on the nearest common ancestor because nothing
 * "entered" or "left" that element.
 */
function traverseEnterLeave(from, to, fn, argFrom, argTo) {
  var common = from && to ? getLowestCommonAncestor(from, to) : null;
  var pathFrom = [];
  while (from && from !== common) {
    pathFrom.push(from);
    from = getParent(from);
  }
  var pathTo = [];
  while (to && to !== common) {
    pathTo.push(to);
    to = getParent(to);
  }
  var i;
  for (i = 0; i < pathFrom.length; i++) {
    fn(pathFrom[i], 'bubbled', argFrom);
  }
  for (i = pathTo.length; i-- > 0;) {
    fn(pathTo[i], 'captured', argTo);
  }
}

module.exports = {
  isAncestor: isAncestor,
  getLowestCommonAncestor: getLowestCommonAncestor,
  getParentInstance: getParentInstance,
  traverseTwoPhase: traverseTwoPhase,
  traverseEnterLeave: traverseEnterLeave,
};
<commit_msg>Check alternate in ReactTreeTraversal (#8992)<commit_after>/**
 * Copyright 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactTreeTraversal
 */

'use strict';

var { HostComponent } = require('ReactTypeOfWork');

function getParent(inst) {
  if (inst._hostParent !== undefined) {
    return inst._hostParent;
  }
  if (typeof inst.tag === 'number') {
    do {
      inst = inst.return;
      // TODO: If this is a HostRoot we might want to bail out.
      // That is depending on if we want nested subtrees (layers) to bubble
      // events to their parent. We could also go through parentNode on the
      // host node but that wouldn't work for React Native and doesn't let us
      // do the portal feature.
    } while (inst && inst.tag !== HostComponent);
    if (inst) {
      return inst;
    }
  }
  return null;
}

/**
 * Return the lowest common ancestor of A and B, or null if they are in
 * different trees.
 */
function getLowestCommonAncestor(instA, instB) {
  var depthA = 0;
  for (var tempA = instA; tempA; tempA = getParent(tempA)) {
    depthA++;
  }
  var depthB = 0;
  for (var tempB = instB; tempB; tempB = getParent(tempB)) {
    depthB++;
  }

  // If A is deeper, crawl up.
  while (depthA - depthB > 0) {
    instA = getParent(instA);
    depthA--;
  }

  // If B is deeper, crawl up.
  while (depthB - depthA > 0) {
    instB = getParent(instB);
    depthB--;
  }

  // Walk in lockstep until we find a match.
  var depth = depthA;
  while (depth--) {
    if (instA === instB || instA === instB.alternate) {
      return instA;
    }
    instA = getParent(instA);
    instB = getParent(instB);
  }
  return null;
}

/**
 * Return if A is an ancestor of B.
 */
function isAncestor(instA, instB) {
  while (instB) {
    if (instA === instB || instA === instB.alternate) {
      return true;
    }
    instB = getParent(instB);
  }
  return false;
}

/**
 * Return the parent instance of the passed-in instance.
 */
function getParentInstance(inst) {
  return getParent(inst);
}

/**
 * Simulates the traversal of a two-phase, capture/bubble event dispatch.
 */
function traverseTwoPhase(inst, fn, arg) {
  var path = [];
  while (inst) {
    path.push(inst);
    inst = getParent(inst);
  }
  var i;
  for (i = path.length; i-- > 0;) {
    fn(path[i], 'captured', arg);
  }
  for (i = 0; i < path.length; i++) {
    fn(path[i], 'bubbled', arg);
  }
}

/**
 * Traverses the ID hierarchy and invokes the supplied `cb` on any IDs that
 * should would receive a `mouseEnter` or `mouseLeave` event.
 *
 * Does not invoke the callback on the nearest common ancestor because nothing
 * "entered" or "left" that element.
 */
function traverseEnterLeave(from, to, fn, argFrom, argTo) {
  var common = from && to ? getLowestCommonAncestor(from, to) : null;
  var pathFrom = [];
  while (from && from !== common) {
    pathFrom.push(from);
    from = getParent(from);
  }
  var pathTo = [];
  while (to && to !== common) {
    pathTo.push(to);
    to = getParent(to);
  }
  var i;
  for (i = 0; i < pathFrom.length; i++) {
    fn(pathFrom[i], 'bubbled', argFrom);
  }
  for (i = pathTo.length; i-- > 0;) {
    fn(pathTo[i], 'captured', argTo);
  }
}

module.exports = {
  isAncestor: isAncestor,
  getLowestCommonAncestor: getLowestCommonAncestor,
  getParentInstance: getParentInstance,
  traverseTwoPhase: traverseTwoPhase,
  traverseEnterLeave: traverseEnterLeave,
};
<|endoftext|>


<commit_before>import { isSticky } from '~/lib/utils/sticky';

describe('sticky', () => {
  const el = {
    offsetTop: 0,
    classList: {},
  };

  beforeEach(() => {
    el.offsetTop = 0;
    el.classList.add = jasmine.createSpy('spy');
    el.classList.remove = jasmine.createSpy('spy');
  });

  describe('classList.remove', () => {
    it('does not call classList.remove when stuck', () => {
      isSticky(el, 0, 0);

      expect(
        el.classList.remove,
      ).not.toHaveBeenCalled();
    });

    it('calls classList.remove when not stuck', () => {
      el.offsetTop = 10;
      isSticky(el, 0, 0);

      expect(
        el.classList.remove,
      ).toHaveBeenCalledWith('is-stuck');
    });
  });

  describe('classList.add', () => {
    it('calls classList.add when stuck', () => {
      isSticky(el, 0, 0);

      expect(
        el.classList.add,
      ).toHaveBeenCalledWith('is-stuck');
    });

    it('does not call classList.add when not stuck', () => {
      el.offsetTop = 10;
      isSticky(el, 0, 0);

      expect(
        el.classList.add,
      ).not.toHaveBeenCalled();
    });
  });
});
<commit_msg>fixed and added specs for removing placeholder element<commit_after>import { isSticky } from '~/lib/utils/sticky';

describe('sticky', () => {
  const el = {
    offsetTop: 0,
    classList: {},
    parentNode: {},
    nextElementSibling: {},
  };
  let isStuck = false;

  beforeEach(() => {
    el.offsetTop = 0;
    el.classList.add = jasmine.createSpy('classListAdd');
    el.classList.remove = jasmine.createSpy('classListRemove');
    el.classList.contains = jasmine.createSpy('classListContains').and.callFake(() => isStuck);
    el.parentNode.insertBefore = jasmine.createSpy('insertBefore');
    el.nextElementSibling.remove = jasmine.createSpy('nextElementSibling');
    el.nextElementSibling.classList = {
      contains: jasmine.createSpy('classListContains').and.returnValue(true),
    };
  });

  afterEach(() => {
    isStuck = false;
  });

  describe('classList.remove', () => {
    it('does not call classList.remove when stuck', () => {
      isSticky(el, 0, 0);

      expect(
        el.classList.remove,
      ).not.toHaveBeenCalled();
    });

    it('calls classList.remove when no longer stuck', () => {
      isStuck = true;

      el.offsetTop = 10;
      isSticky(el, 0, 0);

      expect(
        el.classList.remove,
      ).toHaveBeenCalledWith('is-stuck');
    });

    it('removes placeholder when no longer stuck', () => {
      isStuck = true;

      el.offsetTop = 10;
      isSticky(el, 0, 0, true);

      expect(
        el.nextElementSibling.remove,
      ).toHaveBeenCalled();
    });
  });

  describe('classList.add', () => {
    it('calls classList.add when stuck', () => {
      isSticky(el, 0, 0);

      expect(
        el.classList.add,
      ).toHaveBeenCalledWith('is-stuck');
    });

    it('does not call classList.add when not stuck', () => {
      el.offsetTop = 10;
      isSticky(el, 0, 0);

      expect(
        el.classList.add,
      ).not.toHaveBeenCalled();
    });

    it('inserts placeholder element when stuck', () => {
      isSticky(el, 0, 0, true);

      expect(
        el.parentNode.insertBefore,
      ).toHaveBeenCalled();
    });
  });
});
<|endoftext|>


<commit_before>define([
    'backbone',
    'underscore',
    'text!find/templates/app/page/index-list.html',
    'text!find/templates/app/page/index-item.html',
    'js-whatever/js/list-view'
], function(Backbone, _, listTemplate, itemTemplate, ListView) {

    return Backbone.View.extend({
        listTemplate: _.template(listTemplate),
        itemTemplate: _.template(itemTemplate),
        className: 'table',
        tagName: 'table',

        events: {
            'click tr': function(e) {
                var toggledIndex = $(e.currentTarget).find('[data-id]').data('id');

                this.changeIndex(toggledIndex);
            }
        },

        initialize: function (options) {
            this.queryModel = options.queryModel;
            this.indexesCollection = options.indexesCollection;

            this.indexes = {};

            this.listenTo(this.indexesCollection, 'sync', function() {
                this.selectAll();

                this.listView = new ListView({
                    collection: this.indexesCollection,
                    itemOptions: {
                        tagName: 'tr',
                        className: 'clickable',
                        template: this.itemTemplate
                    }
                });

                this.trigger('sync');

                this.listView.setElement(this.$el).render();

            }, this);

            this.listenTo(this.queryModel, 'change:indexes', function(model, queryModelIndexes) {
                this.indexes = {};

                _.each(queryModelIndexes, function(index) {
                    this.indexes[index] = true;
                }, this);

                this.update();
            });
        },

        render: function() {
            this.$el.html(this.listTemplate());
        },

        selectedIndexes: function() {
            return _.chain(this.indexes).map(function(value, key) {
                return (value ? key : undefined); // Return names of selected indexes and undefined for unselected ones
            }).compact().value();
        },

        changeIndex: function(toggledIndex) {
            this.indexes[toggledIndex] = !this.indexes[toggledIndex];

            this.updateQueryModel(this.selectedIndexes());
        },

        update: function() {
            this.$('i').addClass('hide');

            _.each(this.indexes, function(value, key) {
                var checkbox = this.$("[data-id='" + key + "']").parent().find('i');

                checkbox.toggleClass('hide', !value);
            }, this);

            var selectedIndexes = this.selectedIndexes();

            if(selectedIndexes.length === 1) {
                this.$('[data-id="'+selectedIndexes[0]+'"]').parent().addClass('disabled-index');
            } else {
                this.$('[data-id]').parent().removeClass('disabled-index');
            }
        },

        selectAll: function() {
            this.indexesCollection.each(function(indexModel) {
                this.indexes[indexModel.id] = true;
            }, this);

            this.updateQueryModel(this.selectedIndexes());
        },

        updateQueryModel: function(selectedIndexes) {
            this.queryModel.set({
                indexes: selectedIndexes
            });
        }
    });
});
<commit_msg>Fix issues arising as a result of rebasing (jQuery selector matching too may elements). [rev: daniel.grayling]<commit_after>define([
    'backbone',
    'underscore',
    'text!find/templates/app/page/index-list.html',
    'text!find/templates/app/page/index-item.html',
    'js-whatever/js/list-view'
], function(Backbone, _, listTemplate, itemTemplate, ListView) {

    return Backbone.View.extend({
        listTemplate: _.template(listTemplate),
        itemTemplate: _.template(itemTemplate),
        className: 'table',
        tagName: 'table',

        events: {
            'click tr': function(e) {
                var toggledIndex = $(e.currentTarget).find('tr[data-id]').data('id');

                this.changeIndex(toggledIndex);
            }
        },

        initialize: function (options) {
            this.queryModel = options.queryModel;
            this.indexesCollection = options.indexesCollection;

            this.indexes = {};

            this.listenTo(this.indexesCollection, 'sync', function() {
                this.selectAll();

                this.listView = new ListView({
                    collection: this.indexesCollection,
                    itemOptions: {
                        tagName: 'tr',
                        className: 'clickable',
                        template: this.itemTemplate
                    }
                });

                this.trigger('sync');

                this.listView.setElement(this.$el).render();

            }, this);

            this.listenTo(this.queryModel, 'change:indexes', function(model, queryModelIndexes) {
                this.indexes = {};

                _.each(queryModelIndexes, function(index) {
                    this.indexes[index] = true;
                }, this);

                this.update();
            });
        },

        render: function() {
            this.$el.html(this.listTemplate());
        },

        selectedIndexes: function() {
            return _.chain(this.indexes).map(function(value, key) {
                return (value ? key : undefined); // Return names of selected indexes and undefined for unselected ones
            }).compact().value();
        },

        changeIndex: function(toggledIndex) {
            this.indexes[toggledIndex] = !this.indexes[toggledIndex];

            this.updateQueryModel(this.selectedIndexes());
        },

        update: function() {
            this.$('i').addClass('hide');

            _.each(this.indexes, function(value, key) {
                var checkbox = this.$("tr[data-id='" + key + "'] i");

                checkbox.toggleClass('hide', !value);
            }, this);

            var selectedIndexes = this.selectedIndexes();

            if(selectedIndexes.length === 1) {
                this.$('tr[data-id="' + selectedIndexes[0] + '"]').addClass('disabled-index');
            } else {
                this.$('tr[data-id]').removeClass('disabled-index');
            }
        },

        selectAll: function() {
            this.indexesCollection.each(function(indexModel) {
                this.indexes[indexModel.id] = true;
            }, this);

            this.updateQueryModel(this.selectedIndexes());
        },

        updateQueryModel: function(selectedIndexes) {
            this.queryModel.set({
                indexes: selectedIndexes
            });
        }
    });
});
<|endoftext|>


<commit_before>dojo.provide("dojox.widget.FisheyeLite");
dojo.experimental("dojox.widget.FisheyeLite");

dojo.require("dijit._Widget");
dojo.require("dojox.fx.easing");

dojo.declare("dojox.widget.FisheyeLite",
	dijit._Widget,
	{
	// summary:  A Light-weight Fisheye Component, or an exhanced version
	//		of dojo.fx.Toggler ...
	//
	// description:
	//		A Simple FisheyeList-like widget which (in the interest of
	//		performance) relies on well-styled content for positioning,
	// 		and natural page layout for rendering.
	//
	//		use position:absolute/relative nodes to prevent layout
	//		changes, and use caution when seleting properties to
	//		scale. Negative scaling works, but some properties
	//		react poorly to being set to negative values, IE being
	//		particularly annoying in that regard.
	//
	//		quirk: uses the domNode as the target of the animation
	//		unless it finds a node class="fisheyeTarget" in the container
	//		being turned into a FisheyeLite instance
	//
	// example:
	//	|	// make all the LI's in a node Fisheye's:
	//	|   dojo.query("#node li").forEach(function(n){
	// 	|		new dojox.widget.FisheyeLite({},n);
	//	|	});
	//
	// duationIn: Integer
	//		The time (in ms) the run the show animation
	durationIn: 350,

	// easeIn: Function
	//		An easing function to use for the show animation
	easeIn: dojox.fx.easing.backOut,

	// durationOut: Integer
	//		The Time (in ms) to run the hide animation
	durationOut: 1420,

	// easeOut: Function
	// 		An easing function to use for the hide animation
	easeOut: dojox.fx.easing.elasticOut,

	//	properties: Object
	//			An object of "property":scale pairs
	//			defaults to font-size with a scale of 2.75
	properties: {
		fontSize: 2.75
	},

	// unit: String
	//		Sometimes, you need to specify a unit. Should be part of
	//		properties attrib, but was trying to shorthand the logic there
	unit:"px",

	postCreate: function(){

		this.inherited(arguments);
		this._target = dojo.query(".fisheyeTarget",this.domNode)[0] || this.domNode;
		this._makeAnims();

		this.connect(this.domNode,"onmouseover","show");
		this.connect(this.domNode,"onmouseout","hide");
		this.connect(this._target,"onclick","onClick");

	},

	show: function(){
		// summary:
		//		Show this Fisheye item.
		this._runningOut.stop();
		this._runningIn.play();
	},

	hide: function(){
		// summary:
		//		Hide this fisheye item on mouse leave
		this._runningIn.stop();
		this._runningOut.play();
	},

	_makeAnims: function(){
		// summary:
		//		Pre-generate the animations

		// create two properties: objects, one for each "state"
		var _in = {};
		var _out = {};
		var cs = dojo.getComputedStyle(this._target);
		for(var p in this.properties){
			var v = parseInt(cs[p]);
			// note: do not set negative scale for [a list of properties] for IE support
			// note: filter:'s are your own issue, too ;)
			_out[p] = { end: v, unit:this.unit };
			_in[p] = {	end: (this.properties[p]*v), unit:this.unit };
		}

		this._runningIn = dojo.animateProperty({
			node: this._target,
			easing: this.easeIn,
			duration: this.durationIn,
			properties: _in
		});

		this._runningOut = dojo.animateProperty({
			node: this._target,
			duration: this.durationOut,
			easing: this.easeOut,
			properties: _out
		});

		this.connect(this._runningIn,"onEnd",dojo.hitch(this,"onSelected",this));
	},

	onClick: function(/* Event */e){
		// summary: stub function fired when target is clicked
		//		connect or override to use.
	},

	onSelected: function(/* Object */e){
		// summary: stub function fired when Fisheye Item is fully visible and
		// 		hovered. connect or override use.
	}

});
<commit_msg>move object from properties to constructor so it's not shared.<commit_after>dojo.provide("dojox.widget.FisheyeLite");
dojo.experimental("dojox.widget.FisheyeLite");

dojo.require("dijit._Widget");
dojo.require("dojox.fx.easing");

dojo.declare("dojox.widget.FisheyeLite",
	dijit._Widget,
	{
	// summary:  A Light-weight Fisheye Component, or an exhanced version
	//		of dojo.fx.Toggler ...
	//
	// description:
	//		A Simple FisheyeList-like widget which (in the interest of
	//		performance) relies on well-styled content for positioning,
	// 		and natural page layout for rendering.
	//
	//		use position:absolute/relative nodes to prevent layout
	//		changes, and use caution when seleting properties to
	//		scale. Negative scaling works, but some properties
	//		react poorly to being set to negative values, IE being
	//		particularly annoying in that regard.
	//
	//		quirk: uses the domNode as the target of the animation
	//		unless it finds a node class="fisheyeTarget" in the container
	//		being turned into a FisheyeLite instance
	//
	// example:
	//	|	// make all the LI's in a node Fisheye's:
	//	|   dojo.query("#node li").forEach(function(n){
	// 	|		new dojox.widget.FisheyeLite({},n);
	//	|	});
	//
	// duationIn: Integer
	//		The time (in ms) the run the show animation
	durationIn: 350,

	// easeIn: Function
	//		An easing function to use for the show animation
	easeIn: dojox.fx.easing.backOut,

	// durationOut: Integer
	//		The Time (in ms) to run the hide animation
	durationOut: 1420,

	// easeOut: Function
	// 		An easing function to use for the hide animation
	easeOut: dojox.fx.easing.elasticOut,

	//	properties: Object
	//			An object of "property":scale pairs
	//			defaults to font-size with a scale of 2.75
	properties: null,

	// unit: String
	//		Sometimes, you need to specify a unit. Should be part of
	//		properties attrib, but was trying to shorthand the logic there
	unit:"px",

	constructor: function(props, node){
		this.properties = props.properties || {
			fontSize: 2.75
		}
	},

	postCreate: function(){

		this.inherited(arguments);
		this._target = dojo.query(".fisheyeTarget",this.domNode)[0] || this.domNode;
		this._makeAnims();

		this.connect(this.domNode,"onmouseover","show");
		this.connect(this.domNode,"onmouseout","hide");
		this.connect(this._target,"onclick","onClick");

	},

	show: function(){
		// summary:
		//		Show this Fisheye item.
		this._runningOut.stop();
		this._runningIn.play();
	},

	hide: function(){
		// summary:
		//		Hide this fisheye item on mouse leave
		this._runningIn.stop();
		this._runningOut.play();
	},

	_makeAnims: function(){
		// summary:
		//		Pre-generate the animations

		// create two properties: objects, one for each "state"
		var _in = {};
		var _out = {};
		var cs = dojo.getComputedStyle(this._target);
		for(var p in this.properties){
			var v = parseInt(cs[p]);
			// note: do not set negative scale for [a list of properties] for IE support
			// note: filter:'s are your own issue, too ;)
			_out[p] = { end: v, unit:this.unit };
			_in[p] = {	end: (this.properties[p]*v), unit:this.unit };
		}

		this._runningIn = dojo.animateProperty({
			node: this._target,
			easing: this.easeIn,
			duration: this.durationIn,
			properties: _in
		});

		this._runningOut = dojo.animateProperty({
			node: this._target,
			duration: this.durationOut,
			easing: this.easeOut,
			properties: _out
		});

		this.connect(this._runningIn,"onEnd",dojo.hitch(this,"onSelected",this));
	},

	onClick: function(/* Event */e){
		// summary: stub function fired when target is clicked
		//		connect or override to use.
	},

	onSelected: function(/* Object */e){
		// summary: stub function fired when Fisheye Item is fully visible and
		// 		hovered. connect or override use.
	}

});
<|endoftext|>


<commit_before>var C = crel2;

API.widget.linkMyCSS('css.css');

var txt_register = 'Register';
var txt_login = 'Login';

var js = document.createElement("script");
js.type = "text/javascript";
js.src = '//www.google.com/recaptcha/api/js/recaptcha_ajax.js';
document.body.appendChild(js);

var login = API.widget.create();
login.addClass('login');

var register ,captcha_placeholder, mail, user, pass, button, form, messages, forgot;

C(login,
	form = C('form',
		user = C('input', ['type', 'text',     'name', 'nick',      'placeholder', 'User',     'class', 'c', 'tabindex', 1]),
		pass = C('input', ['type', 'password', 'name', 'password',  'placeholder', 'Password', 'class', 'c', 'tabindex', 2]),
		mail = C('input', ['type', 'text',     'name', 'email',     'placeholder', 'email',    'class', 'c invisible']),
		captcha_placeholder = C('div', ['class', 'captcha_placeholder']),
		C('label', ['class', 'c'],
			register = C('input', ['type', 'checkbox']),
			"I don't have an account"
		),
		C('label', ['class', 'c forgotten'],
			forgot = C('input', ['type', 'checkbox']),
			"I forgot my password"
		),
		button = C('input', ['type', 'submit', 'name', 'submit', 'value', 'Login', 'class', 'c', 'tabindex', 3])
	),
	messages = C('div', ['class', 'message'])
);

API.document.wrapElement(user).wrapElement(pass).wrapElement(mail);

register.checked = false;

register.onclick = function(){
	if(register.disabled) return;

	if(register.checked){
		forgot.disabled = true;

		login.style.marginTop = '-10%';

		button.value = txt_register;
		mail.removeClass('invisible');

		button.setAttribute('tabindex', 5);
		mail.setAttribute('tabindex', 3);

		Recaptcha.create(SERVER_VARS.CAPTCHA_PUB_KEY, captcha_placeholder, {
			theme: "red",
			callback: function(){
				document.getElementById('recaptcha_response_field').setAttribute('tabindex', 4);
			}
		});
	}
	else{
		backToNormal();
	}
}

forgot.onclick = function(){
	if(forgot.disabled) return;

	if(forgot.checked){
		register.disabled = true;

		login.style.marginTop = '-5%';

		button.value = txt_register;
		mail.removeClass('invisible');
		pass.addClass('invisible');
		user.addClass('invisible');

		pass.removeAttribute('tabindex');
		user.removeAttribute('tabindex');
		button.setAttribute('tabindex', 3);
		mail.setAttribute('tabindex', 1);

		Recaptcha.create(SERVER_VARS.CAPTCHA_PUB_KEY, captcha_placeholder, {
			theme: "red",
			callback: function(){
				document.getElementById('recaptcha_response_field').setAttribute('tabindex', 2);
			}
		});
	}
	else{
		backToNormal();
	}
}

function backToNormal(){
	register.disabled = false;
	forgot.disabled = false;

	login.style.marginTop = '0px';

	button.value = txt_login;
	mail.addClass('invisible');
	pass.removeClass('invisible');
	user.removeClass('invisible');

	mail.removeAttribute('tabindex');
	button.setAttribute('tabindex', 3);
	pass.setAttribute('tabindex', 2);
	user.setAttribute('tabindex', 1);

	captcha_placeholder.innerHTML = '';

	form.setAttribute
}

function submit(){
	button.value = 'Wait...';

	var elems = [button, user, pass];

	if(register.checked || forgot.checked){
		elems.push(document.getElementById('recaptcha_response_field'));
		elems.push(document.getElementById('recaptcha_challenge_field'));
		elems.push(mail);
	}

	var data = '';
	for(var i = 0; i < elems.length; i++){
		data += elems[i].getAttribute("name") + '=' + encodeURIComponent(elems[i].value) + '&';
	}
	data = data.substr(0, data.length -1);

	if(register.checked){
		url = 'https://'+API.domain+'register_js.php';
	} else if(forgot.checked){
		url = 'https://'+API.domain+'forgot_js.php';
	} else {
		url = 'https://'+API.domain+'login_js.php';
	}

	API.xhr(url, data, function(data){
		data = JSON.parse(data);
		if(data.status === 'OK'){
			if(register.checked){
				register.checked = false;
				submit();
			} else if(forgot.checked){
				forgot.checked = false;
			}else{
				location.href = '//' + API.domain;
			}
		}
		else{
			fail(data.problem);
		}
	}, function(){fail('Server unreachable');});

	return false;
}

form.onsubmit = submit;

function fail(txt){
	user.addClass('fail');
	pass.addClass('fail');
	mail.addClass('fail');

	messages.innerHTML = txt;

	if(register.checked){
		Recaptcha.reload();
	}

	button.value = register.checked ? txt_register : txt_login;

	setTimeout(function(){
		user.removeClass('fail');
		pass.removeClass('fail');
		mail.removeClass('fail');
	}, 4000);
}
<commit_msg>Login texts fix<commit_after>var C = crel2;

API.widget.linkMyCSS('css.css');

var txt_register = 'Register';
var txt_login = 'Login';
var txt_remember = 'Remember';

var js = document.createElement("script");
js.type = "text/javascript";
js.src = '//www.google.com/recaptcha/api/js/recaptcha_ajax.js';
document.body.appendChild(js);

var login = API.widget.create();
login.addClass('login');

var register ,captcha_placeholder, mail, user, pass, button, form, messages, forgot;

C(login,
	form = C('form',
		user = C('input', ['type', 'text',     'name', 'nick',      'placeholder', 'User',     'class', 'c', 'tabindex', 1]),
		pass = C('input', ['type', 'password', 'name', 'password',  'placeholder', 'Password', 'class', 'c', 'tabindex', 2]),
		mail = C('input', ['type', 'text',     'name', 'email',     'placeholder', 'email',    'class', 'c invisible']),
		captcha_placeholder = C('div', ['class', 'captcha_placeholder']),
		C('label', ['class', 'c'],
			register = C('input', ['type', 'checkbox']),
			"I don't have an account"
		),
		C('label', ['class', 'c forgotten'],
			forgot = C('input', ['type', 'checkbox']),
			"I forgot my password"
		),
		button = C('input', ['type', 'submit', 'name', 'submit', 'value', 'Login', 'class', 'c', 'tabindex', 3])
	),
	messages = C('div', ['class', 'message'])
);

API.document.wrapElement(user).wrapElement(pass).wrapElement(mail);

register.checked = false;

register.onclick = function(){
	if(register.disabled) return;

	if(register.checked){
		forgot.disabled = true;

		login.style.marginTop = '-10%';

		button.value = txt_register;
		mail.removeClass('invisible');

		button.setAttribute('tabindex', 5);
		mail.setAttribute('tabindex', 3);

		Recaptcha.create(SERVER_VARS.CAPTCHA_PUB_KEY, captcha_placeholder, {
			theme: "red",
			callback: function(){
				document.getElementById('recaptcha_response_field').setAttribute('tabindex', 4);
			}
		});
	}
	else{
		backToNormal();
	}
}

forgot.onclick = function(){
	if(forgot.disabled) return;

	if(forgot.checked){
		register.disabled = true;

		login.style.marginTop = '-5%';

		button.value = txt_remember;
		mail.removeClass('invisible');
		pass.addClass('invisible');
		user.addClass('invisible');

		pass.removeAttribute('tabindex');
		user.removeAttribute('tabindex');
		button.setAttribute('tabindex', 3);
		mail.setAttribute('tabindex', 1);

		Recaptcha.create(SERVER_VARS.CAPTCHA_PUB_KEY, captcha_placeholder, {
			theme: "red",
			callback: function(){
				document.getElementById('recaptcha_response_field').setAttribute('tabindex', 2);
			}
		});
	}
	else{
		backToNormal();
	}
}

function backToNormal(){
	register.disabled = false;
	forgot.disabled = false;

	login.style.marginTop = '0px';

	button.value = txt_login;
	mail.addClass('invisible');
	pass.removeClass('invisible');
	user.removeClass('invisible');

	mail.removeAttribute('tabindex');
	button.setAttribute('tabindex', 3);
	pass.setAttribute('tabindex', 2);
	user.setAttribute('tabindex', 1);

	captcha_placeholder.innerHTML = '';
}

function submit(){
	button.value = 'Wait...';

	var elems = [button, user, pass];

	if(register.checked || forgot.checked){
		elems.push(document.getElementById('recaptcha_response_field'));
		elems.push(document.getElementById('recaptcha_challenge_field'));
		elems.push(mail);
	}

	var data = '';
	for(var i = 0; i < elems.length; i++){
		data += elems[i].getAttribute("name") + '=' + encodeURIComponent(elems[i].value) + '&';
	}
	data = data.substr(0, data.length -1);

	if(register.checked){
		url = 'https://'+API.domain+'register_js.php';
	} else if(forgot.checked){
		url = 'https://'+API.domain+'forgot_js.php';
	} else {
		url = 'https://'+API.domain+'login_js.php';
	}

	API.xhr(url, data, function(data){
		data = JSON.parse(data);
		if(data.status === 'OK'){
			if(register.checked){
				register.checked = false;
				submit();
			} else if(forgot.checked){
				forgot.checked = false;
			}else{
				location.href = '//' + API.domain;
			}
		}
		else{
			fail(data.problem);
		}
	}, function(){fail('Server unreachable');});

	return false;
}

form.onsubmit = submit;

function fail(txt){
	user.addClass('fail');
	pass.addClass('fail');
	mail.addClass('fail');

	messages.innerHTML = txt;

	if(register.checked){
		Recaptcha.reload();
	}

	if(register.checked){
		button.value = txt_register;
	} else if(forgot.checked){
		button.value = txt_remember;
	} else {
		button.value = txt_login;
	}

	setTimeout(function(){
		user.removeClass('fail');
		pass.removeClass('fail');
		mail.removeClass('fail');
	}, 4000);
}
<|endoftext|>


<commit_before>import { IRenderMime } from 'jupyterlab/lib/rendermime';
import { IDocumentRegistry } from 'jupyterlab/lib/docregistry';
import { toArray, ArrayExt } from '@phosphor/algorithm';
import { OutputRenderer } from './output';
import { DocWidgetFactory } from './doc';
import './index.css';

/**
 * Activate the extension.
 */
function activatePlugin(app, rendermime, registry) {
  /**
   * Calculate the index of the renderer in the array renderers (e.g. Insert
   * this renderer after any renderers with mime type that matches "+json")
   * or simply pass an integer such as 0.
   */
  // const index = ArrayExt.findLastIndex(
  //   toArray(rendermime.mimeTypes()),
  //   mime => mime.endsWith('+json')
  // ) + 1;
  const index = 0;

  /**
   * Add the renderer to the registry of renderers.
   */
  rendermime.addRenderer(
    {
      mimeType: 'application/geo+json',
      renderer: new OutputRenderer()
    },
    index
  );

  /**
   * Set the extensions associated with GeoJSON.
   */
  const EXTENSIONS = ['.geojson', '.json'];
  const DEFAULT_EXTENSIONS = ['.geojson'];

    /**
     * Add file handler for geojson files.
     */
    const options = {
      fileExtensions: EXTENSIONS,
      defaultFor: DEFAULT_EXTENSIONS,
      name: 'GeoJSON',
      displayName: 'GeoJSON',
      modelName: 'text',
      preferKernel: false,
      canStartKernel: false
    };

  registry.addWidgetFactory(new DocWidgetFactory(options));
}

const Plugin = {
  id: 'jupyter.extensions.GeoJSON',
  requires: [IRenderMime, IDocumentRegistry],
  activate: activatePlugin,
  autoStart: true
};

export default Plugin;
<commit_msg>Add `.geo.json` extension<commit_after>import { IRenderMime } from 'jupyterlab/lib/rendermime';
import { IDocumentRegistry } from 'jupyterlab/lib/docregistry';
import { toArray, ArrayExt } from '@phosphor/algorithm';
import { OutputRenderer } from './output';
import { DocWidgetFactory } from './doc';
import './index.css';

/**
 * Activate the extension.
 */
function activatePlugin(app, rendermime, registry) {
  /**
   * Calculate the index of the renderer in the array renderers (e.g. Insert
   * this renderer after any renderers with mime type that matches "+json")
   * or simply pass an integer such as 0.
   */
  // const index = ArrayExt.findLastIndex(
  //   toArray(rendermime.mimeTypes()),
  //   mime => mime.endsWith('+json')
  // ) + 1;
  const index = 0;

  /**
   * Add the renderer to the registry of renderers.
   */
  rendermime.addRenderer(
    {
      mimeType: 'application/geo+json',
      renderer: new OutputRenderer()
    },
    index
  );

  /**
   * Set the extensions associated with GeoJSON.
   */
  const EXTENSIONS = ['.geojson', '.geo.json', '.json'];
  const DEFAULT_EXTENSIONS = ['.geojson', '.geo.json'];

    /**
     * Add file handler for geojson files.
     */
    const options = {
      fileExtensions: EXTENSIONS,
      defaultFor: DEFAULT_EXTENSIONS,
      name: 'GeoJSON',
      displayName: 'GeoJSON',
      modelName: 'text',
      preferKernel: false,
      canStartKernel: false
    };

  registry.addWidgetFactory(new DocWidgetFactory(options));
}

const Plugin = {
  id: 'jupyter.extensions.GeoJSON',
  requires: [IRenderMime, IDocumentRegistry],
  activate: activatePlugin,
  autoStart: true
};

export default Plugin;
<|endoftext|>


<commit_before>import { connect } from 'react-redux'
import { increment, doubleAsync } from '../modules/counter'

/*  This is a container component. Notice it does not contain any JSX,
    nor does it import React. This component is **only** responsible for
    wiring in the actions and state necessary to render a presentational
    component - in this case, the counter:   */

import Counter from 'components/Counter'

/*  Object of action creators (can also be function that returns object).
    Keys will be passed as props to presentational components. Here we are
    implementing our wrapper around increment; the component doesn't care   */

const mapActionCreators = {
  increment: () => increment(1),
  doubleAsync
}

const mapStateToProps = (state) => ({
  counter: state.counter
})

/*  Note: mapStateToProps is where you should use `reselect` to create selectors, ie:

    import { createSelector } from 'reselect'
    const counter = (state) => state.counter
    const tripleCount = createSelector(counter, (count) => count * 3)
    const mapStateToProps = (state) => ({
      counter: tripleCount(state)
    })

    Selectors can compute derived data, allowing Redux to store the minimal possible state.
    Selectors are efficient. A selector is not recomputed unless one of its arguments change.
    Selectors are composable. They can be used as input to other selectors.
    https://github.com/reactjs/reselect    */

export default connect(mapStateToProps, mapActionCreators)(Counter)
<commit_msg>chore(CounterContainer): clarify mapDispatchToProps var<commit_after>import { connect } from 'react-redux'
import { increment, doubleAsync } from '../modules/counter'

/*  This is a container component. Notice it does not contain any JSX,
    nor does it import React. This component is **only** responsible for
    wiring in the actions and state necessary to render a presentational
    component - in this case, the counter:   */

import Counter from 'components/Counter'

/*  Object of action creators (can also be function that returns object).
    Keys will be passed as props to presentational components. Here we are
    implementing our wrapper around increment; the component doesn't care   */

const mapDispatchtoProps = {
  increment: () => increment(1),
  doubleAsync
}

const mapStateToProps = (state) => ({
  counter: state.counter
})

/*  Note: mapStateToProps is where you should use `reselect` to create selectors, ie:

    import { createSelector } from 'reselect'
    const counter = (state) => state.counter
    const tripleCount = createSelector(counter, (count) => count * 3)
    const mapStateToProps = (state) => ({
      counter: tripleCount(state)
    })

    Selectors can compute derived data, allowing Redux to store the minimal possible state.
    Selectors are efficient. A selector is not recomputed unless one of its arguments change.
    Selectors are composable. They can be used as input to other selectors.
    https://github.com/reactjs/reselect    */

export default connect(mapStateToProps, mapDispatchtoProps)(Counter)
<|endoftext|>


<commit_before>'use strict';

// Takes a callback which is fired when the element is rendered
function onRender(element, callback) {

  // Element can be jQuery or vanilla JS
  if (element instanceof jQuery) element = element[0];

  // Force a layout which returns a promise
  // Promise.resolve(element.getBoundingClientRect())
  // .then(function() {
  //   requestAnimationFrame(function() {
  //     callback();
  //   });
  // });
  callback();

}

// Check if used as a standalone script or a node module
if (typeof exports === 'object') {
  module.exports = onRender;
} else {
  window.onRender = onRender;
}
<commit_msg>Whoops.<commit_after>'use strict';

// Takes a callback which is fired when the element is rendered
function onRender(element, callback) {

  // Element can be jQuery or vanilla JS
  if (element instanceof jQuery) element = element[0];

  // Force a layout which returns a promise
  Promise.resolve(element.getBoundingClientRect())
  .then(function() {
    requestAnimationFrame(function() {
      callback();
    });
  });
  callback();

}

// Check if used as a standalone script or a node module
if (typeof exports === 'object') {
  module.exports = onRender;
} else {
  window.onRender = onRender;
}
<|endoftext|>


<commit_before>   if (window && window.jQuery && $ === window.jQuery) {
      $.extend($, {
         make : function ( string ) {
            return $(string);
         },

         concat : function ( args ) {
            if (args instanceof Array) {
               return args.join('');
            } else {
               args = Array.prototype.slice.apply(arguments);
               return String.prototype.concat.apply(args.join(''));
            }
         },

         subscriptions : {},

         // Topic: string defining topic: /some/topic
         // Data: a string, number, array or object.
         subscribe : function (topic, callback) {
            if (!$.subscriptions[topic]) {
               $.subscriptions[topic] = [];
            }
            var token = ($.uuidNum());
            $.subscriptions[topic].push({
               token: token,
               callback: callback
            });
            return token;
         },

         unsubscribe : function ( token ) {
            setTimeout(function() {
               for (var m in $.subscriptions) {
                  if ($.subscriptions[m]) {
                      for (var i = 0, len = $.subscriptions[m].length; i < len; i++) {
                          if ($.subscriptions[m][i].token === token) {
                              $.subscriptions[m].splice(i, 1);
                              return token;
                          }
                      }
                  }
               }
               return false;
            });
         },

         publish : function ( topic, args ) {
            if (!$.subscriptions[topic]) {
               return false;
            }
            setTimeout(function () {
               var len = $.subscriptions[topic] ? $.subscriptions[topic].length : 0;
               while (len--) {
                   $.subscriptions[topic][len].callback(topic, args);
               }
               return true;
            });
         },

         templates : {},

         template : function ( tmpl, variable ) {
            var regex, delimiterOpen, delimiterClosed;
            variable = variable ? variable : 'data';
            regex = /\[\[=([\s\S]+?)\]\]/g;
            delimiterOpen = '[[';
            delimiterClosed = ']]';
            var template =  new Function(variable,
               "var p=[];" + "p.push('" + tmpl
               .replace(/[\r\t\n]/g, " ")
               .split("'").join("\\'")
               .replace(regex,"',$1,'")
               .split(delimiterOpen).join("');")
               .split(delimiterClosed).join("p.push('") + "');" +
               "return p.join('');");
            return template;
         },
         isiPhone : /iphone/img.test(navigator.userAgent),
         isiPad : /ipad/img.test(navigator.userAgent),
         isiPod : /ipod/img.test(navigator.userAgent),
         isiOS : /ip(hone|od|ad)/img.test(navigator.userAgent),
         isAndroid : (/android/img.test(navigator.userAgent) && !/trident/img.test(navigator.userAgent)),
         isWebOS : /webos/img.test(navigator.userAgent),
         isBlackberry : /blackberry/img.test(navigator.userAgent),
         isTouchEnabled : ('createTouch' in document),
         isOnline :  navigator.onLine,
         isStandalone : navigator.standalone,
         isiOS6 : navigator.userAgent.match(/OS 6/i),
         isiOS7 : navigator.userAgent.match(/OS 7/i),
         isWin : /trident/img.test(navigator.userAgent),
         isWinPhone : (/trident/img.test(navigator.userAgent) && /mobile/img.test(navigator.userAgent)),
         isIE10 : navigator.userAgent.match(/msie 10/i),
         isIE11 : navigator.userAgent.match(/msie 11/i),
         isWebkit : navigator.userAgent.match(/webkit/),
         isMobile : /mobile/img.test(navigator.userAgent),
         isDesktop : !(/mobile/img.test(navigator.userAgent)),
         isSafari : (!/Chrome/img.test(navigator.userAgent) && /Safari/img.test(navigator.userAgent)),
         isChrome : /chrome/i.test(navigator.userAgent)
      });
   }<commit_msg>Add $.uuidNum to jQuery extensions.<commit_after>   if (window && window.jQuery && $ === window.jQuery) {
      $.extend($, {
         uuidNum : function ( ) {
            return Math.floor(((1 + Math.random()) * 0x100000000));
         },
         make : function ( string ) {
            return $(string);
         },

         concat : function ( args ) {
            if (args instanceof Array) {
               return args.join('');
            } else {
               args = Array.prototype.slice.apply(arguments);
               return String.prototype.concat.apply(args.join(''));
            }
         },

         subscriptions : {},

         // Topic: string defining topic: /some/topic
         // Data: a string, number, array or object.
         subscribe : function (topic, callback) {
            if (!$.subscriptions[topic]) {
               $.subscriptions[topic] = [];
            }
            var token = ($.uuidNum());
            $.subscriptions[topic].push({
               token: token,
               callback: callback
            });
            return token;
         },

         unsubscribe : function ( token ) {
            setTimeout(function() {
               for (var m in $.subscriptions) {
                  if ($.subscriptions[m]) {
                      for (var i = 0, len = $.subscriptions[m].length; i < len; i++) {
                          if ($.subscriptions[m][i].token === token) {
                              $.subscriptions[m].splice(i, 1);
                              return token;
                          }
                      }
                  }
               }
               return false;
            });
         },

         publish : function ( topic, args ) {
            if (!$.subscriptions[topic]) {
               return false;
            }
            setTimeout(function () {
               var len = $.subscriptions[topic] ? $.subscriptions[topic].length : 0;
               while (len--) {
                   $.subscriptions[topic][len].callback(topic, args);
               }
               return true;
            });
         },

         templates : {},

         template : function ( tmpl, variable ) {
            var regex, delimiterOpen, delimiterClosed;
            variable = variable ? variable : 'data';
            regex = /\[\[=([\s\S]+?)\]\]/g;
            delimiterOpen = '[[';
            delimiterClosed = ']]';
            var template =  new Function(variable,
               "var p=[];" + "p.push('" + tmpl
               .replace(/[\r\t\n]/g, " ")
               .split("'").join("\\'")
               .replace(regex,"',$1,'")
               .split(delimiterOpen).join("');")
               .split(delimiterClosed).join("p.push('") + "');" +
               "return p.join('');");
            return template;
         },
         isiPhone : /iphone/img.test(navigator.userAgent),
         isiPad : /ipad/img.test(navigator.userAgent),
         isiPod : /ipod/img.test(navigator.userAgent),
         isiOS : /ip(hone|od|ad)/img.test(navigator.userAgent),
         isAndroid : (/android/img.test(navigator.userAgent) && !/trident/img.test(navigator.userAgent)),
         isWebOS : /webos/img.test(navigator.userAgent),
         isBlackberry : /blackberry/img.test(navigator.userAgent),
         isTouchEnabled : ('createTouch' in document),
         isOnline :  navigator.onLine,
         isStandalone : navigator.standalone,
         isiOS6 : navigator.userAgent.match(/OS 6/i),
         isiOS7 : navigator.userAgent.match(/OS 7/i),
         isWin : /trident/img.test(navigator.userAgent),
         isWinPhone : (/trident/img.test(navigator.userAgent) && /mobile/img.test(navigator.userAgent)),
         isIE10 : navigator.userAgent.match(/msie 10/i),
         isIE11 : navigator.userAgent.match(/msie 11/i),
         isWebkit : navigator.userAgent.match(/webkit/),
         isMobile : /mobile/img.test(navigator.userAgent),
         isDesktop : !(/mobile/img.test(navigator.userAgent)),
         isSafari : (!/Chrome/img.test(navigator.userAgent) && /Safari/img.test(navigator.userAgent)),
         isChrome : /chrome/i.test(navigator.userAgent)
      });
   }<|endoftext|>