<commit_before>// Copyright 2010 WebDriver committers
// Copyright 2010 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview Utilities for working with errors as defined by WebDriver's
 * wire protocol: http://code.google.com/p/selenium/wiki/JsonWireProtocol.
 */

goog.provide('bot.Error');
goog.provide('bot.ErrorCode');

goog.require('goog.debug.Error');
goog.require('goog.object');

/**
 * Error codes from the WebDriver wire protocol:
 * http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes
 *
 * @enum {number}
 */
bot.ErrorCode = {
  SUCCESS: 0,  // Included for completeness

  NO_SUCH_ELEMENT: 7,
  NO_SUCH_FRAME: 8,
  UNKNOWN_COMMAND: 9,
  UNSUPPORTED_OPERATION: 9,  // Alias.
  STALE_ELEMENT_REFERENCE: 10,
  ELEMENT_NOT_VISIBLE: 11,
  INVALID_ELEMENT_STATE: 12,
  UNKNOWN_ERROR: 13,
  ELEMENT_NOT_SELECTABLE: 15,
  XPATH_LOOKUP_ERROR: 19,
  NO_SUCH_WINDOW: 23,
  INVALID_COOKIE_DOMAIN: 24,
  UNABLE_TO_SET_COOKIE: 25,
  MODAL_DIALOG_OPENED: 26,
  MODAL_DIALOG_OPEN: 27,
  SCRIPT_TIMEOUT: 28,
  INVALID_SELECTOR_ERROR: 32
};

/**
 * Error extension that includes error status codes from the WebDriver wire
 * protocol:
 * http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes
 *
 * @param {!bot.ErrorCode} code The error's status code.
 * @param {string} opt_message Optional error message.
 * @constructor
 * @extends {goog.debug.Error}
 */
bot.Error = function(code, opt_message) {
  goog.base(this, opt_message);

  /**
   * This error's status code.
   * @type {!bot.ErrorCode}
   */
  this.code = code;

  /** @inheritDoc */
  this.name = (/**@type {string}*/ bot.Error.NAMES_[code] ||
      bot.Error.NAMES_[bot.ErrorCode.UNKNOWN_ERROR]);
};
goog.inherits(bot.Error, goog.debug.Error);

/**
 * A map of error codes to error names.
 * @type {Object.<bot.ErrorCode, String>}
 * @const
 * @private
 */
bot.Error.NAMES_ = goog.object.transpose({
  'NoSuchElementError': bot.ErrorCode.NO_SUCH_ELEMENT,
  'NoSuchFrameError': bot.ErrorCode.NO_SUCH_FRAME,
  'UnknownCommandError': bot.ErrorCode.UNKNOWN_COMMAND,
  'StaleElementReferenceError': bot.ErrorCode.STALE_ELEMENT_REFERENCE,
  'ElementNotVisibleError': bot.ErrorCode.ELEMENT_NOT_VISIBLE,
  'InvalidElementStateError': bot.ErrorCode.INVALID_ELEMENT_STATE,
  'UnknownError': bot.ErrorCode.UNKNOWN_ERROR,
  'ElementNotSelectableError': bot.ErrorCode.ELEMENT_NOT_SELECTABLE,
  'XPathLookupError': bot.ErrorCode.XPATH_LOOKUP_ERROR,
  'NoSuchWindowError': bot.ErrorCode.NO_SUCH_WINDOW,
  'InvalidCookieDomainError': bot.ErrorCode.INVALID_COOKIE_DOMAIN,
  'UnableToSetCookieError': bot.ErrorCode.UNABLE_TO_SET_COOKIE,
  'ModalDialogOpenedError': bot.ErrorCode.MODAL_DIALOG_OPENED,
  'ModalDialogOpenError': bot.ErrorCode.MODAL_DIALOG_OPEN,
  'ScriptTimeoutError': bot.ErrorCode.SCRIPT_TIMEOUT
});

/**
 * Flag used for duck-typing when this code is embedded in a Firefox extension.
 * This is required since an Error thrown in one component and the reported
 * to another will fail instanceof checks in the second component.
 * @type {boolean}
 */
bot.Error.prototype.isAutomationError = true;

if (goog.DEBUG) {
/** @return {string} The string representation of this error. */
bot.Error.prototype.toString = function() {
  return '[' + this.name + '] ' + this.message;
};
}
<commit_msg>SimonStewart: Updating constants for error numbers<commit_after>// Copyright 2010 WebDriver committers
// Copyright 2010 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview Utilities for working with errors as defined by WebDriver's
 * wire protocol: http://code.google.com/p/selenium/wiki/JsonWireProtocol.
 */

goog.provide('bot.Error');
goog.provide('bot.ErrorCode');

goog.require('goog.debug.Error');
goog.require('goog.object');

/**
 * Error codes from the WebDriver wire protocol:
 * http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes
 *
 * @enum {number}
 */
bot.ErrorCode = {
  SUCCESS: 0,  // Included for completeness

  NO_SUCH_ELEMENT: 7,
  NO_SUCH_FRAME: 8,
  UNKNOWN_COMMAND: 9,
  UNSUPPORTED_OPERATION: 9,  // Alias.
  STALE_ELEMENT_REFERENCE: 10,
  ELEMENT_NOT_VISIBLE: 11,
  INVALID_ELEMENT_STATE: 12,
  UNKNOWN_ERROR: 13,
  ELEMENT_NOT_SELECTABLE: 15,
  JAVASCRIPT_ERROR: 17,
  XPATH_LOOKUP_ERROR: 19,
  NO_SUCH_WINDOW: 23,
  INVALID_COOKIE_DOMAIN: 24,
  UNABLE_TO_SET_COOKIE: 25,
  MODAL_DIALOG_OPENED: 26,
  MODAL_DIALOG_OPEN: 27,
  SCRIPT_TIMEOUT: 28
};

/**
 * Error extension that includes error status codes from the WebDriver wire
 * protocol:
 * http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes
 *
 * @param {!bot.ErrorCode} code The error's status code.
 * @param {string} opt_message Optional error message.
 * @constructor
 * @extends {goog.debug.Error}
 */
bot.Error = function(code, opt_message) {
  goog.base(this, opt_message);

  /**
   * This error's status code.
   * @type {!bot.ErrorCode}
   */
  this.code = code;

  /** @inheritDoc */
  this.name = (/**@type {string}*/ bot.Error.NAMES_[code] ||
      bot.Error.NAMES_[bot.ErrorCode.UNKNOWN_ERROR]);
};
goog.inherits(bot.Error, goog.debug.Error);

/**
 * A map of error codes to error names.
 * @type {Object.<bot.ErrorCode, String>}
 * @const
 * @private
 */
bot.Error.NAMES_ = goog.object.transpose({
  'NoSuchElementError': bot.ErrorCode.NO_SUCH_ELEMENT,
  'NoSuchFrameError': bot.ErrorCode.NO_SUCH_FRAME,
  'UnknownCommandError': bot.ErrorCode.UNKNOWN_COMMAND,
  'StaleElementReferenceError': bot.ErrorCode.STALE_ELEMENT_REFERENCE,
  'ElementNotVisibleError': bot.ErrorCode.ELEMENT_NOT_VISIBLE,
  'InvalidElementStateError': bot.ErrorCode.INVALID_ELEMENT_STATE,
  'UnknownError': bot.ErrorCode.UNKNOWN_ERROR,
  'ElementNotSelectableError': bot.ErrorCode.ELEMENT_NOT_SELECTABLE,
  'XPathLookupError': bot.ErrorCode.XPATH_LOOKUP_ERROR,
  'NoSuchWindowError': bot.ErrorCode.NO_SUCH_WINDOW,
  'InvalidCookieDomainError': bot.ErrorCode.INVALID_COOKIE_DOMAIN,
  'UnableToSetCookieError': bot.ErrorCode.UNABLE_TO_SET_COOKIE,
  'ModalDialogOpenedError': bot.ErrorCode.MODAL_DIALOG_OPENED,
  'ModalDialogOpenError': bot.ErrorCode.MODAL_DIALOG_OPEN,
  'ScriptTimeoutError': bot.ErrorCode.SCRIPT_TIMEOUT
});

/**
 * Flag used for duck-typing when this code is embedded in a Firefox extension.
 * This is required since an Error thrown in one component and the reported
 * to another will fail instanceof checks in the second component.
 * @type {boolean}
 */
bot.Error.prototype.isAutomationError = true;

if (goog.DEBUG) {
  /** @return {string} The string representation of this error. */
  bot.Error.prototype.toString = function() {
    return '[' + this.name + '] ' + this.message;
  };
}
<|endoftext|>


<commit_before>/*
 * Licensed to Apereo under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Apereo licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License.  You may obtain a
 * copy of the License at the following location:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
'use strict';

define(['angular', 'jquery'], function(angular, $) {
    return angular.module('portal.storage.services', [])

    /**
    Key Value Service

    This is your basic key->value store for angular. This goes really well with
    [KeyValueStore](https://github.com/UW-Madison-DoIT/KeyValueStore).

    To enable and use, 1) setup a service that accepts a
    getValue?key=<key> and setValue (this is a post function that has a key and
    value object). 2) set a URL in SERVICE_LOC.kvURL. You may have to use
    json-proxy-service or CORS if your key value store is on another machine.

    Service functions:
      - getValue(key)
        - key : string
      - setValue(key, value)
        - key : string
        - value : object
      - isKVStoreActivated()
    **/
    .factory('keyValueService', ['$http', 'miscService', 'SERVICE_LOC',
        function($http, miscService, SERVICE_LOC) {
      // private functions
      var successFn = function(response) {
        return response.data;
      };

      var errorFn = function(response) {
        miscService.redirectUser(response.status, 'Key Value Service');
        return response.data;
      };

      // public functions
      var isKVStoreActivated = function() {
        return !!SERVICE_LOC.kvURL;
      };

      var getValue = function(key) {
        return $http.get(SERVICE_LOC.kvURL + '/'+key)
                  .then(successFn, errorFn);
      };

      var setValue = function(key, value) {
        return $http.put(SERVICE_LOC.kvURL + '/'+ key, value)
                  .then(successFn, errorFn);
      };

      var deleteValue = function(key) {
        return $http.delete(SERVICE_LOC.kvURL + '/'+ key)
                  .then(successFn, errorFn);
      };

      return {
        setValue: setValue,
        deleteValue: deleteValue,
        getValue: getValue,
        isKVStoreActivated: isKVStoreActivated,
      };
    }]);
});
<commit_msg>cache read of key value store<commit_after>/*
 * Licensed to Apereo under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Apereo licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License.  You may obtain a
 * copy of the License at the following location:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
'use strict';

define(['angular', 'jquery'], function(angular, $) {
    return angular.module('portal.storage.services', [])

    /**
    Key Value Service

    This is your basic key->value store for angular. This goes really well with
    [KeyValueStore](https://github.com/UW-Madison-DoIT/KeyValueStore).

    To enable and use, 1) setup a service that accepts a
    getValue?key=<key> and setValue (this is a post function that has a key and
    value object). 2) set a URL in SERVICE_LOC.kvURL. You may have to use
    json-proxy-service or CORS if your key value store is on another machine.

    Service functions:
      - getValue(key)
        - key : string
      - setValue(key, value)
        - key : string
        - value : object
      - isKVStoreActivated()
    **/
    .factory('keyValueService', ['$http', 'miscService', 'SERVICE_LOC',
        function($http, miscService, SERVICE_LOC) {
      // private functions
      var successFn = function(response) {
        return response.data;
      };

      var errorFn = function(response) {
        miscService.redirectUser(response.status, 'Key Value Service');
        return response.data;
      };

      // public functions
      var isKVStoreActivated = function() {
        return !!SERVICE_LOC.kvURL;
      };

      var getValue = function(key) {
        return $http.get(SERVICE_LOC.kvURL + '/'+key, {cache: true})
                  .then(successFn, errorFn);
      };

      var setValue = function(key, value) {
        return $http.put(SERVICE_LOC.kvURL + '/'+ key, value)
                  .then(successFn, errorFn);
      };

      var deleteValue = function(key) {
        return $http.delete(SERVICE_LOC.kvURL + '/'+ key)
                  .then(successFn, errorFn);
      };

      return {
        setValue: setValue,
        deleteValue: deleteValue,
        getValue: getValue,
        isKVStoreActivated: isKVStoreActivated,
      };
    }]);
});
<|endoftext|>


<commit_before>/**
 * Types describing our Redux state and store.
 *
 * This isn't the place for types that are borrowed from the API;
 * those go under `src/api/` (typically in `src/api/modelTypes.js`)
 * and can be imported here as needed.
 *
 * @flow strict-local
 */

import type { InputSelector } from 'reselect';

import type { Account, Outbox } from './types';
import type { Action, NavigateAction } from './actionTypes';
import type {
  Topic,
  HuddlesUnreadItem,
  Message,
  MuteTuple,
  PmsUnreadItem,
  RealmBot,
  RealmEmojiState,
  RealmFilter,
  Narrow,
  Stream,
  StreamUnreadItem,
  Subscription,
  User,
  UserGroup,
  UserPresence,
  UserStatusMapObject,
} from './api/apiTypes';

import type { SessionState } from './session/sessionReducers';

export type * from './actionTypes';

/**
 * An index on `MessagesState`, listing messages in each narrow.
 *
 * Keys are `JSON.stringify`-encoded `Narrow` objects.
 * Values are sorted lists of message IDs.
 *
 * See also `MessagesState`, which stores the message data indexed by ID.
 */
export type NarrowsState = {
  [narrow: string]: number[],
};

/**
 * A map with all messages we've stored locally, indexed by ID.
 *
 * See also `NarrowsState`, which is an index on this data that identifies
 * messages belonging to a given narrow.
 */
export type MessagesState = {|
  [id: number]: $Exact<Message>,
|};

export type UserStatusState = UserStatusMapObject;

export type StreamsState = Stream[];

export type SubscriptionsState = Subscription[];

export type AccountsState = Account[];

/**
 * Info about how complete our knowledge is of the messages in some narrow.
 *
 * @prop older - true just if in some fetch we reached the oldest message
 *   in the narrow.  No need to fetch more in that direction.
 * @prop newer - true just if in some fetch we reached the newest message in
 *   the narrow.  Of course their may always be new messages, but we should
 *   learn about them through events; so again, no need to fetch more.
 */
export type CaughtUp = {|
  older: boolean,
  newer: boolean,
|};

export type Fetching = {|
  older: boolean,
  newer: boolean,
|};

/**
 * Info about how completely we know the messages in each narrow of
 * MessagesState.
 */
export type CaughtUpState = {|
  [narrow: string]: CaughtUp,
|};

export type FetchingState = {
  [narrow: string]: Fetching,
};

export type FlagsState = {|
  read: { [messageId: number]: boolean },
  starred: { [messageId: number]: boolean },
  collapsed: { [messageId: number]: boolean },
  mentions: { [messageId: number]: boolean },
  wildcard_mentions: { [messageId: number]: boolean },
  summarize_in_home: { [messageId: number]: boolean },
  summarize_in_stream: { [messageId: number]: boolean },
  force_expand: { [messageId: number]: boolean },
  force_collapse: { [messageId: number]: boolean },
  has_alert_word: { [messageId: number]: boolean },
  historical: { [messageId: number]: boolean },
  is_me_message: { [messageId: number]: boolean },
|};

export type FlagName = $Keys<FlagsState>;

export type MigrationsState = {|
  version?: string,
|};

export type LoadingState = {|
  presence: boolean,
  subscriptions: boolean,
  streams: boolean,
  unread: boolean,
  users: boolean,
|};

export type MuteState = MuteTuple[];

export type NavigationRouteState = {
  key: string,
  routeName: string,
  /** The fields in `params` vary by route; see `navActions.js`. */
  params?: {
    narrow?: Narrow,
  },
};

export type NavigationState = {|
  index: number,
  isTransitioning: boolean,
  key: string,
  routes: NavigationRouteState[],
|};

/**
 * State with general info about a Zulip organization; our state subtree `realm`.
 *
 * @prop twentyFourHourTime
 * @prop canCreateStreams
 * @prop crossRealmBots - The server's cross-realm bots; e.g., Welcome Bot.
 *   Cross-realm bots should be treated like normal bots.
 * @prop nonActiveUsers - All users in the organization with `is_active`
 *   false; for normal users, this means they or an admin deactivated their
 *   account.  See `User` and the linked documentation.
 * @prop filters
 * @prop emoji
 * @prop isAdmin
 */
export type RealmState = {|
  twentyFourHourTime: boolean,
  canCreateStreams: boolean,
  crossRealmBots: RealmBot[],
  nonActiveUsers: User[],
  filters: RealmFilter[],
  emoji: RealmEmojiState,
  isAdmin: boolean,
|};

export type TopicsState = {|
  [number]: Topic[],
|};

export type ThemeName = 'default' | 'night';

export type SettingsState = {|
  locale: string,
  theme: ThemeName,
  offlineNotification: boolean,
  onlineNotification: boolean,
  experimentalFeaturesEnabled: boolean,
  streamNotification: boolean,
|};

export type TypingState = {|
  [normalizedRecipients: string]: {
    time: number,
    userIds: number[],
  },
|};

export type UnreadStreamsState = StreamUnreadItem[];
export type UnreadHuddlesState = HuddlesUnreadItem[];
export type UnreadPmsState = PmsUnreadItem[];
export type UnreadMentionsState = number[];

export type AlertWordsState = string[];

export type DraftsState = {|
  [narrow: string]: string,
|};

/**
 * A collection of (almost) all users in the Zulip org; our `users` state subtree.
 *
 * This contains all users except deactivated users and cross-realm bots.
 * For those, see RealmState.
 */
export type UsersState = User[];

export type UserGroupsState = UserGroup[];

/**
 * The `presence` subtree of our Redux state.
 *
 * @prop (email) - Indexes over all users for which the app has received a
 *   presence status.
 */
export type PresenceState = {|
  [email: string]: UserPresence,
|};

export type OutboxState = Outbox[];

export type UnreadState = {|
  streams: UnreadStreamsState,
  huddles: UnreadHuddlesState,
  pms: UnreadPmsState,
  mentions: UnreadMentionsState,
|};

/**
 * Our complete Redux state tree.
 *
 * Each property is a subtree maintained by its own reducer function.
 */
export type GlobalState = {|
  accounts: AccountsState,
  alertWords: AlertWordsState,
  caughtUp: CaughtUpState,
  drafts: DraftsState,
  fetching: FetchingState,
  flags: FlagsState,
  migrations: MigrationsState,
  loading: LoadingState,
  messages: MessagesState,
  mute: MuteState,
  narrows: NarrowsState,
  nav: NavigationState,
  outbox: OutboxState,
  presence: PresenceState,
  realm: RealmState,
  session: SessionState,
  settings: SettingsState,
  streams: StreamsState,
  subscriptions: SubscriptionsState,
  topics: TopicsState,
  typing: TypingState,
  unread: UnreadState,
  userGroups: UserGroupsState,
  userStatus: UserStatusState,
  users: UsersState,
|};

/** A selector returning TResult, with extra parameter TParam. */
// Seems like this should be OutputSelector... but for whatever reason,
// putting that on a selector doesn't cause the result type to propagate to
// the corresponding parameter when used in `createSelector`, and this does.
export type Selector<TResult, TParam = void> = InputSelector<GlobalState, TParam, TResult>;

export type GetState = () => GlobalState;

export type PlainDispatch = <A: Action | NavigateAction>(action: A) => A;

export interface Dispatch {
  <A: Action | NavigateAction>(action: A): A;
  <T>((Dispatch, GetState) => T): T;
}
<commit_msg>types: Sort state-subtree types lexicographically.<commit_after>/**
 * Types describing our Redux state and store.
 *
 * This isn't the place for types that are borrowed from the API;
 * those go under `src/api/` (typically in `src/api/modelTypes.js`)
 * and can be imported here as needed.
 *
 * @flow strict-local
 */

import type { InputSelector } from 'reselect';

import type { Account, Outbox } from './types';
import type { Action, NavigateAction } from './actionTypes';
import type {
  Topic,
  HuddlesUnreadItem,
  Message,
  MuteTuple,
  PmsUnreadItem,
  RealmBot,
  RealmEmojiState,
  RealmFilter,
  Narrow,
  Stream,
  StreamUnreadItem,
  Subscription,
  User,
  UserGroup,
  UserPresence,
  UserStatusMapObject,
} from './api/apiTypes';

import type { SessionState } from './session/sessionReducers';

export type * from './actionTypes';

export type AccountsState = Account[];

export type AlertWordsState = string[];

/**
 * Info about how complete our knowledge is of the messages in some narrow.
 *
 * @prop older - true just if in some fetch we reached the oldest message
 *   in the narrow.  No need to fetch more in that direction.
 * @prop newer - true just if in some fetch we reached the newest message in
 *   the narrow.  Of course their may always be new messages, but we should
 *   learn about them through events; so again, no need to fetch more.
 */
export type CaughtUp = {|
  older: boolean,
  newer: boolean,
|};

/**
 * Info about how completely we know the messages in each narrow of
 * MessagesState.
 */
export type CaughtUpState = {|
  [narrow: string]: CaughtUp,
|};

export type DraftsState = {|
  [narrow: string]: string,
|};

export type Fetching = {|
  older: boolean,
  newer: boolean,
|};

export type FetchingState = {
  [narrow: string]: Fetching,
};

export type FlagsState = {|
  read: { [messageId: number]: boolean },
  starred: { [messageId: number]: boolean },
  collapsed: { [messageId: number]: boolean },
  mentions: { [messageId: number]: boolean },
  wildcard_mentions: { [messageId: number]: boolean },
  summarize_in_home: { [messageId: number]: boolean },
  summarize_in_stream: { [messageId: number]: boolean },
  force_expand: { [messageId: number]: boolean },
  force_collapse: { [messageId: number]: boolean },
  has_alert_word: { [messageId: number]: boolean },
  historical: { [messageId: number]: boolean },
  is_me_message: { [messageId: number]: boolean },
|};

export type FlagName = $Keys<FlagsState>;

export type LoadingState = {|
  presence: boolean,
  subscriptions: boolean,
  streams: boolean,
  unread: boolean,
  users: boolean,
|};

/**
 * A map with all messages we've stored locally, indexed by ID.
 *
 * See also `NarrowsState`, which is an index on this data that identifies
 * messages belonging to a given narrow.
 */
export type MessagesState = {|
  [id: number]: $Exact<Message>,
|};

export type MigrationsState = {|
  version?: string,
|};

export type MuteState = MuteTuple[];

/**
 * An index on `MessagesState`, listing messages in each narrow.
 *
 * Keys are `JSON.stringify`-encoded `Narrow` objects.
 * Values are sorted lists of message IDs.
 *
 * See also `MessagesState`, which stores the message data indexed by ID.
 */
export type NarrowsState = {
  [narrow: string]: number[],
};

export type NavigationRouteState = {
  key: string,
  routeName: string,
  /** The fields in `params` vary by route; see `navActions.js`. */
  params?: {
    narrow?: Narrow,
  },
};

export type NavigationState = {|
  index: number,
  isTransitioning: boolean,
  key: string,
  routes: NavigationRouteState[],
|};

export type OutboxState = Outbox[];

/**
 * The `presence` subtree of our Redux state.
 *
 * @prop (email) - Indexes over all users for which the app has received a
 *   presence status.
 */
export type PresenceState = {|
  [email: string]: UserPresence,
|};

/**
 * State with general info about a Zulip organization; our state subtree `realm`.
 *
 * @prop twentyFourHourTime
 * @prop canCreateStreams
 * @prop crossRealmBots - The server's cross-realm bots; e.g., Welcome Bot.
 *   Cross-realm bots should be treated like normal bots.
 * @prop nonActiveUsers - All users in the organization with `is_active`
 *   false; for normal users, this means they or an admin deactivated their
 *   account.  See `User` and the linked documentation.
 * @prop filters
 * @prop emoji
 * @prop isAdmin
 */
export type RealmState = {|
  twentyFourHourTime: boolean,
  canCreateStreams: boolean,
  crossRealmBots: RealmBot[],
  nonActiveUsers: User[],
  filters: RealmFilter[],
  emoji: RealmEmojiState,
  isAdmin: boolean,
|};

export type ThemeName = 'default' | 'night';

export type SettingsState = {|
  locale: string,
  theme: ThemeName,
  offlineNotification: boolean,
  onlineNotification: boolean,
  experimentalFeaturesEnabled: boolean,
  streamNotification: boolean,
|};

export type StreamsState = Stream[];

export type SubscriptionsState = Subscription[];

export type TopicsState = {|
  [number]: Topic[],
|};

export type TypingState = {|
  [normalizedRecipients: string]: {
    time: number,
    userIds: number[],
  },
|};

export type UnreadStreamsState = StreamUnreadItem[];
export type UnreadHuddlesState = HuddlesUnreadItem[];
export type UnreadPmsState = PmsUnreadItem[];
export type UnreadMentionsState = number[];
export type UnreadState = {|
  streams: UnreadStreamsState,
  huddles: UnreadHuddlesState,
  pms: UnreadPmsState,
  mentions: UnreadMentionsState,
|};

export type UserGroupsState = UserGroup[];

export type UserStatusState = UserStatusMapObject;

/**
 * A collection of (almost) all users in the Zulip org; our `users` state subtree.
 *
 * This contains all users except deactivated users and cross-realm bots.
 * For those, see RealmState.
 */
export type UsersState = User[];

/**
 * Our complete Redux state tree.
 *
 * Each property is a subtree maintained by its own reducer function.
 */
export type GlobalState = {|
  accounts: AccountsState,
  alertWords: AlertWordsState,
  caughtUp: CaughtUpState,
  drafts: DraftsState,
  fetching: FetchingState,
  flags: FlagsState,
  loading: LoadingState,
  messages: MessagesState,
  migrations: MigrationsState,
  mute: MuteState,
  narrows: NarrowsState,
  nav: NavigationState,
  outbox: OutboxState,
  presence: PresenceState,
  realm: RealmState,
  session: SessionState,
  settings: SettingsState,
  streams: StreamsState,
  subscriptions: SubscriptionsState,
  topics: TopicsState,
  typing: TypingState,
  unread: UnreadState,
  userGroups: UserGroupsState,
  userStatus: UserStatusState,
  users: UsersState,
|};

/** A selector returning TResult, with extra parameter TParam. */
// Seems like this should be OutputSelector... but for whatever reason,
// putting that on a selector doesn't cause the result type to propagate to
// the corresponding parameter when used in `createSelector`, and this does.
export type Selector<TResult, TParam = void> = InputSelector<GlobalState, TParam, TResult>;

export type GetState = () => GlobalState;

export type PlainDispatch = <A: Action | NavigateAction>(action: A) => A;

export interface Dispatch {
  <A: Action | NavigateAction>(action: A): A;
  <T>((Dispatch, GetState) => T): T;
}
<|endoftext|>


<commit_before>/**
 * @module Typography
 * @submodule Loading & Displaying
 * @for p5
 * @requires core
 */

'use strict';

var p5 = require('../core/core');
var constants = require('../core/constants');

require('../core/error_helpers');

/**
 * Draws text to the screen. Displays the information specified in the first
 * parameter on the screen in the position specified by the additional
 * parameters. A default font will be used unless a font is set with the
 * textFont() function and a default size will be used unless a font is set
 * with textSize(). Change the color of the text with the fill() function.
 * Change the outline of the text with the stroke() and strokeWeight()
 * functions.
 *

 * The text displays in relation to the textAlign() function, which gives the
 * option to draw to the left, right, and center of the coordinates.
 *

 * The x2 and y2 parameters define a rectangular area to display within and
 * may only be used with string data. When these parameters are specified,
 * they are interpreted based on the current rectMode() setting. Text that
 * does not fit completely within the rectangle specified will not be drawn
 * to the screen.
 *
 * @method text
 * @param {String} str the alphanumeric symbols to be displayed
 * @param {Number} x   x-coordinate of text
 * @param {Number} y   y-coordinate of text
 * @param {Number} x2  by default, the width of the text box,
 *                     see rectMode() for more info
 * @param {Number} y2  by default, the height of the text box,
 *                     see rectMode() for more info
 * @return {Object} this
 * @example
 *

 *
 * textSize(32);
 * text("word", 10, 30);
 * fill(0, 102, 153);
 * text("word", 10, 60);
 * fill(0, 102, 153, 51);
 * text("word", 10, 90);
 *
 *

 *

 *
 * s = "The quick brown fox jumped over the lazy dog.";
 * fill(50);
 * text(s, 10, 10, 70, 80); // Text wraps within text box
 *
 *

 *
 * @alt
 *'word' displayed 3 times going from black, blue to translucent blue
 * The quick brown fox jumped over the lazy dog.
 *
 */
p5.prototype.text = function(str, x, y, maxWidth, maxHeight) {
  return (!(this._renderer._doFill || this._renderer._doStroke)) ? this :
    this._renderer.text.apply(this._renderer, arguments);
};

/**
 * Sets the current font that will be drawn with the text() function.
 *
 * @method textFont
 * @param {Object|String} f a font loaded via loadFont(), or a String
 * representing a web safe font (a font
 * that is generally available across all systems).
 * @return {Object|String} the current font
 * @example
 *

 *
 * fill(0);
 * textSize(12);
 * textFont("Georgia");
 * text("Georgia", 12, 30);
 * textFont("Helvetica");
 * text("Helvetica", 12, 60);
 *
 *

 *

 *
 * var fontRegular, fontItalic, fontBold;
 * function preload() {
 *    fontRegular = loadFont("assets/Regular.otf");
 *    fontItalic = loadFont("assets/Italic.ttf");
 *    fontBold = loadFont("assets/Bold.ttf");
 * }
 * function setup() {
 *    background(210);
 *    fill(0).strokeWeight(0).textSize(10);
 *    textFont(fontRegular);
 *    text("Font Style Normal", 10, 30);
 *    textFont(fontItalic);
 *    text("Font Style Italic", 10, 50);
 *    textFont(fontBold);
 *    text("Font Style Bold", 10, 70);
 * }
 *
 *

 *
 * @alt
 *words Font Style Normal displayed normally, Italic in italic and bold in bold
 *
 */
p5.prototype.textFont = function(theFont, theSize) {

  if (arguments.length) {

    if (!theFont) {

      throw Error('null font passed to textFont');
    }

    this._renderer._setProperty('_textFont', theFont);

    if (theSize) {

      this._renderer._setProperty('_textSize', theSize);
      this._renderer._setProperty('_textLeading',
        theSize * constants._DEFAULT_LEADMULT);
    }

    return this._renderer._applyTextProperties();
  }

  return this._renderer._textFont;
};

module.exports = p5;
<commit_msg>fixed lint error<commit_after>/**
 * @module Typography
 * @submodule Loading & Displaying
 * @for p5
 * @requires core
 */

'use strict';

var p5 = require('../core/core');
var constants = require('../core/constants');

require('../core/error_helpers');

/**
 * Draws text to the screen. Displays the information specified in the first
 * parameter on the screen in the position specified by the additional
 * parameters. A default font will be used unless a font is set with the
 * textFont() function and a default size will be used unless a font is set
 * with textSize(). Change the color of the text with the fill() function.
 * Change the outline of the text with the stroke() and strokeWeight()
 * functions.
 *

 * The text displays in relation to the textAlign() function, which gives the
 * option to draw to the left, right, and center of the coordinates.
 *

 * The x2 and y2 parameters define a rectangular area to display within and
 * may only be used with string data. When these parameters are specified,
 * they are interpreted based on the current rectMode() setting. Text that
 * does not fit completely within the rectangle specified will not be drawn
 * to the screen.
 *
 * @method text
 * @param {String} str the alphanumeric symbols to be displayed
 * @param {Number} x   x-coordinate of text
 * @param {Number} y   y-coordinate of text
 * @param {Number} x2  by default, the width of the text box,
 *                     see rectMode() for more info
 * @param {Number} y2  by default, the height of the text box,
 *                     see rectMode() for more info
 * @return {Object} this
 * @example
 *

 *
 * textSize(32);
 * text("word", 10, 30);
 * fill(0, 102, 153);
 * text("word", 10, 60);
 * fill(0, 102, 153, 51);
 * text("word", 10, 90);
 *
 *

 *

 *
 * s = "The quick brown fox jumped over the lazy dog.";
 * fill(50);
 * text(s, 10, 10, 70, 80); // Text wraps within text box
 *
 *

 *
 * @alt
 *'word' displayed 3 times going from black, blue to translucent blue
 * The quick brown fox jumped over the lazy dog.
 *
 */
p5.prototype.text = function(str, x, y, maxWidth, maxHeight) {
  return (!(this._renderer._doFill || this._renderer._doStroke)) ? this :
    this._renderer.text.apply(this._renderer, arguments);
};

/**
 * Sets the current font that will be drawn with the text() function.
 *
 * @method textFont
 * @param {Object|String} f a font loaded via loadFont(), or a String
 * representing a web safe font (a font
 * that is generally available across all systems)
 * @return {Object|String} the current font
 * @example
 *

 *
 * fill(0);
 * textSize(12);
 * textFont("Georgia");
 * text("Georgia", 12, 30);
 * textFont("Helvetica");
 * text("Helvetica", 12, 60);
 *
 *

 *

 *
 * var fontRegular, fontItalic, fontBold;
 * function preload() {
 *    fontRegular = loadFont("assets/Regular.otf");
 *    fontItalic = loadFont("assets/Italic.ttf");
 *    fontBold = loadFont("assets/Bold.ttf");
 * }
 * function setup() {
 *    background(210);
 *    fill(0).strokeWeight(0).textSize(10);
 *    textFont(fontRegular);
 *    text("Font Style Normal", 10, 30);
 *    textFont(fontItalic);
 *    text("Font Style Italic", 10, 50);
 *    textFont(fontBold);
 *    text("Font Style Bold", 10, 70);
 * }
 *
 *

 *
 * @alt
 *words Font Style Normal displayed normally, Italic in italic and bold in bold
 *
 */
p5.prototype.textFont = function(theFont, theSize) {

  if (arguments.length) {

    if (!theFont) {

      throw Error('null font passed to textFont');
    }

    this._renderer._setProperty('_textFont', theFont);

    if (theSize) {

      this._renderer._setProperty('_textSize', theSize);
      this._renderer._setProperty('_textLeading',
        theSize * constants._DEFAULT_LEADMULT);
    }

    return this._renderer._applyTextProperties();
  }

  return this._renderer._textFont;
};

module.exports = p5;
<|endoftext|>


<commit_before>import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import theme from './theme.css';

class StatusLabel extends PureComponent {
  static propTypes = {
    children: PropTypes.node.isRequired,
    className: PropTypes.string,
    color: PropTypes.oneOf(['neutral', 'mint', 'violet', 'ruby', 'gold', 'aqua']),
    size: PropTypes.oneOf(['small', 'medium']),
  };

  static defaultProps = {
    color: 'neutral',
    size: 'medium',
  };

  render() {
    const { children, className, color, size, ...others } = this.props;

    const classes = cx(theme.label, theme[color], theme[size], className);

    return (

        {children}

    );
  }
}

export default StatusLabel;
<commit_msg>Changed const name to use our naming convention (classNames instead of classes)<commit_after>import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import theme from './theme.css';

class StatusLabel extends PureComponent {
  static propTypes = {
    children: PropTypes.node.isRequired,
    className: PropTypes.string,
    color: PropTypes.oneOf(['neutral', 'mint', 'violet', 'ruby', 'gold', 'aqua']),
    size: PropTypes.oneOf(['small', 'medium']),
  };

  static defaultProps = {
    color: 'neutral',
    size: 'medium',
  };

  render() {
    const { children, className, color, size, ...others } = this.props;

    const classNames = cx(theme.label, theme[color], theme[size], className);

    return (

        {children}

    );
  }
}

export default StatusLabel;
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2016-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.
 *
 * @flow
 */

import type Config from '../config';
import * as constants from '../constants.js';
import * as child from './child.js';
import {registries} from '../resolvers/index.js';
import type {Reporter} from '../reporters/index.js';
import Spinner from '../reporters/console/Spinner.js';

const path = require('path');

export default async function (config: Config, cwd: string, cmds: Array<string>, reporter?: Reporter): Promise<Array<{
  cwd: string,
  command: string,
  stdout: string,
}>> {
  const results = [];

  for (const cmd of cmds) {
    const env = Object.assign({}, process.env);

    // this is used in some places apparently..
    env.npm_execpath = path.join(__dirname, '..', '..', 'bin', 'kpm.js');

    // split up the path
    const pathParts = (env[constants.ENV_PATH_KEY] || '').split(path.delimiter);

    // add node-gyp
    pathParts.unshift(path.join(__dirname, '..', '..', 'bin', 'node-gyp-bin'));

    // add node_modules .bin
    for (const registry of Object.keys(registries)) {
      pathParts.unshift(path.join(cwd, config.registries[registry].folder, '.bin'));
    }

    // join path back together
    env[constants.ENV_PATH_KEY] = pathParts.join(path.delimiter);

    let spinner;
    if (reporter) {
      spinner = reporter.activity();
    }

    // TODO we should pass reporter/stdout to spawn in verbose mode
    let stdout = await child.spawn('sh', ['-c', cmd], {cwd, env});

    if (spinner) {
      spinner.end();
    }

    results.push({cwd, command: cmd, stdout});
  }

  return results;
}
<commit_msg>removed unused<commit_after>/**
 * Copyright (c) 2016-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.
 *
 * @flow
 */

import type Config from '../config';
import * as constants from '../constants.js';
import * as child from './child.js';
import {registries} from '../resolvers/index.js';
import type {Reporter} from '../reporters/index.js';

const path = require('path');

export default async function (config: Config, cwd: string, cmds: Array<string>, reporter?: Reporter): Promise<Array<{
  cwd: string,
  command: string,
  stdout: string,
}>> {
  const results = [];

  for (const cmd of cmds) {
    const env = Object.assign({}, process.env);

    // this is used in some places apparently..
    env.npm_execpath = path.join(__dirname, '..', '..', 'bin', 'kpm.js');

    // split up the path
    const pathParts = (env[constants.ENV_PATH_KEY] || '').split(path.delimiter);

    // add node-gyp
    pathParts.unshift(path.join(__dirname, '..', '..', 'bin', 'node-gyp-bin'));

    // add node_modules .bin
    for (const registry of Object.keys(registries)) {
      pathParts.unshift(path.join(cwd, config.registries[registry].folder, '.bin'));
    }

    // join path back together
    env[constants.ENV_PATH_KEY] = pathParts.join(path.delimiter);

    let spinner;
    if (reporter) {
      spinner = reporter.activity();
    }

    // TODO we should pass reporter/stdout to spawn in verbose mode
    let stdout = await child.spawn('sh', ['-c', cmd], {cwd, env});

    if (spinner) {
      spinner.end();
    }

    results.push({cwd, command: cmd, stdout});
  }

  return results;
}
<|endoftext|>


<commit_before>import notifier from 'node-notifier';
// import stackTrace from 'stack-trace';
import log from 'fancy-log';
import prettyjson from 'prettyjson';

export default (config) => {
    let errorHandler;

    errorHandler = function (error) {
        let errorPrint;

        errorPrint = error;

        if (error.message) {
            // console.log('error', error.stack);

            if (config.notifications) {
                notifier.notify({
                    message: error.message,
                    title: error.name
                });
            }

            errorPrint = {
                message: error.message,
                name: error.name,
                plugin: error.plugin
            };

            /* stack: _.map(stackTrace.parse(error), (crumb) => {
                return crumb.fileName + ':' + crumb.lineNumber + ':' + crumb.columnNumber;
            }) */

            /* eslint-disable no-underscore-dangle */
            if (error._babel && error.codeFrame) {
            /* eslint-enable no-underscore-dangle */
                errorPrint.code = {
                    file: error.fileName + ':' + error.loc.line + ':' + error.loc.column,
                    frame: error.codeFrame
                };

                if (errorPrint.message.indexOf(error.fileName + ': ') === 0) {
                    errorPrint.message = errorPrint.message.substr(error.fileName.length + 2);
                }
            }
        }

        log('\n\n' + prettyjson.render(errorPrint) + '\n');

        this.emit('end');
    };

    return {
        errorHandler
    };
};
<commit_msg>Added stack trace to error output.<commit_after>import _ from 'lodash';
import notifier from 'node-notifier';
import stackTrace from 'stack-trace';
import log from 'fancy-log';
import prettyjson from 'prettyjson';

export default (config) => {
    let errorHandler;

    errorHandler = function (error) {
        let errorPrint;

        errorPrint = error;

        if (error.message) {
            if (config.notifications) {
                notifier.notify({
                    message: error.message,
                    title: error.name
                });
            }

            errorPrint = {
                message: error.message,
                name: error.name,
                plugin: error.plugin
            };

            if (error.stack) {
                errorPrint.stack = _.map(stackTrace.parse(error), (crumb) => {
                    return crumb.fileName + ':' + crumb.lineNumber + ':' + crumb.columnNumber;
                });
            }

            /* eslint-disable no-underscore-dangle */
            if (error._babel && error.codeFrame) {
            /* eslint-enable no-underscore-dangle */
                errorPrint.code = {
                    file: error.fileName + ':' + error.loc.line + ':' + error.loc.column,
                    frame: error.codeFrame
                };

                if (errorPrint.message.indexOf(error.fileName + ': ') === 0) {
                    errorPrint.message = errorPrint.message.substr(error.fileName.length + 2);
                }
            }
        }

        log('\n\n' + prettyjson.render(errorPrint) + '\n');

        this.emit('end');
    };

    return {
        errorHandler
    };
};
<|endoftext|>


<commit_before>import { update, rotate, zoom, setFocus } from 'usco-orbit-controls'
import { computeCameraToFitBounds, cameraOffsetToEntityBoundsCenter } from 'usco-camera-utils'

import { model } from '../modelUtils'
import {animationFrames, rafStream} from '../most/animationFrames'

import mat4 from 'gl-mat4'
import limitFlow from '../most/limitFlow'

export default function controlsStream (interactions, cameraData, focuses$, entityFocuses$, projection$) {
  const {settings, camera} = cameraData
  const {gestures} = interactions

  const rate$ = rafStream() // heartBeat

  /*let i = 0
  var newdiv = document.createElement("DIV")
  newdiv.style.zIndex = 99
  newdiv.style.position = 'absolute'
  newdiv.style.color = 'red'
  newdiv.style.top = '40px'
  let textNode = document.createTextNode("some text"+i)
  newdiv.appendChild(textNode)
  document.body.appendChild(newdiv)*/

  const dragMoves$ = gestures.dragMoves
    // .throttle(16) // FIXME: not sure, could be optimized some more
    .filter(x => x !== undefined)
    .map(function (data) {
      let delta = [data.delta.x, data.delta.y]
      if (data.type === 'touch') {
        // delta = delta.map(x => x / mobileReductor)
      }
      return delta
    })
    .map(function (delta) {
      const angle = [-Math.PI * delta[0], -Math.PI * delta[1]]
      return angle
    })
    .map(x => x.map(y => y * 0.1)) // empirical reduction factor

  const zooms$ = gestures.zooms
    .map(x => -x) // we invert zoom direction
    .startWith(0)
    .filter(x => !isNaN(x))
    .map(x => x * 2.5)
    //.throttle(10)

  // model/ state/ reducers
  function makeCameraModel () {
    function setProjection (state, input) {
      const projection = mat4.perspective([], state.fov, input.width / input.height, // context.viewportWidth / context.viewportHeight,
        state.near,
        state.far)
      //state = Object.assign({}, state, {projection})
      state.projection = projection
      state.aspect = input.width / input.height
      //state = Object.assign({}, state, update(settings, state)) // not sure
      return state
    }

    function applyRotation (state, angles) {
      //textNode.nodeValue= 'applyRotation'
      state = rotate(settings, state, angles) // mutating, meh
      return state
    }

    function applyZoom (state, zooms) {
      state = zoom(settings, state, zooms) // mutating, meh
      return state
    }

    function applyFocusOn (state, focuses) {
      state = setFocus(settings, state, focuses) // mutating, meh
      return state
    }

    function zoomToFit (state, input) {
      //console.log('zoomToFit', state.position, state.target,  input)
      let camera = state
      const {bounds, transforms} = input
      const offsetTargetAndPosition = cameraOffsetToEntityBoundsCenter({camera, bounds, transforms, axis: 2})
      camera = Object.assign({}, state, offsetTargetAndPosition)
      const phase2 = computeCameraToFitBounds({camera, bounds, transforms})
      state.targetTgt = phase2.target
      state.positionTgt = phase2.position
      return state
    }

    //this is used for 'continuous updates' for things like spin effects, autoRotate etc
    function updateState (state) {
      //i++
      //textNode.nodeValue= 'foo'+i
      //return state
      return Object.assign({}, state, update(settings, state))
    }

    const updateFunctions = {
      setProjection,
      applyZoom,
      applyRotation,
      //applyFocusOn,
      zoomToFit,
      updateState
    }
    const actions = {
      setProjection: projection$,
      applyZoom: zooms$,
      applyRotation: dragMoves$,
      //applyFocusOn: focuses$,
      zoomToFit: entityFocuses$,
      updateState: rate$
    }

    const cameraState$ = model(camera, actions, updateFunctions)

    return cameraState$
  }

  const cameraState$ = makeCameraModel()

  return cameraState$
    .thru(limitFlow(33))
  /*return rate$.sample(x => x,
    cameraState$
      .filter(x => x.changed)
      .merge(cameraState$.take(1))
  )*/
}
<commit_msg>feat(controlsStream): experimenting with tweaks to computeCamerToFitBounds etc<commit_after>import { update, rotate, zoom, setFocus } from 'usco-orbit-controls'
import { computeCameraToFitBounds, cameraOffsetToEntityBoundsCenter } from 'usco-camera-utils'

import { model } from '../modelUtils'
import {animationFrames, rafStream} from '../most/animationFrames'

import mat4 from 'gl-mat4'
import limitFlow from '../most/limitFlow'

export default function controlsStream (interactions, cameraData, focuses$, entityFocuses$, projection$) {
  let {settings, camera} = cameraData
  const {gestures} = interactions

  const rate$ = rafStream() // heartBeat

  /*let i = 0
  var newdiv = document.createElement("DIV")
  newdiv.style.zIndex = 99
  newdiv.style.position = 'absolute'
  newdiv.style.color = 'red'
  newdiv.style.top = '40px'
  let textNode = document.createTextNode("some text"+i)
  newdiv.appendChild(textNode)
  document.body.appendChild(newdiv)*/

  const dragMoves$ = gestures.dragMoves
    // .throttle(16) // FIXME: not sure, could be optimized some more
    .filter(x => x !== undefined)
    .map(function (data) {
      let delta = [data.delta.x, data.delta.y]
      if (data.type === 'touch') {
        // delta = delta.map(x => x / mobileReductor)
      }
      return delta
    })
    .map(function (delta) {
      const angle = [-Math.PI * delta[0], -Math.PI * delta[1]]
      return angle
    })
    .map(x => x.map(y => y * 0.1)) // empirical reduction factor

  const zooms$ = gestures.zooms
    .map(x => -x) // we invert zoom direction
    .startWith(0)
    .filter(x => !isNaN(x))
    .map(x => x * 2.5)
    //.throttle(10)

  // model/ state/ reducers
  function makeCameraModel () {
    function setProjection (state, input) {
      const projection = mat4.perspective([], state.fov, input.width / input.height, // context.viewportWidth / context.viewportHeight,
        state.near,
        state.far)
      //state = Object.assign({}, state, {projection})
      state.projection = projection
      state.aspect = input.width / input.height
      //state = Object.assign({}, state, update(settings, state)) // not sure
      return state
    }

    function applyRotation (state, angles) {
      //textNode.nodeValue= 'applyRotation'
      state = rotate(settings, state, angles) // mutating, meh
      return state
    }

    function applyZoom (state, zooms) {
      state = zoom(settings, state, zooms)//, {adjustPlanes: true}) // mutating, meh
      return state
    }

    function applyFocusOn (state, focuses) {
      state = setFocus(settings, state, focuses) // mutating, meh
      return state
    }

    function zoomToFit (state, input) {
      //console.log('zoomToFit', state.position, state.target,  input)
      let camera = state
      const {bounds, transforms} = input
      const offsetTargetAndPosition = cameraOffsetToEntityBoundsCenter({camera, bounds, transforms, axis: 2})
      camera = Object.assign({}, state, offsetTargetAndPosition)
      const phase2 = computeCameraToFitBounds({camera, bounds, transforms})
      state.targetTgt = phase2.target
      state.positionTgt = phase2.position
      return state
      //FIXME: just a test for zoomToFit for now
      //console.log('zoomToFit', state.position, state.target,  input)
      /*camera.near = 0.01
      settings.limits.minDistance = 0
      let camera = state
      const {bounds, transforms} = input
      camera = Object.assign({}, state, cameraOffsetToEntityBoundsCenter({camera, bounds, transforms, axis: 2}))
      camera = Object.assign({}, state, computeCameraToFitBounds({camera, bounds, transforms}))
      return camera*/
    }

    //this is used for 'continuous updates' for things like spin effects, autoRotate etc
    function updateState (state) {
      //i++
      //textNode.nodeValue= 'foo'+i
      //return state
      return Object.assign({}, state, update(settings, state))
    }

    const updateFunctions = {
      setProjection,
      applyZoom,
      applyRotation,
      //applyFocusOn,
      zoomToFit,
      updateState
    }
    const actions = {
      setProjection: projection$,
      applyZoom: zooms$,
      applyRotation: dragMoves$,
      //applyFocusOn: focuses$,
      zoomToFit: entityFocuses$,
      updateState: rate$
    }

    const cameraState$ = model(camera, actions, updateFunctions)

    return cameraState$
  }

  const cameraState$ = makeCameraModel()

  return cameraState$
    .thru(limitFlow(33))
  /*return rate$.sample(x => x,
    cameraState$
      .filter(x => x.changed)
      .merge(cameraState$.take(1))
  )*/
}
<|endoftext|>


<commit_before>/**
 * Option widget with an option icon and indicator.
 *
 * Use together with OO.ui.SelectWidget.
 *
 * @class
 * @extends OO.ui.OptionWidget
 * @mixins OO.ui.IconElement
 * @mixins OO.ui.IndicatorElement
 *
 * @constructor
 * @param {Object} [config] Configuration options
 */
OO.ui.DecoratedOptionWidget = function OoUiDecoratedOptionWidget( config ) {
	// Parent constructor
	OO.ui.DecoratedOptionWidget.super.call( this, config );

	// Mixin constructors
	OO.ui.IconElement.call( this, config );
	OO.ui.IndicatorElement.call( this, config );

	// Initialization
	this.$element
		.addClass( 'oo-ui-decoratedOptionWidget' )
		.prepend( this.$icon )
		.append( this.$indicator );
};

/* Setup */

OO.inheritClass( OO.ui.DecoratedOptionWidget, OO.ui.OptionWidget );
OO.mixinClass( OO.ui.OptionWidget, OO.ui.IconElement );
OO.mixinClass( OO.ui.OptionWidget, OO.ui.IndicatorElement );
<commit_msg>DecoratedOptionWidget: Add description and example<commit_after>/**
 * DecoratedOptionWidgets are {@link OO.ui.OptionWidget options} that can be configured
 * with an {@link OO.ui.IconElement icon} and/or {@link OO.ui.IndicatorElement indicator}.
 * This class is used with OO.ui.SelectWidget to create a selection of mutually exclusive
 * options. For more information about options and selects, please see the
 * [OOjs UI documentation on MediaWiki][1].
 *
 *     @example
 *     // Decorated options in a select widget
 *     var select=new OO.ui.SelectWidget( {
 *         items: [
 *             new OO.ui.DecoratedOptionWidget( {
 *                 data: 'a',
 *                 label: 'Option with icon',
 *                 icon: 'help'
 *             } ),
 *             new OO.ui.DecoratedOptionWidget( {
 *                 data: 'b',
 *                 label: 'Option with indicator',
 *                 indicator: 'next'
 *             } )
 *         ]
 *     } );
 *     $('body').append(select.$element);
 *
 * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
 *
 * @class
 * @extends OO.ui.OptionWidget
 * @mixins OO.ui.IconElement
 * @mixins OO.ui.IndicatorElement
 *
 * @constructor
 * @param {Object} [config] Configuration options
 */
OO.ui.DecoratedOptionWidget = function OoUiDecoratedOptionWidget( config ) {
	// Parent constructor
	OO.ui.DecoratedOptionWidget.super.call( this, config );

	// Mixin constructors
	OO.ui.IconElement.call( this, config );
	OO.ui.IndicatorElement.call( this, config );

	// Initialization
	this.$element
		.addClass( 'oo-ui-decoratedOptionWidget' )
		.prepend( this.$icon )
		.append( this.$indicator );
};

/* Setup */

OO.inheritClass( OO.ui.DecoratedOptionWidget, OO.ui.OptionWidget );
OO.mixinClass( OO.ui.OptionWidget, OO.ui.IconElement );
OO.mixinClass( OO.ui.OptionWidget, OO.ui.IndicatorElement );
<|endoftext|>


<commit_before>define([
    'vellum/form',
    'jquery',
    'underscore',
    'vellum/mugs',
    'vellum/parser',
    'vellum/tree',
    'vellum/util',
    'vellum/widgets',
    'tpl!vellum/templates/widget_update_case',
    'vellum/core'
], function (
    form_,
    $,
    _,
    mugs,
    parser,
    Tree,
    util,
    widgets,
    widget_update_case
){
    function createsCase(mug) {
        return mug ? mug.p.use_create : false;
    }

    function closesCase(mug) {
        return mug ? mug.p.use_close : false;
    }

    function updatesCase(mug) {
        return mug ? mug.p.use_update : false;
    }

    function addSetValue(mug) {
        var path = mug.absolutePath;

        if (createsCase(mug)) {
            mug.form.addSetValue('xforms-ready', path + "/case/@case_id", mug.p.case_id);
        }
    }

    var saveCasePropWidget = function (mug, options) {
        var widget = widgets.normal(mug, options),
            id = options.id;

        // todo make a style for this when vellum gets a facelift
        widget.kvInput = $('
').attr('name', id);

        widget.getControl = function () {
            return widget.kvInput;
        };

        widget.setValue = function (value) {
            value = _.isUndefined(value) ? {} : value;
            widget.kvInput.html(widget_update_case({
                props: value
            }));
            widget.kvInput.find('input').bind('change keyup', function () {
                widget.handleChange();
            });
            widget.kvInput.find('.fd-add-update-property').click(function (e) {
                widget.refreshControl();
                e.preventDefault();
            });
            widget.kvInput.find('.fd-remove-update-property').click(function (e) {
                $(this).parent().parent().parent().remove();
                widget.refreshControl();
                widget.save();
                e.preventDefault();
            });
        };

        widget.getValue = function () {
            var currentValues = {};
            _.each(widget.kvInput.find('.fd-update-property'), function (kvPair) {
                var $pair = $(kvPair);
                currentValues[$pair.find('.fd-update-property-name').val()] = {
                    calculate: $pair.find('.fd-update-property-source').val(),
                    relevant: $pair.find('.fd-update-property-relevant').val(),
                };
            });
            return currentValues;
        };

        widget.updateValue = function () {
            var currentValues = widget.getValue();
            if (!("" in currentValues)) {
                widget.kvInput.find('.btn').removeClass('hide');
                widget.kvInput.find('.fd-remove-update-property').removeClass('hide');
            }
            widget.save();
        };

        widget.refreshControl = function () {
            widget.setValue(widget.getValue());
        };

        return widget;
    };

    var CASE_XMLNS = "http://commcarehq.org/case/transaction/v2",
        saveToCaseMugOptions = {
            typeName: 'Save to Case',
            isTypeChangeable: false,
            isDataOnly: true,
            supportsDataNodeRole: true,
            icon: 'icon-save',
            init: function (mug, form) {},
            spec: {
                xmlnsAttr: { presence: "optional" },
                "date_modified": {
                    lstring: "Date modified",
                    visibility: 'visible',
                    presence: 'required',
                    widget: widgets.xPath,
                    validationFunc: function (mug) {
                        if (mug.p.date_modified === "") {
                            return "Date Modified is required";
                        }
                        return 'pass';
                    }
                },
                "user_id": {
                    lstring: "User ID",
                    visibility: 'visible',
                    presence: 'optional',
                    widget: widgets.xPath
                },
                "case_id": {
                    lstring: "Case ID",
                    visibility: 'visible',
                    presence: 'optional',
                    widget: widgets.xPath,
                    validationFunc: function (mug) {
                        if (mug.p.case_id === "") {
                            return "Case ID is required";
                        }
                        return 'pass';
                    }
                },
                "use_create": {
                    lstring: "Create Case",
                    visibility: 'visible',
                    presence: 'optional',
                    widget: widgets.checkbox
                },
                "create_property": {
                    lstring: "Properties To Create",
                    visibility: 'visible',
                    presence: 'optional',
                    widget: saveCasePropWidget,
                    validationFunc: function (mug) {
                        if (mug.p.use_create) {
                            var props = _.without(_.keys(mug.p.create_property), ""),
                                required = ["case_type", "case_name"],
                                optional = ["owner_id"],
                                legal = _.union(required, optional),
                                illegalProps = _.difference(props, legal),
                                requiredProps = _.intersection(props, required),
                                invalidProps = _.filter(props, function(p) {
                                    return !/[a-z_]*/.test(p);
                                });

                            if (requiredProps.length !== required.length) {
                                return "You must include " +
                                    required.join(", ") +
                                    " columns to create a case";
                            } else if (illegalProps.length > 0) {
                                return "You can only use the following properties: " +
                                    legal.join(', ');
                            } else if (invalidProps.length > 0) {
                                return invalidProps.join(", ") +
                                    " are invalid properties";
                            }

                        }
                        return 'pass';
                    }
                },
                "use_close": {
                    lstring: "Close Case",
                    visibility: 'visible',
                    presence: 'optional',
                    widget: widgets.checkbox
                },
                "close_condition": {
                    lstring: "Close Condition",
                    visibility: 'visible',
                    presence: 'optional',
                    widget: widgets.xPath
                },
                "use_update": {
                    lstring: "Update Case",
                    visibility: 'visible',
                    presence: 'optional',
                    widget: widgets.checkbox
                },
                "update_property": {
                    lstring: "Properties To Update",
                    visibility: 'visible',
                    presence: 'optional',
                    widget: saveCasePropWidget,
                    validationFunc: function (mug) {
                        if (mug.p.use_update) {
                            var props = _.without(_.keys(mug.p.update_property), ""),
                                invalidProps = _.filter(props, function(p) {
                                    return !/[a-z_]*/.test(p);
                                });

                            if (invalidProps.length > 0) {
                                return invalidProps.join(", ") +
                                    " are invalid properties";
                            }
                        }
                        return 'pass';
                    }
                }
            },
            getExtraDataAttributes: function (mug) {
                addSetValue(mug);
                return {
                    "vellum:role": "SaveToCase"
                };
            },
            dataChildFilter: function (children, mug) {
                function simpleNode(name, children, dataValue) {
                    children = children ? children : [];
                    var node = new Tree.Node(children, {
                        getNodeID: function () { return name; },
                        p: {
                            rawDataAttributes: null,
                            dataValue: dataValue
                        },
                        options: { }
                    });
                    return node;
                }

                function makeColumns(properties) {
                    return _.map(properties, function(v, k) {
                        if (k) {
                            return simpleNode(k);
                        }
                    });
                }

                var actions = [];
                if (createsCase(mug)) {
                    actions.push(simpleNode('create', makeColumns(mug.p.create_property)));
                }

                if (updatesCase(mug)) {
                    actions.push(simpleNode('update', makeColumns(mug.p.update_property)));
                }

                if (closesCase(mug)) {
                    actions.push(simpleNode('close'));
                }

                return [new Tree.Node(actions, {
                    getNodeID: function () { return "c:case"; },
                    p: {rawDataAttributes: null},
                    options: {
                        getExtraDataAttributes: function (mug) {
                            return {
                                "xmlns:c": CASE_XMLNS,
                                case_id: '',
                                date_modified: '',
                                user_id: '',
                            };
                        }
                    }
                })];
            },
            getBindList: function (mug) {
                var ret = [];
                function generateBinds(action, properties) {
                    return _.map(properties, function(v, k) {
                        return {
                            nodeset: mug.absolutePath + "/case/" + action + "/" + k,
                            calculate: v.calculate,
                            relevant: v.relevant
                        };
                    });
                }

                if (createsCase(mug)) {
                    ret = ret.concat(generateBinds('create', mug.p.create_property));
                }
                if (updatesCase(mug)) {
                    ret = ret.concat(generateBinds('update', mug.p.update_property));
                }
                if (closesCase(mug)) {
                    ret.push({
                        nodeset: mug.absolutePath + "/case/close",
                        relevant: mug.p.close_condition
                    });
                }

                if (createsCase(mug) || updatesCase(mug) || closesCase(mug)) {
                    ret.push({
                        nodeset: mug.absolutePath + "/case/@date_modified",
                        calculate: mug.p.date_modified,
                        type: "xsd:dateTime"
                    });
                    ret.push({
                        nodeset: mug.absolutePath + "/case/@user_id",
                        calculate: mug.p.user_id
                    });

                    if (!createsCase(mug)) {
                        ret.push({
                            nodeset: mug.absolutePath + "/case/@case_id",
                            calculate: mug.p.case_id
                        });
                    }
                }
                return ret;
            },
            parseDataNode: function (mug, $node) {
                var case_ = $node.children(),
                    create = case_.find('create'),
                    close = case_.find('close'),
                    update = case_.find('update');
                if (create && create.length !== 0) {
                    mug.p.use_create = true;
                }
                if (update && update.length !== 0) {
                    mug.p.use_update = true;
                }
                if (close && close.length !== 0) {
                    mug.p.use_close = true;
                }
                return $([]);
            }
        },
        sectionData = {
            SaveToCase: [
                {
                    slug: "main",
                    displayName: "Basic",
                    properties: [
                        "nodeID",
                        "date_modified",
                        "user_id",
                        "case_id",
                    ],
                },
                {
                    slug: "create",
                    displayName: "Create",
                    properties: [
                        "use_create",
                        "create_property",
                    ],
                    isCollapsed: function (mug) {
                        return !createsCase(mug);
                    },
                },
                {
                    slug: "update",
                    displayName: "Update",
                    properties: [
                        "use_update",
                        "update_property",
                    ],
                    isCollapsed: function (mug) {
                        return !updatesCase(mug);
                    },
                },
                {
                    slug: "close",
                    displayName: "Close",
                    properties: [
                        "use_close",
                        "close_condition",
                    ],
                    isCollapsed: function (mug) {
                        return !closesCase(mug);
                    },
                }
            ]
        };

    $.vellum.plugin("saveToCase", {}, {
        getAdvancedQuestions: function () {
            return this.__callOld().concat(["SaveToCase"]);
        },
        handleMugParseFinish: function (mug) {
            this.__callOld();
            // cases that are created use a setvalue for case_id
            if (!createsCase(mug)) {
                return;
            }
            var values = _.object(_.map(mug.form.getSetValues(), function (value) {
                    return [value.ref, value];
                }));

            _.each(values, function(value) {
                if (/case\/@case_id$/.test(value.ref)) {
                    mug.p.case_id = value.value;
                }
            });
        },
        getMugTypes: function () {
            var types = this.__callOld();
            types.normal.SaveToCase = util.extend(
                mugs.defaultOptions, saveToCaseMugOptions);
            return types;
        },
        getSections: function (mug) {
            if (sectionData.hasOwnProperty(mug.__className)) {
                return _.map(sectionData[mug.__className], function (section) {
                    var tmpSection = _.clone(section);
                    if (_.isFunction(tmpSection.isCollapsed)) {
                        tmpSection.isCollapsed = tmpSection.isCollapsed(mug);
                    }
                    return tmpSection;
                });
            }
            return this.__callOld();
        },
        parseBindElement: function (form, el, path) {
            var mug = form.getMugByPath(path);
            if (!mug) {
                var basePath = path.replace(/\/case\/[\w_]*\/[\w_]*$/, "");
                if (path !== basePath) {
                    mug = form.getMugByPath(basePath);
                    if (mug.__className === "SaveToCase") {
                        var matchRet = path.match(/\/case\/(create|update)\/([\w_]*)$/),
                            action = matchRet[1],
                            prop = matchRet[2],
                            pKey;

                        if (action === "create") {
                            pKey = 'create_property';
                        } else if (action === "update") {
                            pKey = 'update_property';
                        }

                        if (!mug.p[pKey]) {
                            mug.p[pKey] = {};
                        }
                        mug.p[pKey][prop] = {
                            calculate: el.attr("calculate"),
                            relevant: el.attr("relevant")
                        };
                        return;
                    }
                }

                var validAttrs = [
                    { attr: 'close', mugProp: 'close_condition', elAttr: 'relevant' },
                    { attr: '@date_modified', mugProp: 'date_modified', elAttr: 'calculate' },
                    { attr: '@user_id', mugProp: 'user_id', elAttr: 'calculate' },
                    { attr: '@case_id', mugProp: 'case_id', elAttr: 'calculate' },
                ];

                var attr = _.find(validAttrs, function (v) {
                    var re = new RegExp("/case/" + v.attr + "$"),
                        tmpPath = path.replace(re, "");
                    if (path !== tmpPath) {
                        mug = form.getMugByPath(tmpPath);
                        if (mug.__className === "SaveToCase") {
                            return true;
                        }
                    }
                    return false;
                });

                if (attr) {
                    mug.p[attr.mugProp] = el.attr(attr.elAttr);
                    return;
                }
            }
            this.__callOld();
        }
    });
});
<commit_msg>Generic property widget<commit_after>define([
    'vellum/form',
    'jquery',
    'underscore',
    'vellum/mugs',
    'vellum/parser',
    'vellum/tree',
    'vellum/util',
    'vellum/widgets',
    'tpl!vellum/templates/widget_update_case',
    'vellum/core'
], function (
    form_,
    $,
    _,
    mugs,
    parser,
    Tree,
    util,
    widgets,
    widget_update_case
){
    function createsCase(mug) {
        return mug ? mug.p.use_create : false;
    }

    function closesCase(mug) {
        return mug ? mug.p.use_close : false;
    }

    function updatesCase(mug) {
        return mug ? mug.p.use_update : false;
    }

    function addSetValue(mug) {
        var path = mug.absolutePath;

        if (createsCase(mug)) {
            mug.form.addSetValue('xforms-ready', path + "/case/@case_id", mug.p.case_id);
        }
    }

    var propertyWidget = function (mug, options) {
            var widget = widgets.normal(mug, options),
                id = options.id;

            widget.kvInput = $('
').attr('name', id);

            widget.getControl = function () {
                return widget.kvInput;
            };

            widget.refreshControl = function () {
                widget.setValue(widget.getValue());
            };

            return widget;
        },
        saveCasePropWidget = function (mug, options) {
            var widget = propertyWidget(mug, options);

            widget.setValue = function (value) {
                value = _.isUndefined(value) ? {} : value;
                widget.kvInput.html(widget_update_case({
                    props: value
                }));
                widget.kvInput.find('input').bind('change keyup', function () {
                    widget.handleChange();
                });
                widget.kvInput.find('.fd-add-update-property').click(function (e) {
                    widget.refreshControl();
                    e.preventDefault();
                });
                widget.kvInput.find('.fd-remove-update-property').click(function (e) {
                    $(this).parent().parent().parent().remove();
                    widget.refreshControl();
                    widget.save();
                    e.preventDefault();
                });
            };

            widget.getValue = function () {
                var currentValues = {};
                _.each(widget.kvInput.find('.fd-update-property'), function (kvPair) {
                    var $pair = $(kvPair);
                    currentValues[$pair.find('.fd-update-property-name').val()] = {
                        calculate: $pair.find('.fd-update-property-source').val(),
                        relevant: $pair.find('.fd-update-property-relevant').val(),
                    };
                });
                return currentValues;
            };

            return widget;
        };

    var CASE_XMLNS = "http://commcarehq.org/case/transaction/v2",
        saveToCaseMugOptions = {
            typeName: 'Save to Case',
            isTypeChangeable: false,
            isDataOnly: true,
            supportsDataNodeRole: true,
            icon: 'icon-save',
            init: function (mug, form) {},
            spec: {
                xmlnsAttr: { presence: "optional" },
                "date_modified": {
                    lstring: "Date modified",
                    visibility: 'visible',
                    presence: 'required',
                    widget: widgets.xPath,
                    validationFunc: function (mug) {
                        if (mug.p.date_modified === "") {
                            return "Date Modified is required";
                        }
                        return 'pass';
                    }
                },
                "user_id": {
                    lstring: "User ID",
                    visibility: 'visible',
                    presence: 'optional',
                    widget: widgets.xPath
                },
                "case_id": {
                    lstring: "Case ID",
                    visibility: 'visible',
                    presence: 'optional',
                    widget: widgets.xPath,
                    validationFunc: function (mug) {
                        if (mug.p.case_id === "") {
                            return "Case ID is required";
                        }
                        return 'pass';
                    }
                },
                "use_create": {
                    lstring: "Create Case",
                    visibility: 'visible',
                    presence: 'optional',
                    widget: widgets.checkbox
                },
                "create_property": {
                    lstring: "Properties To Create",
                    visibility: 'visible',
                    presence: 'optional',
                    widget: saveCasePropWidget,
                    validationFunc: function (mug) {
                        if (mug.p.use_create) {
                            var props = _.without(_.keys(mug.p.create_property), ""),
                                required = ["case_type", "case_name"],
                                optional = ["owner_id"],
                                legal = _.union(required, optional),
                                illegalProps = _.difference(props, legal),
                                requiredProps = _.intersection(props, required),
                                invalidProps = _.filter(props, function(p) {
                                    return !/[a-z_]*/.test(p);
                                });

                            if (requiredProps.length !== required.length) {
                                return "You must include " +
                                    required.join(", ") +
                                    " columns to create a case";
                            } else if (illegalProps.length > 0) {
                                return "You can only use the following properties: " +
                                    legal.join(', ');
                            } else if (invalidProps.length > 0) {
                                return invalidProps.join(", ") +
                                    " are invalid properties";
                            }

                        }
                        return 'pass';
                    }
                },
                "use_close": {
                    lstring: "Close Case",
                    visibility: 'visible',
                    presence: 'optional',
                    widget: widgets.checkbox
                },
                "close_condition": {
                    lstring: "Close Condition",
                    visibility: 'visible',
                    presence: 'optional',
                    widget: widgets.xPath
                },
                "use_update": {
                    lstring: "Update Case",
                    visibility: 'visible',
                    presence: 'optional',
                    widget: widgets.checkbox
                },
                "update_property": {
                    lstring: "Properties To Update",
                    visibility: 'visible',
                    presence: 'optional',
                    widget: saveCasePropWidget,
                    validationFunc: function (mug) {
                        if (mug.p.use_update) {
                            var props = _.without(_.keys(mug.p.update_property), ""),
                                invalidProps = _.filter(props, function(p) {
                                    return !/[a-z_]*/.test(p);
                                });

                            if (invalidProps.length > 0) {
                                return invalidProps.join(", ") +
                                    " are invalid properties";
                            }
                        }
                        return 'pass';
                    }
                }
            },
            getExtraDataAttributes: function (mug) {
                addSetValue(mug);
                return {
                    "vellum:role": "SaveToCase"
                };
            },
            dataChildFilter: function (children, mug) {
                function simpleNode(name, children, dataValue) {
                    children = children ? children : [];
                    var node = new Tree.Node(children, {
                        getNodeID: function () { return name; },
                        p: {
                            rawDataAttributes: null,
                            dataValue: dataValue
                        },
                        options: { }
                    });
                    return node;
                }

                function makeColumns(properties) {
                    return _.map(properties, function(v, k) {
                        if (k) {
                            return simpleNode(k);
                        }
                    });
                }

                var actions = [];
                if (createsCase(mug)) {
                    actions.push(simpleNode('create', makeColumns(mug.p.create_property)));
                }

                if (updatesCase(mug)) {
                    actions.push(simpleNode('update', makeColumns(mug.p.update_property)));
                }

                if (closesCase(mug)) {
                    actions.push(simpleNode('close'));
                }

                return [new Tree.Node(actions, {
                    getNodeID: function () { return "c:case"; },
                    p: {rawDataAttributes: null},
                    options: {
                        getExtraDataAttributes: function (mug) {
                            return {
                                "xmlns:c": CASE_XMLNS,
                                case_id: '',
                                date_modified: '',
                                user_id: '',
                            };
                        }
                    }
                })];
            },
            getBindList: function (mug) {
                var ret = [];
                function generateBinds(action, properties) {
                    return _.map(properties, function(v, k) {
                        return {
                            nodeset: mug.absolutePath + "/case/" + action + "/" + k,
                            calculate: v.calculate,
                            relevant: v.relevant
                        };
                    });
                }

                if (createsCase(mug)) {
                    ret = ret.concat(generateBinds('create', mug.p.create_property));
                }
                if (updatesCase(mug)) {
                    ret = ret.concat(generateBinds('update', mug.p.update_property));
                }
                if (closesCase(mug)) {
                    ret.push({
                        nodeset: mug.absolutePath + "/case/close",
                        relevant: mug.p.close_condition
                    });
                }

                if (createsCase(mug) || updatesCase(mug) || closesCase(mug)) {
                    ret.push({
                        nodeset: mug.absolutePath + "/case/@date_modified",
                        calculate: mug.p.date_modified,
                        type: "xsd:dateTime"
                    });
                    ret.push({
                        nodeset: mug.absolutePath + "/case/@user_id",
                        calculate: mug.p.user_id
                    });

                    if (!createsCase(mug)) {
                        ret.push({
                            nodeset: mug.absolutePath + "/case/@case_id",
                            calculate: mug.p.case_id
                        });
                    }
                }
                return ret;
            },
            parseDataNode: function (mug, $node) {
                var case_ = $node.children(),
                    create = case_.find('create'),
                    close = case_.find('close'),
                    update = case_.find('update');
                if (create && create.length !== 0) {
                    mug.p.use_create = true;
                }
                if (update && update.length !== 0) {
                    mug.p.use_update = true;
                }
                if (close && close.length !== 0) {
                    mug.p.use_close = true;
                }
                return $([]);
            }
        },
        sectionData = {
            SaveToCase: [
                {
                    slug: "main",
                    displayName: "Basic",
                    properties: [
                        "nodeID",
                        "date_modified",
                        "user_id",
                        "case_id",
                    ],
                },
                {
                    slug: "create",
                    displayName: "Create",
                    properties: [
                        "use_create",
                        "create_property",
                    ],
                    isCollapsed: function (mug) {
                        return !createsCase(mug);
                    },
                },
                {
                    slug: "update",
                    displayName: "Update",
                    properties: [
                        "use_update",
                        "update_property",
                    ],
                    isCollapsed: function (mug) {
                        return !updatesCase(mug);
                    },
                },
                {
                    slug: "close",
                    displayName: "Close",
                    properties: [
                        "use_close",
                        "close_condition",
                    ],
                    isCollapsed: function (mug) {
                        return !closesCase(mug);
                    },
                }
            ]
        };

    $.vellum.plugin("saveToCase", {}, {
        getAdvancedQuestions: function () {
            return this.__callOld().concat(["SaveToCase"]);
        },
        handleMugParseFinish: function (mug) {
            this.__callOld();
            // cases that are created use a setvalue for case_id
            if (!createsCase(mug)) {
                return;
            }
            var values = _.object(_.map(mug.form.getSetValues(), function (value) {
                    return [value.ref, value];
                }));

            _.each(values, function(value) {
                if (/case\/@case_id$/.test(value.ref)) {
                    mug.p.case_id = value.value;
                }
            });
        },
        getMugTypes: function () {
            var types = this.__callOld();
            types.normal.SaveToCase = util.extend(
                mugs.defaultOptions, saveToCaseMugOptions);
            return types;
        },
        getSections: function (mug) {
            if (sectionData.hasOwnProperty(mug.__className)) {
                return _.map(sectionData[mug.__className], function (section) {
                    var tmpSection = _.clone(section);
                    if (_.isFunction(tmpSection.isCollapsed)) {
                        tmpSection.isCollapsed = tmpSection.isCollapsed(mug);
                    }
                    return tmpSection;
                });
            }
            return this.__callOld();
        },
        parseBindElement: function (form, el, path) {
            var mug = form.getMugByPath(path);
            if (!mug) {
                var basePath = path.replace(/\/case\/[\w_]*\/[\w_]*$/, "");
                if (path !== basePath) {
                    mug = form.getMugByPath(basePath);
                    if (mug.__className === "SaveToCase") {
                        var matchRet = path.match(/\/case\/(create|update)\/([\w_]*)$/),
                            action = matchRet[1],
                            prop = matchRet[2],
                            pKey;

                        if (action === "create") {
                            pKey = 'create_property';
                        } else if (action === "update") {
                            pKey = 'update_property';
                        }

                        if (!mug.p[pKey]) {
                            mug.p[pKey] = {};
                        }
                        mug.p[pKey][prop] = {
                            calculate: el.attr("calculate"),
                            relevant: el.attr("relevant")
                        };
                        return;
                    }
                }

                var validAttrs = [
                    { attr: 'close', mugProp: 'close_condition', elAttr: 'relevant' },
                    { attr: '@date_modified', mugProp: 'date_modified', elAttr: 'calculate' },
                    { attr: '@user_id', mugProp: 'user_id', elAttr: 'calculate' },
                    { attr: '@case_id', mugProp: 'case_id', elAttr: 'calculate' },
                ];

                var attr = _.find(validAttrs, function (v) {
                    var re = new RegExp("/case/" + v.attr + "$"),
                        tmpPath = path.replace(re, "");
                    if (path !== tmpPath) {
                        mug = form.getMugByPath(tmpPath);
                        if (mug.__className === "SaveToCase") {
                            return true;
                        }
                    }
                    return false;
                });

                if (attr) {
                    mug.p[attr.mugProp] = el.attr(attr.elAttr);
                    return;
                }
            }
            this.__callOld();
        }
    });
});
<|endoftext|>