<commit_before>'use strict';

var replace     = String.prototype.replace
  , toUpperCase = function (m, a) { return a.toUpperCase(); };

module.exports = function () {
	return replace.call(this, /-([a-z0-9])/g, toUpperCase);
};
<commit_msg>Use precompiled regexp<commit_after>'use strict';

var replace= String.prototype.replace

  , re = /-([a-z0-9])/g
  , toUpperCase = function (m, a) { return a.toUpperCase(); };

module.exports = function () {
	return replace.call(this, re, toUpperCase);
};
<|endoftext|>


<commit_before>angular.module('angular-ui-flag', [])
.directive('uiFlag', function($http) {
	return {
		scope: {
			style: '=',
		},
		restrict: 'AE',
		template:
			'<svg width="{{style.frame.width}}" height="{{style.frame.height}}">' +
				'<g class="ui-flag-background"></g>' +
				'<g class="ui-flag-foreground"></g>' +
				'<g class="ui-flag-feature"></g>' +
			'</svg>',
		controller: function($scope) {
			$scope.elementSections; // SVG groups of each aspect of the flag (background, foreground + feature)

			// Utility functions {{{
			$scope.splitColors = function(element) {
				var output = [[],[],[]];

				angular.element(element).find('path')
					.filter(function(key, elem) {
						var ae = angular.element(elem);
						var fill = ae.css('fill');
						var colorNo = false;

						if (ae.attr('colorNo')) { // Categorised previously
							colorNo = ae.attr('colorNo');
						} else if (fill == 'rgb(255, 0, 0)') {
							colorNo = 0;
						} else if (fill == 'rgb(0, 255, 0)') {
							colorNo = 1;
						} else if (fill == 'rgb(0, 0, 255)') {
							colorNo = 2;
						}

						if (colorNo !== false) {
							ae.attr('colorNo', colorNo);
							output[colorNo].push(ae);
						}
					});

				return output;
			};
			// }}}

			// Redrawing {{{
			// Background {{{
			$scope._lastBackground;
			$scope.redrawBackground = function() {
				if ($scope.style.background.svg == $scope._lastBackground) return $scope.styleBackground(); // No need to reload - just restyle
				$http.get($scope.style.background.svg)
					.then(function(res) {
						$scope.elementSections.background.innerHTML = res.data;
						var newSVG = angular.element($scope.elementSections.background.children[0]);
						newSVG.attr({
							width: $scope.style.frame.width,
							height: $scope.style.frame.height,
							viewBox: '0 0 73.653713 43.963027',
						});
						if (newSVG[0].viewBox) newSVG[0].viewBox.baseVal.height = 43.963027;
						$scope.styleBackground();
						$scope._lastBackground = $scope.style.background.svg;
					})
			};

			$scope.styleBackground = function() {
				var splits = $scope.splitColors($scope.elementSections.background.children[0]);

				splits[0].forEach(function(elem) { elem.css('fill', $scope.style.background.color1) });
				splits[1].forEach(function(elem) { elem.css('fill', $scope.style.background.color2) });
				splits[2].forEach(function(elem) { elem.css('fill', $scope.style.background.color3) });
			};
			// }}}

			// Foreground {{{
			$scope.lastForeground;
			$scope.redrawForeground = function() {
				if ($scope.style.foreground.svg == $scope._lastForeground) return $scope.styleForeground(); // No need to reload - just restyle
				$http.get($scope.style.foreground.svg)
					.then(function(res) {
						$scope.elementSections.foreground.innerHTML = res.data;
						var newSVG = angular.element($scope.elementSections.foreground.children[0]);
						newSVG.attr({
							width: $scope.style.frame.width,
							height: $scope.style.frame.height,
						});
						$scope.styleForeground();
						$scope._lastForeground = $scope.style.foreground.svg;
					})
			};

			$scope.styleForeground = function() {
				var splits = $scope.splitColors($scope.elementSections.foreground.children[0]);

				splits[0].forEach(function(elem) { elem.css('fill', $scope.style.foreground.color1) });
				splits[1].forEach(function(elem) { elem.css('fill', $scope.style.foreground.color2) });
				splits[2].forEach(function(elem) { elem.css('fill', $scope.style.foreground.color3) });
			};
			// }}}

			// Feature {{{
			$scope._lastFeature;
			$scope.redrawFeature = function() {
				// if ($scope.style.feature.svg == $scope._lastFeature) return $scope.styleFeature(); // No need to reload - just restyle
				$http.get($scope.style.feature.svg)
					.then(function(res) {
						var boundingElem = angular.element($scope.elementSections.background).find('#feature');
						if (!boundingElem.length) return console.warn('Cannot find #feature ID within background', $scope.style.background.svg);
						var boundingRect = boundingElem[0].getBoundingClientRect();
						console.log('BOUNDING RECT', boundingRect);

						$scope.elementSections.feature.innerHTML = res.data;
						var newSVG = angular.element($scope.elementSections.feature.children[0]);
						newSVG.attr({
							left: boundingRect.left,
							top: boundingRect.top,
							width: boundingRect.width,
							height: boundingRect.height,
						});
						$scope.styleFeature();
						$scope._lastFeature = $scope.style.feature.svg;
					})
			};

			$scope.styleFeature = function() {
				var splits = $scope.splitColors($scope.elementSections.feature);

				splits[0].forEach(function(elem) { elem.css('fill', $scope.style.feature.color1) });
				splits[1].forEach(function(elem) { elem.css('fill', $scope.style.feature.color2) });
				splits[2].forEach(function(elem) { elem.css('fill', $scope.style.feature.color3) });
			};
			// }}}
			// }}}

			// Watching + trigger redrawing {{{
			$scope.$watchGroup(['style.background.svg', 'style.background.color1', 'style.background.color2', 'style.background.color3'], $scope.redrawBackground);
			$scope.$watchGroup(['style.foreground.svg', 'style.foreground.color1', 'style.foreground.color2', 'style.foreground.color3'], $scope.redrawForeground);
			$scope.$watchGroup(['style.feature.svg', 'style.background.svg', 'style.feature.color1', 'style.feature.color2', 'style.feature.color3'], $scope.redrawFeature);
			// }}}
		},
		link: function($scope, elem, attr, ctrl) {
			$scope.elementSections = {
				background: elem.find('.ui-flag-background')[0],
				foreground: elem.find('.ui-flag-foreground')[0],
				feature: elem.find('.ui-flag-feature')[0],
			};
		},
	}
});
<commit_msg>BUGFIX: Caching invalidation issue where feature woulnt resize itself if the background changed<commit_after>angular.module('angular-ui-flag', [])
.directive('uiFlag', function($http) {
	return {
		scope: {
			style: '=',
		},
		restrict: 'AE',
		template:
			'<svg width="{{style.frame.width}}" height="{{style.frame.height}}">' +
				'<g class="ui-flag-background"></g>' +
				'<g class="ui-flag-foreground"></g>' +
				'<g class="ui-flag-feature"></g>' +
			'</svg>',
		controller: function($scope) {
			$scope.elementSections; // SVG groups of each aspect of the flag (background, foreground + feature)

			// Utility functions {{{
			$scope.splitColors = function(element) {
				var output = [[],[],[]];

				angular.element(element).find('path')
					.filter(function(key, elem) {
						var ae = angular.element(elem);
						var fill = ae.css('fill');
						var colorNo = false;

						if (ae.attr('colorNo')) { // Categorised previously
							colorNo = ae.attr('colorNo');
						} else if (fill == 'rgb(255, 0, 0)') {
							colorNo = 0;
						} else if (fill == 'rgb(0, 255, 0)') {
							colorNo = 1;
						} else if (fill == 'rgb(0, 0, 255)') {
							colorNo = 2;
						}

						if (colorNo !== false) {
							ae.attr('colorNo', colorNo);
							output[colorNo].push(ae);
						}
					});

				return output;
			};
			// }}}

			// Redrawing {{{
			// Background {{{
			$scope._lastBackground;
			$scope.redrawBackground = function() {
				if ($scope.style.background.svg == $scope._lastBackground) return $scope.styleBackground(); // No need to reload - just restyle
				$http.get($scope.style.background.svg)
					.then(function(res) {
						$scope.elementSections.background.innerHTML = res.data;
						var newSVG = angular.element($scope.elementSections.background.children[0]);
						newSVG.attr({
							width: $scope.style.frame.width,
							height: $scope.style.frame.height,
							viewBox: '0 0 73.653713 43.963027',
						});
						if (newSVG[0].viewBox) newSVG[0].viewBox.baseVal.height = 43.963027;
						$scope.styleBackground();
						$scope._lastBackground = $scope.style.background.svg;
					})
			};

			$scope.styleBackground = function() {
				var splits = $scope.splitColors($scope.elementSections.background.children[0]);

				splits[0].forEach(function(elem) { elem.css('fill', $scope.style.background.color1) });
				splits[1].forEach(function(elem) { elem.css('fill', $scope.style.background.color2) });
				splits[2].forEach(function(elem) { elem.css('fill', $scope.style.background.color3) });
			};
			// }}}

			// Foreground {{{
			$scope.lastForeground;
			$scope.redrawForeground = function() {
				if ($scope.style.foreground.svg == $scope._lastForeground) return $scope.styleForeground(); // No need to reload - just restyle
				$http.get($scope.style.foreground.svg)
					.then(function(res) {
						$scope.elementSections.foreground.innerHTML = res.data;
						var newSVG = angular.element($scope.elementSections.foreground.children[0]);
						newSVG.attr({
							width: $scope.style.frame.width,
							height: $scope.style.frame.height,
						});
						$scope.styleForeground();
						$scope._lastForeground = $scope.style.foreground.svg;
					})
			};

			$scope.styleForeground = function() {
				var splits = $scope.splitColors($scope.elementSections.foreground.children[0]);

				splits[0].forEach(function(elem) { elem.css('fill', $scope.style.foreground.color1) });
				splits[1].forEach(function(elem) { elem.css('fill', $scope.style.foreground.color2) });
				splits[2].forEach(function(elem) { elem.css('fill', $scope.style.foreground.color3) });
			};
			// }}}

			// Feature {{{
			$scope._lastFeature;
			$scope._lastFeatureBackground;
			$scope.redrawFeature = function() {
				if (
					$scope.style.background.svg == $scope._lastFeatureBackground && // Background hasn't changed
					$scope.style.feature.svg == $scope._lastFeature // Feature hasn't changed
				) return $scope.styleFeature(); // No need to reload - just restyle

				$http.get($scope.style.feature.svg)
					.then(function(res) {
						var boundingElem = angular.element($scope.elementSections.background).find('#feature');
						if (!boundingElem.length) return console.warn('Cannot find #feature ID within background', $scope.style.background.svg);
						var boundingRect = boundingElem[0].getBoundingClientRect();
						console.log('BOUNDING RECT', boundingRect);

						$scope.elementSections.feature.innerHTML = res.data;
						var newSVG = angular.element($scope.elementSections.feature.children[0]);
						newSVG.attr({
							left: boundingRect.left,
							top: boundingRect.top,
							width: boundingRect.width,
							height: boundingRect.height,
						});
						$scope.styleFeature();
						$scope._lastFeature = $scope.style.feature.svg;
						$scope._lastFeatureBackground = $scope.style.background.svg;
					})
			};

			$scope.styleFeature = function() {
				var splits = $scope.splitColors($scope.elementSections.feature);

				splits[0].forEach(function(elem) { elem.css('fill', $scope.style.feature.color1) });
				splits[1].forEach(function(elem) { elem.css('fill', $scope.style.feature.color2) });
				splits[2].forEach(function(elem) { elem.css('fill', $scope.style.feature.color3) });
			};
			// }}}
			// }}}

			// Watching + trigger redrawing {{{
			$scope.$watchGroup(['style.background.svg', 'style.background.color1', 'style.background.color2', 'style.background.color3'], $scope.redrawBackground);
			$scope.$watchGroup(['style.foreground.svg', 'style.foreground.color1', 'style.foreground.color2', 'style.foreground.color3'], $scope.redrawForeground);
			$scope.$watchGroup(['style.feature.svg', 'style.background.svg', 'style.feature.color1', 'style.feature.color2', 'style.feature.color3'], $scope.redrawFeature);
			// }}}
		},
		link: function($scope, elem, attr, ctrl) {
			$scope.elementSections = {
				background: elem.find('.ui-flag-background')[0],
				foreground: elem.find('.ui-flag-foreground')[0],
				feature: elem.find('.ui-flag-feature')[0],
			};
		},
	}
});
<|endoftext|>


<commit_before>#!/Users/admin/.nvm/versions/node/v4.2.4/bin/node
// ^ full path to node must be specified above, edit for your system. may also try:
// #!/usr/local/bin/node

'use strict';

const process = require('process');
const fs = require('fs');
const nativeMessage = require('chrome-native-messaging');
const input = new nativeMessage.Input();
const transform = new nativeMessage.Transform(messageHandler);
const output = new nativeMessage.Output();

process.stdin
  .pipe(input)
  .pipe(transform)
  .pipe(output)
  .pipe(process.stdout);

function encodeError(err) {
  return {error:
            {
              code: err.errno, // must be an integer, but err.code is a string like 'ENOENT'
              message: err.toString(),
            }
          };
}

function encodeResult(err, data) {
  if (err) {
    return encodeError(err);
  } else {
    return {result:data};
  }
}

function messageHandler(msg, push, done) {
  const method = msg.method;
  const params = msg.params;

  if (method === 'echo') {
    push(msg);
    done();
    /* TODO
  } else if (method === 'fs.access') {
    const path = params[0];
    if (params.length < 2) {
      fs.access(path, (err) => {

      });

*/
  } else if (method === 'fs.readFile') {
    const path = params[0];
    // TODO: restrict access to only a limited set of files
    if (params.length < 2) {
      //const callback = params[1];
      fs.readFile(path, (err, data) => {
        push(encodeResult(err, data));
        done();
      });
    } else {
      const options = params[1];
      //const callback = params[2];
      fs.readFile(path, options, (err, data) => {
        push(encodeResult(err, data));
        done();
      });
    }
  } else {
    push({error: {
      code: -32601, // defined in http://www.jsonrpc.org/specification#response_object
      message: 'Method not found',
      data: `invalid method: ${method}`}
    });
    done();
  }
}
<commit_msg>createCallback()<commit_after>#!/Users/admin/.nvm/versions/node/v4.2.4/bin/node
// ^ full path to node must be specified above, edit for your system. may also try:
// #!/usr/local/bin/node

'use strict';

const process = require('process');
const fs = require('fs');
const nativeMessage = require('chrome-native-messaging');
const input = new nativeMessage.Input();
const transform = new nativeMessage.Transform(messageHandler);
const output = new nativeMessage.Output();

process.stdin
  .pipe(input)
  .pipe(transform)
  .pipe(output)
  .pipe(process.stdout);

function encodeError(err) {
  return {error:
            {
              code: err.errno, // must be an integer, but err.code is a string like 'ENOENT'
              message: err.toString(),
            }
          };
}

function encodeResult(err, data) {
  if (err) {
    return encodeError(err);
  } else {
    return {result:data};
  }
}

function createCallback(push, done) {
  function genericCallback(err, data) {
    push(encodeResult(err, data));
    done();
  }

  return genericCallback;
}

function messageHandler(msg, push, done) {
  const method = msg.method;
  const params = msg.params;

  if (method === 'echo') {
    push(msg);
    done();
    /* TODO
  } else if (method === 'fs.access') {
    const path = params[0];
    if (params.length < 2) {
      fs.access(path, (err) => {

      });

*/
  } else if (method === 'fs.readFile') {
    const path = params[0];
    // TODO: restrict access to only a limited set of files
    if (params.length < 2) {
      //const callback = params[1];
      fs.readFile(path, createCallback(push, done));
    } else {
      const options = params[1];
      //const callback = params[2];
      fs.readFile(path, options, createCallback(push, done));
    }
  } else {
    push({error: {
      code: -32601, // defined in http://www.jsonrpc.org/specification#response_object
      message: 'Method not found',
      data: `invalid method: ${method}`}
    });
    done();
  }
}
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import { withApollo, gql } from 'react-apollo'
import _ from 'lodash'

import { FIRSTLOAD_CHATROOMS_QUERY } from './ChatList'
import { FIRSTLOAD_USER_CHATROOMS_QUERY } from './UserChatList'

class NewChat extends Component {

  constructor(props) {
    super(props);
    this.state = {
      title: '',
    }
  }

  onCreateChat = async (e) => {
    e.preventDefault()

    const currentUserId = this.props.currentUserId
    const currentUsername = this.props.currentUsername

    if(!currentUserId) {
      alert('You must log in first.')
      return
    }

    try {
      const userCount = (await this.props.client.query({ query: USER_COUNT_QUERY })).data._allUsersMeta.count
      const randomSkip = _.random(0, userCount - 2)
      if(userCount - 2 < 0) {
        alert("Oops, can't find any users.")
        return
      }

      // alert('random skip is' + randomSkip)

      // To prevent duplicate, get two random users, at least one will never be a duplicate.
      const twoRandomUsers  = (await this.props.client.query({
        query: GET_USERS_QUERY,
        variables: {
          first: 2,
          skip: randomSkip,
        }
      })).data.allUsers

      const twoRandomUserIds = twoRandomUsers.map(u => u.id)

      // alert('fetched users are' + twoRandomUsers.map(u => u.username).join(' '))

      let anotherUserId;
      let anotherUsername;

      // if both not same as current user id, can random
      if (twoRandomUserIds[0] !== currentUserId && twoRandomUserIds[1] !== currentUserId) {
        anotherUserId = _.sample(twoRandomUserIds)
        anotherUsername = anotherUserId === twoRandomUserIds[0] ? twoRandomUsers[0].username : twoRandomUsers[1].username
      } else {
        // some is duplicate, choose one that's not
        anotherUserId = twoRandomUserIds[0] !== currentUserId ? twoRandomUserIds[0] : twoRandomUserIds[1]
        anotherUsername = twoRandomUserIds[0] !== currentUserId ? twoRandomUsers[0].username : twoRandomUsers[1].username
      }

      const { data: { createChatroom: { id } } } = await this.props.client.mutate({
        mutation: CREATE_CHAT_MUTATION,
        variables: {
          title: this.state.title,
          userIds: [currentUserId, anotherUserId],
        },
        // You may simply use this, in which case we don't need to update the store manually in 'update'
        // But this is slower.
        // refetchQueries: [
        //   {
        //     query: FIRSTLOAD_CHATROOMS_QUERY,
        //   },
        //   {
        //     query: FIRSTLOAD_USER_CHATROOMS_QUERY,
        //     variables: {
        //       forUserId: currentUserId,
        //     }
        //   }
        // ],
        optimisticResponse: {
          __typename: 'Mutation',
          createChatroom: {
            __typename: 'Chatroom',
            id: '',
            createdAt: (new Date()).toISOString(),
            title: this.state.title,
            users: [
              {
                __typename: 'User',
                id: '',
                username: currentUsername,
              },
              {
                __typename: 'User',
                id: '',
                username: anotherUsername,
              }
            ],
          }
        },
        update: (store, { data: { createChatroom }}) => {

          // Update the store (so that the graphql components across the app will get updated)

          // Why doesn't it be smart and update all 'related' queries & components !??
          // https://github.com/apollographql/apollo-client/issues/1697

          try  {
            // All chatlist
            // 1. read from store
            const allChatroomsData = store.readQuery({
              query: FIRSTLOAD_CHATROOMS_QUERY,
            })

            // Must update messagesMeta manually, since the newly added object doesn't has one
            createChatroom._messagesMeta = {
              count: 0,
              __typename: '_QueryMeta',
            }

            // 2. append at first position
            allChatroomsData.allChatrooms.unshift(createChatroom)
            allChatroomsData._allChatroomsMeta.count += 1

            // 3. write back
            store.writeQuery({
              query: FIRSTLOAD_CHATROOMS_QUERY,
              data: allChatroomsData,
            })
          } catch (err) {
            // Probably query allChatrooms doesn't exist. (e.g., in case user enters directly to '/talk' page)
            // console.log('Error: ', err)
          }

          try {
            // User chatlist
            // 1. read from store
            const userChatroomsData = store.readQuery({
              query: FIRSTLOAD_USER_CHATROOMS_QUERY,
              variables: {
                forUserId: currentUserId,
              },
            })

            // Must update messagesMeta manually, since the newly added object doesn't has one
            createChatroom._messagesMeta = {
              count: 0,
              __typename: '_QueryMeta',
            }

            // 2. append at first position
            userChatroomsData.allChatrooms.unshift(createChatroom)
            userChatroomsData._allChatroomsMeta.count += 1

            // 3. write back
            store.writeQuery({
              query: FIRSTLOAD_USER_CHATROOMS_QUERY,
              data: userChatroomsData,
              variables: {
                forUserId: currentUserId,
              },
            })
          } catch (err) {
            // This shouldn't error, since we can add chat only in '/talk' page.
            // This means FIRSTLOAD_USER_CHATROOMS_QUERY should exist.
            console.err('Error: ', err)
          }

        },
      })

      this.setState({
        title: '',
      })
      this.props.onCreateNewChatroom(id)
    } catch(err) {
      alert("Oops: " + err.graphQLErrors[0].message);
    }
  }

  render() {
    const confirmDisabled = !this.state.title
    return (

        <form onSubmit={this.onCreateChat}>
          <input
              value={this.state.title}
              onChange={e => this.setState({ title: e.target.value})}
              placeholder="insert a topic here"
              type="text"
              className="add-chat-input"
          />

          <button
              className="primary-button"
              type="submit"
              disabled={confirmDisabled}
          >
              Create chat
          </button>

        </form>

)
    }
}

const CREATE_CHAT_MUTATION = gql`
  mutation CreateChatroomMutation($title: String!, $userIds: [ID!]!) {
    createChatroom(title: $title, usersIds: $userIds) {
      id,
      createdAt,
      title,
      users {
        id,
        username,
      }
    }
  }
`

const USER_COUNT_QUERY = gql`
  query UserCountQuery {
    _allUsersMeta {
      count
    }
  }
`

const GET_USERS_QUERY =  gql`
  query GetUsersQuery($first: Int!, $skip: Int!) {
    allUsers(first: $first, skip: $skip) {
      id,
      username,
    }
  }
`

NewChat.propTypes = {
  currentUserId: React.PropTypes.string.isRequired,
  currentUsername: React.PropTypes.string.isRequired,
}

export default withApollo(NewChat)
<commit_msg>Update mutation to reflect schema change<commit_after>import React, { Component } from 'react'
import { withApollo, gql } from 'react-apollo'
import _ from 'lodash'

import { FIRSTLOAD_CHATROOMS_QUERY } from './ChatList'
import { FIRSTLOAD_USER_CHATROOMS_QUERY } from './UserChatList'

class NewChat extends Component {

  constructor(props) {
    super(props);
    this.state = {
      title: '',
    }
  }

  onCreateChat = async (e) => {
    e.preventDefault()

    const currentUserId = this.props.currentUserId
    const currentUsername = this.props.currentUsername

    if(!currentUserId) {
      alert('You must log in first.')
      return
    }

    try {
      const userCount = (await this.props.client.query({ query: USER_COUNT_QUERY })).data._allUsersMeta.count
      const randomSkip = _.random(0, userCount - 2)
      if(userCount - 2 < 0) {
        alert("Oops, can't find any users.")
        return
      }

      // alert('random skip is' + randomSkip)

      // To prevent duplicate, get two random users, at least one will never be a duplicate.
      const twoRandomUsers  = (await this.props.client.query({
        query: GET_USERS_QUERY,
        variables: {
          first: 2,
          skip: randomSkip,
        }
      })).data.allUsers

      const twoRandomUserIds = twoRandomUsers.map(u => u.id)

      // alert('fetched users are' + twoRandomUsers.map(u => u.username).join(' '))

      let anotherUserId;
      let anotherUsername;

      // if both not same as current user id, can random
      if (twoRandomUserIds[0] !== currentUserId && twoRandomUserIds[1] !== currentUserId) {
        anotherUserId = _.sample(twoRandomUserIds)
        anotherUsername = anotherUserId === twoRandomUserIds[0] ? twoRandomUsers[0].username : twoRandomUsers[1].username
      } else {
        // some is duplicate, choose one that's not
        anotherUserId = twoRandomUserIds[0] !== currentUserId ? twoRandomUserIds[0] : twoRandomUserIds[1]
        anotherUsername = twoRandomUserIds[0] !== currentUserId ? twoRandomUsers[0].username : twoRandomUsers[1].username
      }

      const { data: { createChatroom: { id } } } = await this.props.client.mutate({
        mutation: CREATE_CHAT_MUTATION,
        variables: {
          title: this.state.title,
          userIds: [currentUserId, anotherUserId],
          createdById: currentUserId,
        },
        // You may simply use this, in which case we don't need to update the store manually in 'update'
        // But this is slower.
        // refetchQueries: [
        //   {
        //     query: FIRSTLOAD_CHATROOMS_QUERY,
        //   },
        //   {
        //     query: FIRSTLOAD_USER_CHATROOMS_QUERY,
        //     variables: {
        //       forUserId: currentUserId,
        //     }
        //   }
        // ],
        optimisticResponse: {
          __typename: 'Mutation',
          createChatroom: {
            __typename: 'Chatroom',
            id: '',
            createdAt: (new Date()).toISOString(),
            title: this.state.title,
            users: [
              {
                __typename: 'User',
                id: '',
                username: currentUsername,
              },
              {
                __typename: 'User',
                id: '',
                username: anotherUsername,
              }
            ],
          }
        },
        update: (store, { data: { createChatroom }}) => {

          // Update the store (so that the graphql components across the app will get updated)

          // Why doesn't it be smart and update all 'related' queries & components !??
          // https://github.com/apollographql/apollo-client/issues/1697

          try  {
            // All chatlist
            // 1. read from store
            const allChatroomsData = store.readQuery({
              query: FIRSTLOAD_CHATROOMS_QUERY,
            })

            // Must update messagesMeta manually, since the newly added object doesn't has one
            createChatroom._messagesMeta = {
              count: 0,
              __typename: '_QueryMeta',
            }

            // 2. append at first position
            allChatroomsData.allChatrooms.unshift(createChatroom)
            allChatroomsData._allChatroomsMeta.count += 1

            // 3. write back
            store.writeQuery({
              query: FIRSTLOAD_CHATROOMS_QUERY,
              data: allChatroomsData,
            })
          } catch (err) {
            // Probably query allChatrooms doesn't exist. (e.g., in case user enters directly to '/talk' page)
            // console.log('Error: ', err)
          }

          try {
            // User chatlist
            // 1. read from store
            const userChatroomsData = store.readQuery({
              query: FIRSTLOAD_USER_CHATROOMS_QUERY,
              variables: {
                forUserId: currentUserId,
              },
            })

            // Must update messagesMeta manually, since the newly added object doesn't has one
            createChatroom._messagesMeta = {
              count: 0,
              __typename: '_QueryMeta',
            }

            // 2. append at first position
            userChatroomsData.allChatrooms.unshift(createChatroom)
            userChatroomsData._allChatroomsMeta.count += 1

            // 3. write back
            store.writeQuery({
              query: FIRSTLOAD_USER_CHATROOMS_QUERY,
              data: userChatroomsData,
              variables: {
                forUserId: currentUserId,
              },
            })
          } catch (err) {
            // This shouldn't error, since we can add chat only in '/talk' page.
            // This means FIRSTLOAD_USER_CHATROOMS_QUERY should exist.
            console.err('Error: ', err)
          }

        },
      })

      this.setState({
        title: '',
      })
      this.props.onCreateNewChatroom(id)
    } catch(err) {
      alert("Oops: " + err.graphQLErrors[0].message);
    }
  }

  render() {
    const confirmDisabled = !this.state.title
    return (

        <form onSubmit={this.onCreateChat}>
          <input
              value={this.state.title}
              onChange={e => this.setState({ title: e.target.value})}
              placeholder="insert a topic here"
              type="text"
              className="add-chat-input"
          />

          <button
              className="primary-button"
              type="submit"
              disabled={confirmDisabled}
          >
              Create chat
          </button>

        </form>

)
    }
}

const CREATE_CHAT_MUTATION = gql`
  mutation CreateChatroomMutation($title: String!, $createdById: ID!, $userIds: [ID!]!) {
    createChatroom(title: $title, usersIds: $userIds, createdById: $createdById) {
      id,
      createdAt,
      title,
      createdBy {
        id
      },
      users {
        id,
        username,
      }
    }
  }
`

const USER_COUNT_QUERY = gql`
  query UserCountQuery {
    _allUsersMeta {
      count
    }
  }
`

const GET_USERS_QUERY =  gql`
  query GetUsersQuery($first: Int!, $skip: Int!) {
    allUsers(first: $first, skip: $skip) {
      id,
      username,
    }
  }
`

NewChat.propTypes = {
  currentUserId: React.PropTypes.string.isRequired,
  currentUsername: React.PropTypes.string.isRequired,
}

export default withApollo(NewChat)
<|endoftext|>


<commit_before>App.info({
  id: 'br.com.app.sistemameionorte',
  name: 'Meio Norte',
  description: 'Aplicativo Interativo Sistema Meio Norte de Comunicação',
  version: '0.1.58',
  author: 'Agência Getup',
  email: 'alissonplus@gmail.com',
  website: 'http://sistemameionorte.com.br'
});

App.icons({

  // iOS
  'iphone': 'public/icons/ios/Icon-60.png',
  'iphone_2x': 'public/icons/ios/AppIcon.appiconset/Icon-60@2x.png',
  'iphone_3x': 'public/icons/ios/AppIcon.appiconset/Icon-60@3x.png',

  // Android
  'android_ldpi': 'public/icons/android/drawable-ldpi/ic_launcher.png',
  'android_mdpi': 'public/icons/android/drawable-mdpi/ic_launcher.png',
  'android_hdpi': 'public/icons/android/drawable-hdpi/ic_launcher.png',
  'android_xhdpi': 'public/icons/android/drawable-xhdpi/ic_launcher.png'

});

App.launchScreens({

  // iOS
  'iphone': 'public/splash/ios/iphone.png',
  'iphone_2x': 'public/splash/ios/iphone_2x.png',
  'iphone5': 'public/splash/ios/iphone5.png',
  'iphone6': 'public/splash/ios/iphone6.png',
  'iphone6p_portrait': 'public/splash/ios/iphone6p_portrait.png',

  // Android
  'android_ldpi_portrait': 'public/splash/android/drawable-ldpi/screen.png',
  'android_ldpi_landscape': 'public/splash/android/drawable-land-ldpi/screen.png',
  'android_mdpi_portrait': 'public/splash/android/drawable-mdpi/screen.png',
  'android_mdpi_landscape': 'public/splash/android/drawable-land-mdpi/screen.png',
  'android_hdpi_portrait': 'public/splash/android/drawable-hdpi/screen.png',
  'android_hdpi_landscape': 'public/splash/android/drawable-land-hdpi/screen.png',
  'android_xhdpi_portrait': 'public/splash/android/drawable-xhdpi/screen.png',
  'android_xhdpi_landscape': 'public/splash/android/drawable-land-xhdpi/screen.png'
});

App.setPreference('BackgroundColor', '0xffffffff');
App.setPreference('HideKeyboardFormAccessoryBar', true);
App.setPreference('StatusBarOverlaysWebView', 'true');
App.setPreference('StatusBarStyle', 'lightcontent');
App.setPreference('Orientation', 'portrait');
App.setPreference("SplashScreen", "screen");
App.setPreference("SplashScreenDelay", "10000");

// cordova plugin facebook
App.configurePlugin('com.phonegap.plugins.facebookconnect', {
     APP_ID: '580730978674615',
     APP_NAME: 'sistemamn'
});

// regras de acesso cross origen
App.accessRule("*", {launchExternal: true});
App.accessRule('*.google-analytics.com/*');
App.accessRule("*://*.meteor.com/*");
App.accessRule("*://*.sistemameionorte.com.br/*");
App.accessRule("*://*.meionorte.com/*");
//App.accessRule("*://localhost:3010/*");
<commit_msg>Alteracoes<commit_after>App.info({
  id: 'br.com.app.sistemameionorte',
  name: 'Meio Norte',
  description: 'Aplicativo Interativo Sistema Meio Norte de Comunicação',
  version: '0.1.58',
  author: 'Agência Getup',
  email: 'alissonplus@gmail.com',
  website: 'http://sistemameionorte.com.br'
});

App.icons({

  // iOS
  'iphone': 'public/icons/ios/Icon-60.png',
  'iphone_2x': 'public/icons/ios/AppIcon.appiconset/Icon-60@2x.png',
  'iphone_3x': 'public/icons/ios/AppIcon.appiconset/Icon-60@3x.png',

  // Android
  'android_ldpi': 'public/icons/android/drawable-ldpi/ic_launcher.png',
  'android_mdpi': 'public/icons/android/drawable-mdpi/ic_launcher.png',
  'android_hdpi': 'public/icons/android/drawable-hdpi/ic_launcher.png',
  'android_xhdpi': 'public/icons/android/drawable-xhdpi/ic_launcher.png'

});

App.launchScreens({

  // iOS
  'iphone': 'public/splash/ios/iphone.png',
  'iphone_2x': 'public/splash/ios/iphone_2x.png',
  'iphone5': 'public/splash/ios/iphone5.png',
  'iphone6': 'public/splash/ios/iphone6.png',
  'iphone6p_portrait': 'public/splash/ios/iphone6p_portrait.png',

  // Android
  'android_ldpi_portrait': 'public/splash/android/drawable-ldpi/screen.png',
  'android_ldpi_landscape': 'public/splash/android/drawable-land-ldpi/screen.png',
  'android_mdpi_portrait': 'public/splash/android/drawable-mdpi/screen.png',
  'android_mdpi_landscape': 'public/splash/android/drawable-land-mdpi/screen.png',
  'android_hdpi_portrait': 'public/splash/android/drawable-hdpi/screen.png',
  'android_hdpi_landscape': 'public/splash/android/drawable-land-hdpi/screen.png',
  'android_xhdpi_portrait': 'public/splash/android/drawable-xhdpi/screen.png',
  'android_xhdpi_landscape': 'public/splash/android/drawable-land-xhdpi/screen.png'
});

App.setPreference('BackgroundColor', '0xffffffff');
App.setPreference('HideKeyboardFormAccessoryBar', true);
App.setPreference('StatusBarOverlaysWebView', 'true');
App.setPreference('StatusBarStyle', 'lightcontent');
App.setPreference('Orientation', 'portrait');
App.setPreference("SplashScreen", "screen");
App.setPreference("SplashScreenDelay", "1000");

// cordova plugin facebook
App.configurePlugin('com.phonegap.plugins.facebookconnect', {
     APP_ID: '580730978674615',
     APP_NAME: 'sistemamn'
});

// regras de acesso cross origen
App.accessRule("*");
App.accessRule('*.google-analytics.com/*');
App.accessRule("*://*.meteor.com/*");
App.accessRule("http://admin.sistemameionorte.com.br/");
App.accessRule("http://meionorte.com/");
App.accessRule("http://jornal.meionorte.com/");
<|endoftext|>


<commit_before>const DEBUG = false;

var gLogger = {
  messages: [],
  log: function(aMessage) {
    this.messages.push(aMessage);
    this.output(aMessage);
  },
  output: function(aMessage) {
    if (!DEBUG)
      return;
    Components.utils.import('resource://force-filter-status-modules/lib/textIO.jsm');
    var file = Cc['@mozilla.org/file/directory_service;1']
                 .getService(Ci.nsIProperties)
                 .get('Desk', Ci.nsIFile);
    file.append('force-filter-status.log');
    var previous = textIO.readFrom(file, 'UTF-8') || '';
    var log;
    if (aMessage) {
      log = previous + '\n[' + (new Date()) + '] ' + aMessage;
    } else {
      log = [previous, this.messages.join('\n')].join('\n-----' + (new Date()) + '-----\n');
    }
    textIO.writeTo(log, file, 'UTF-8');
/*
    Cc['@mozilla.org/embedcomp/prompt-service;1']
      .getService(Ci.nsIPromptService)
      .alert(null, kID, file.path+'\n\n'+this.messages.join('\n'));;
*/
  }
}

const kCID  = Components.ID('{c7a54030-3fd4-11e4-916c-0800200c9a66}');
const kID   = '@clear-code.com/force-filter-status/startup;1';
const kNAME = 'ForceFilterStatusStartupService';

var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;

Cu.import('resource://gre/modules/XPCOMUtils.jsm');

XPCOMUtils.defineLazyServiceGetter(this,
                                   'AccountManager',
                                   '@mozilla.org/messenger/account-manager;1',
                                   'nsIMsgAccountManager');
XPCOMUtils.defineLazyServiceGetter(this,
                                   'MailSession',
                                   '@mozilla.org/messenger/services/session;1',
                                   'nsIMsgMailSession');
XPCOMUtils.defineLazyServiceGetter(this,
                                   'ObserverService',
                                   '@mozilla.org/observer-service;1',
                                   'nsIObserverService');

Cu.import('resource://force-filter-status-modules/lib/prefs.js');
Cu.import('resource://force-filter-status-modules/lib/jsdeferred.js');

const BASE = 'extensions.force-filter-status@clear-code.com.';

function ForceFilterStatusStartupService() {
  this.ready = false;
  this.active = false;
}
ForceFilterStatusStartupService.prototype = {
  classID          : kCID,
  contractID       : kID,
  classDescription : kNAME,

  observe : function(aSubject, aTopic, aData)
  {
    gLogger.log('observe: ' + aTopic);
    switch (aTopic)
    {
      case 'profile-after-change':
        ObserverService.addObserver(this, 'final-ui-startup', false);
        ObserverService.addObserver(this, 'sessionstore-windows-restored', false);
        ObserverService.addObserver(this, 'mail-startup-done', false);
        return;

      case 'final-ui-startup':
        ObserverService.removeObserver(this, 'final-ui-startup');
        this.registerListener();
        var self = this;
        return this.waitUntilStarted()
          .next(function() {
            return self.checkFilters();
          });
        return;

      case 'sessionstore-windows-restored':
      case 'mail-startup-done':
        ObserverService.removeObserver(this, 'sessionstore-windows-restored');
        ObserverService.removeObserver(this, 'mail-startup-done');
        this.ready = true;
        if (this.waitUntilStarted_trigger)
          this.waitUntilStarted_trigger.call();
        return;
    }
  },

  get allExistingFilterLists() {
    var filterLists = [];
    this.allIncomingServers.forEach(function(aIncomingServer) {
      try {
        filterLists.push(aIncomingServer.getFilterList(MailSession.topmostMsgWindow));
      }
      catch(e) {
        // this fails for local folder account
      }
    }, this);
    return filterLists;
  },
  get allIncomingServers() {
    var servers = [];
    this.allAccounts.forEach(function(aAccount) {
      if (aAccount.incomingServer)
        servers.push(aAccount.incomingServer);
    }, this);
    return servers;
  },

  get allAccounts() {
    return this.toArray(AccountManager.accounts, Ci.nsIMsgAccount);
  },
  get allAccountKeys() {
    return this.allAccounts.map(function(aAccount) {
      return this.getAccountKey(aAccount);
    }, this).filter(function(aKey) {
      return aKey != '';
    });
  },
  toArray: function (aEnumerator, aInterface) {
    aInterface = aInterface || Ci.nsISupports;
    var array = [];
    if (aEnumerator instanceof Ci.nsISupportsArray) {
      let count = aEnumerator.Count();
      for (let i = 0; i < count; i++) {
        array.push(aEnumerator.QueryElementAt(i, aInterface));
      }
    } else if (aEnumerator instanceof Ci.nsIArray) {
      let count = aEnumerator.length;
      for (let i = 0; i < count; i++) {
        array.push(aEnumerator.queryElementAt(i, aInterface));
      }
    } else if (aEnumerator instanceof Ci.nsISimpleEnumerator) {
      while (aEnumerator.hasMoreElements()) {
        array.push(aEnumerator.getNext().QueryInterface(aInterface));
      }
    }
    return array;
  },

  checkFilters : function()
  {
    if (this.checking)
      return;
    this.checking = true;

    var self = this;
    var changedCount = { value : 0 };
    var existingFilterLists = this.allExistingFilterLists;
    return Deferred.next(function() {
        return self.removeDisallowedFilters(existingFilterLists, changedCount);
      })
      .error(function(error) {
        Components.utils.reportError(error);
        gLogger.log('unexpected error: ' + error + '\n' + error.stack);
      })
      .next(function() {
        self.checking = false;
        if (changedCount.value > 0)
          self.restart();
      });
  },

  removeDisallowedFilters : function(aFilterLists, aChangedCount)
  {
    var prefEntries = prefs.getDescendants(BASE + 'disallow.patterns.');
    if (prefEntries.length == 0)
      return;

    var patterns = [];
    prefEntries.forEach(function(aKey) {
      var pattern = prefs.getPref(aKey);
      if (!pattern)
        return;

      patterns.push(pattern);
    }, this);

    if (patterns.length == 0)
      return;

    patterns = new RegExp(patterns.join('|'), 'im');

    aFilterLists.forEach(function(aFilterList) {
      var beforeCount = aChangedCount.value;
      for (var i = aFilterList.filterCount - 1; i > -1; i--) {
        let filter = aFilterList.getFilterAt(i);
        let serialized = this.serializeFilter(filter);
        if (patterns.test(serialized)) {
          aFilterList.removeFilterAt(i);
          aChangedCount.value++;
        }
      }
      if (aChangedCount.value != beforeCount)
        aFilterList.saveToDefaultFile();
    }, this);
  },
  serializeFilter : function(aFilter) {
    var serialized = '';
    var stream = {
      close: function() {},
      flush: function() {},
      write: function(aString, aCount) {
        serialized += aString;
        return aCount;
      },
      writeFrom: function(aInputStream, aCount) {
        return aCount;
      },
      isNonBlocking: function() {
        return false;
      }
    };
    aFilter.SaveToTextFile(stream);
    return decodeURIComponent(escape(serialized));
  },

  waitUntilStarted : function() {
    if (this.ready)
      return;

    this.waitUntilStarted_trigger = new Deferred();
    return this.waitUntilStarted_trigger;
  },

  restart : function()
  {
    gLogger.log('try to restart');
    Cc['@mozilla.org/toolkit/app-startup;1']
      .getService(Ci.nsIAppStartup)
      .quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit);
  },

  QueryInterface : function(aIID)
  {
    if(!aIID.equals(Ci.nsIObserver) &&
       !aIID.equals(Ci.nsISupports)) {
      throw Components.results.NS_ERROR_NO_INTERFACE;
    }
    return this;
  }
};

Components.utils.import('resource://gre/modules/XPCOMUtils.jsm');
var NSGetFactory = XPCOMUtils.generateNSGetFactory([ForceFilterStatusStartupService]);
<commit_msg>Serialize filters only once<commit_after>const DEBUG = false;

var gLogger = {
  messages: [],
  log: function(aMessage) {
    this.messages.push(aMessage);
    this.output(aMessage);
  },
  output: function(aMessage) {
    if (!DEBUG)
      return;
    Components.utils.import('resource://force-filter-status-modules/lib/textIO.jsm');
    var file = Cc['@mozilla.org/file/directory_service;1']
                 .getService(Ci.nsIProperties)
                 .get('Desk', Ci.nsIFile);
    file.append('force-filter-status.log');
    var previous = textIO.readFrom(file, 'UTF-8') || '';
    var log;
    if (aMessage) {
      log = previous + '\n[' + (new Date()) + '] ' + aMessage;
    } else {
      log = [previous, this.messages.join('\n')].join('\n-----' + (new Date()) + '-----\n');
    }
    textIO.writeTo(log, file, 'UTF-8');
/*
    Cc['@mozilla.org/embedcomp/prompt-service;1']
      .getService(Ci.nsIPromptService)
      .alert(null, kID, file.path+'\n\n'+this.messages.join('\n'));;
*/
  }
}

const kCID  = Components.ID('{c7a54030-3fd4-11e4-916c-0800200c9a66}');
const kID   = '@clear-code.com/force-filter-status/startup;1';
const kNAME = 'ForceFilterStatusStartupService';

var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;

Cu.import('resource://gre/modules/XPCOMUtils.jsm');

XPCOMUtils.defineLazyServiceGetter(this,
                                   'AccountManager',
                                   '@mozilla.org/messenger/account-manager;1',
                                   'nsIMsgAccountManager');
XPCOMUtils.defineLazyServiceGetter(this,
                                   'MailSession',
                                   '@mozilla.org/messenger/services/session;1',
                                   'nsIMsgMailSession');
XPCOMUtils.defineLazyServiceGetter(this,
                                   'ObserverService',
                                   '@mozilla.org/observer-service;1',
                                   'nsIObserverService');

Cu.import('resource://force-filter-status-modules/lib/prefs.js');
Cu.import('resource://force-filter-status-modules/lib/jsdeferred.js');

const BASE = 'extensions.force-filter-status@clear-code.com.';

function ForceFilterStatusStartupService() {
  this.ready = false;
  this.active = false;
}
ForceFilterStatusStartupService.prototype = {
  classID          : kCID,
  contractID       : kID,
  classDescription : kNAME,

  observe : function(aSubject, aTopic, aData)
  {
    gLogger.log('observe: ' + aTopic);
    switch (aTopic)
    {
      case 'profile-after-change':
        ObserverService.addObserver(this, 'final-ui-startup', false);
        ObserverService.addObserver(this, 'sessionstore-windows-restored', false);
        ObserverService.addObserver(this, 'mail-startup-done', false);
        return;

      case 'final-ui-startup':
        ObserverService.removeObserver(this, 'final-ui-startup');
        this.registerListener();
        var self = this;
        return this.waitUntilStarted()
          .next(function() {
            return self.checkFilters();
          });
        return;

      case 'sessionstore-windows-restored':
      case 'mail-startup-done':
        ObserverService.removeObserver(this, 'sessionstore-windows-restored');
        ObserverService.removeObserver(this, 'mail-startup-done');
        this.ready = true;
        if (this.waitUntilStarted_trigger)
          this.waitUntilStarted_trigger.call();
        return;
    }
  },

  get allExistingFilters() {
    var filters = [];
    this.allExistingFilterLists.forEach(function(aFilterList) {
      for (var i = aFilterList.filterCount - 1; i > -1; i--) {
        let filter = aFilterList.getFilterAt(i);
        filters.push({
          raw:        filter,
          serialized: this.serializeFilter(filter),
          list:       aFilterList,
          index:      i
        });
      }
    }, this);
    return filters;
  },
  serializeFilter : function(aFilter) {
    var serialized = '';
    var stream = {
      close: function() {},
      flush: function() {},
      write: function(aString, aCount) {
        serialized += aString;
        return aCount;
      },
      writeFrom: function(aInputStream, aCount) {
        return aCount;
      },
      isNonBlocking: function() {
        return false;
      }
    };
    aFilter.SaveToTextFile(stream);
    return decodeURIComponent(escape(serialized));
  },

  get allExistingFilterLists() {
    var filterLists = [];
    this.allIncomingServers.forEach(function(aIncomingServer) {
      try {
        filterLists.push(aIncomingServer.getFilterList(MailSession.topmostMsgWindow));
      }
      catch(e) {
        // this fails for local folder account
      }
    }, this);
    return filterLists;
  },

  get allIncomingServers() {
    var servers = [];
    this.allAccounts.forEach(function(aAccount) {
      if (aAccount.incomingServer)
        servers.push(aAccount.incomingServer);
    }, this);
    return servers;
  },

  get allAccounts() {
    return this.toArray(AccountManager.accounts, Ci.nsIMsgAccount);
  },
  get allAccountKeys() {
    return this.allAccounts.map(function(aAccount) {
      return this.getAccountKey(aAccount);
    }, this).filter(function(aKey) {
      return aKey != '';
    });
  },
  toArray: function (aEnumerator, aInterface) {
    aInterface = aInterface || Ci.nsISupports;
    var array = [];
    if (aEnumerator instanceof Ci.nsISupportsArray) {
      let count = aEnumerator.Count();
      for (let i = 0; i < count; i++) {
        array.push(aEnumerator.QueryElementAt(i, aInterface));
      }
    } else if (aEnumerator instanceof Ci.nsIArray) {
      let count = aEnumerator.length;
      for (let i = 0; i < count; i++) {
        array.push(aEnumerator.queryElementAt(i, aInterface));
      }
    } else if (aEnumerator instanceof Ci.nsISimpleEnumerator) {
      while (aEnumerator.hasMoreElements()) {
        array.push(aEnumerator.getNext().QueryInterface(aInterface));
      }
    }
    return array;
  },

  checkFilters : function()
  {
    if (this.checking)
      return;
    this.checking = true;

    var self = this;
    var changedCount = { value : 0 };
    var existingFilters = this.allExistingFilters;
    return Deferred.next(function() {
        return self.removeDisallowedFilters(existingFilters, changedCount);
      })
      .error(function(error) {
        Components.utils.reportError(error);
        gLogger.log('unexpected error: ' + error + '\n' + error.stack);
      })
      .next(function() {
        self.checking = false;
        if (changedCount.value > 0)
          self.restart();
      });
  },

  removeDisallowedFilters : function(aFilters, aChangedCount)
  {
    var prefEntries = prefs.getDescendants(BASE + 'disallow.patterns.');
    if (prefEntries.length == 0)
      return;

    var patterns = [];
    prefEntries.forEach(function(aKey) {
      var pattern = prefs.getPref(aKey);
      if (!pattern)
        return;

      patterns.push(pattern);
    }, this);

    if (patterns.length == 0)
      return;

    patterns = new RegExp(patterns.join('|'), 'im');

    var beforeCount = aChangedCount.value;
    for (var i = aFilters.length - 1; i > -1; i--) {
      let filter = aFilters[i];
      if (patterns.test(filter.serialized)) {
        filter.list.removeFilterAt(filter.index);
        aFilters.splice(i, 1);
        aChangedCount.value++;
      }
    }
    if (aChangedCount.value != beforeCount)
      this.allExistingFilterLists.forEach(function(aFilterList) {
        aFilterList.saveToDefaultFile();
      });
  },

  waitUntilStarted : function() {
    if (this.ready)
      return;

    this.waitUntilStarted_trigger = new Deferred();
    return this.waitUntilStarted_trigger;
  },

  restart : function()
  {
    gLogger.log('try to restart');
    Cc['@mozilla.org/toolkit/app-startup;1']
      .getService(Ci.nsIAppStartup)
      .quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit);
  },

  QueryInterface : function(aIID)
  {
    if(!aIID.equals(Ci.nsIObserver) &&
       !aIID.equals(Ci.nsISupports)) {
      throw Components.results.NS_ERROR_NO_INTERFACE;
    }
    return this;
  }
};

Components.utils.import('resource://gre/modules/XPCOMUtils.jsm');
var NSGetFactory = XPCOMUtils.generateNSGetFactory([ForceFilterStatusStartupService]);
<|endoftext|>


<commit_before>const kCID  = Components.ID('{cd2892e0-87a8-11e2-9e96-0800200c9a66}');
const kID   = '@clear-code.com/force-addon-status/startup;1';
const kNAME = 'ForceAddonStatusStartupService';

const Cc = Components.classes;
const Ci = Components.interfaces;

const ObserverService = Cc['@mozilla.org/observer-service;1']
                         .getService(Ci.nsIObserverService);

Components.utils.import('resource://gre/modules/AddonManager.jsm');

Components.utils.import('resource://force-addon-status-modules/lib/prefs.js');
Components.utils.import('resource://force-addon-status-modules/lib/jsdeferred.js');

const BASE = 'extensions.force-addon-status@clear-code.com.';

function ForceAddonStatusStartupService() {
}
ForceAddonStatusStartupService.prototype = {
  classID          : kCID,
  contractID       : kID,
  classDescription : kNAME,

  observe : function(aSubject, aTopic, aData)
  {
    switch (aTopic)
    {
      case 'profile-after-change':
        ObserverService.addObserver(this, 'final-ui-startup', false);
        return;

      case 'final-ui-startup':
        ObserverService.removeObserver(this, 'final-ui-startup');
        this.checkStatus();
        return;
    }
  },

  checkStatus : function()
  {
    var self = this;
    var changedCount = { value : 0 };
    this.checkExtensionsStatus(changedCount)
      .next(function() {
        return self.checkPluginsStatus(changedCount);
      })
      .next(function() {
        if (changedCount.value > 0) {
          self.restart();
        }
      })
      .error(function(error) {
        Components.utils.reportError(error);
      });
  },

  checkExtensionsStatus : function(aChangedCount)
  {
    var deferredTasks = [];

    var prefix = BASE + 'addons.';
    var keys = prefs.getDescendant(prefix);
    aChangedCount.value = aChangedCount.value || 0;
    keys.forEach(function(aKey) {
      var id = aKey.replace(prefix, '');
      var shouldBeActive = prefs.getPref(aKey);
      var deferred = new Deferred();
      AddonManager.getAddonByID(id, function(aAddon) {
        if (!aAddon || aAddon.isActive == shouldBeActive)
          return deferred.call();

        aAddon.userDisabled = !shouldBeActive;
        aChangedCount.value++;
        deferred.call();
      });
      deferredTasks.push(deferred);
    });

    if (deferredTasks.length > 0)
      return Deferred.parallel(deferredTasks);
    else
      return Deferred;
  },

  checkPluginsStatus : function(aChangedCount)
  {
    aChangedCount.value = aChangedCount.value || 0;

    var controlledPlugins = prefs.getChildren(BASE + 'plugins.');
    if (controlledPlugins.length == 0)
      return Deferred;

    var allPatterns = [];
    controlledPlugins = controlledPlugins.map(function(aEntryBaseKey) {
      var pattern = prefs.getPref(aEntryBaseKey + '.pattern');
      if (!pattern) return null;
      allPatterns.push(pattern);
      return {
        pattern :        new RegExp(pattern),
        shouldBeActive : prefs.getPref(aEntryBaseKey + '.status')
      };
    });
    controlledPlugins = controlledPlugins.filter(function(aControl) {
      return aControl;
    });

    allPatterns = new RegExp('(' + allPatterns.join('|') + ')');

    var PluginHost = Cc['@mozilla.org/plugin/host;1']
                      .getService(Ci.nsIPluginHost);
    var plugins = PluginHost.getPluginTags();
    plugins.forEach(function(aPluginTag) {
      if (!allPatterns.test(aPluginTag.name))
        return;

      controlledPlugins.some(function(aControl) {
        if (!aControl.pattern.test(aPluginTag.name))
          return false;
        if (aPluginTag.disabled == !aControl.shouldBeActive)
          return true;
        aPluginTag.disabled = !aControl.shouldBeActive;
        aChangedCount.value++;
        return true;
      });
    });

    return Deferred;
  },

  restart : function()
  {
    Cc['@mozilla.org/toolkit/app-startup;1']
      .getService(Ci.nsIAppStartup)
      .quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eForceQuit);
  },

  QueryInterface : function(aIID)
  {
    if(!aIID.equals(Ci.nsIObserver) &&
       !aIID.equals(Ci.nsISupports)) {
      throw Components.results.NS_ERROR_NO_INTERFACE;
    }
    return this;
  }
};

Components.utils.import('resource://gre/modules/XPCOMUtils.jsm');
var NSGetFactory = XPCOMUtils.generateNSGetFactory([ForceAddonStatusStartupService]);
<commit_msg>Make "blocklisted" status configurable<commit_after>const kCID  = Components.ID('{cd2892e0-87a8-11e2-9e96-0800200c9a66}');
const kID   = '@clear-code.com/force-addon-status/startup;1';
const kNAME = 'ForceAddonStatusStartupService';

const Cc = Components.classes;
const Ci = Components.interfaces;

const ObserverService = Cc['@mozilla.org/observer-service;1']
                         .getService(Ci.nsIObserverService);

Components.utils.import('resource://gre/modules/AddonManager.jsm');

Components.utils.import('resource://force-addon-status-modules/lib/prefs.js');
Components.utils.import('resource://force-addon-status-modules/lib/jsdeferred.js');

const BASE = 'extensions.force-addon-status@clear-code.com.';

function ForceAddonStatusStartupService() {
}
ForceAddonStatusStartupService.prototype = {
  classID          : kCID,
  contractID       : kID,
  classDescription : kNAME,

  observe : function(aSubject, aTopic, aData)
  {
    switch (aTopic)
    {
      case 'profile-after-change':
        ObserverService.addObserver(this, 'final-ui-startup', false);
        return;

      case 'final-ui-startup':
        ObserverService.removeObserver(this, 'final-ui-startup');
        this.checkStatus();
        return;
    }
  },

  checkStatus : function()
  {
    var self = this;
    var changedCount = { value : 0 };
    this.checkExtensionsStatus(changedCount)
      .next(function() {
        return self.checkPluginsStatus(changedCount);
      })
      .next(function() {
        if (changedCount.value > 0) {
          self.restart();
        }
      })
      .error(function(error) {
        Components.utils.reportError(error);
      });
  },

  checkExtensionsStatus : function(aChangedCount)
  {
    var deferredTasks = [];

    var prefix = BASE + 'addons.';
    var keys = prefs.getDescendant(prefix);
    aChangedCount.value = aChangedCount.value || 0;
    keys.forEach(function(aKey) {
      var id = aKey.replace(prefix, '');
      var shouldBeActive = prefs.getPref(aKey);
      var deferred = new Deferred();
      AddonManager.getAddonByID(id, function(aAddon) {
        if (!aAddon || aAddon.isActive == shouldBeActive)
          return deferred.call();

        aAddon.userDisabled = !shouldBeActive;
        aChangedCount.value++;
        deferred.call();
      });
      deferredTasks.push(deferred);
    });

    if (deferredTasks.length > 0)
      return Deferred.parallel(deferredTasks);
    else
      return Deferred;
  },

  checkPluginsStatus : function(aChangedCount)
  {
    aChangedCount.value = aChangedCount.value || 0;

    var controlledPlugins = prefs.getChildren(BASE + 'plugins.');
    if (controlledPlugins.length == 0)
      return Deferred;

    var allPatterns = [];
    controlledPlugins = controlledPlugins.map(function(aEntryBaseKey) {
      var pattern = prefs.getPref(aEntryBaseKey + '.pattern');
      if (!pattern) return null;
      allPatterns.push(pattern);
      return {
        pattern :        new RegExp(pattern),
        shouldBeActive : prefs.getPref(aEntryBaseKey + '.status'),
        blocklisted :    prefs.getPref(aEntryBaseKey + '.blocklisted')
      };
    });
    controlledPlugins = controlledPlugins.filter(function(aControl) {
      return aControl;
    });

    allPatterns = new RegExp('(' + allPatterns.join('|') + ')');

    var PluginHost = Cc['@mozilla.org/plugin/host;1']
                      .getService(Ci.nsIPluginHost);
    var plugins = PluginHost.getPluginTags();
    plugins.forEach(function(aPluginTag) {
      if (!allPatterns.test(aPluginTag.name))
        return;

      controlledPlugins.some(function(aControl) {
        if (!aControl.pattern.test(aPluginTag.name))
          return false;
        if (aPluginTag.disabled == !aControl.shouldBeActive &&
            aPluginTag.blocklisted === aControl.blocklisted)
          return true;
        aPluginTag.disabled = !aControl.shouldBeActive;
        if (aPluginTag.blocklisted !== null)
          aPluginTag.blocklisted = aControl.blocklisted;
        aChangedCount.value++;
        return true;
      });
    });

    return Deferred;
  },

  restart : function()
  {
    Cc['@mozilla.org/toolkit/app-startup;1']
      .getService(Ci.nsIAppStartup)
      .quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eForceQuit);
  },

  QueryInterface : function(aIID)
  {
    if(!aIID.equals(Ci.nsIObserver) &&
       !aIID.equals(Ci.nsISupports)) {
      throw Components.results.NS_ERROR_NO_INTERFACE;
    }
    return this;
  }
};

Components.utils.import('resource://gre/modules/XPCOMUtils.jsm');
var NSGetFactory = XPCOMUtils.generateNSGetFactory([ForceAddonStatusStartupService]);
<|endoftext|>


<commit_before>App.info({
  id: 'br.com.app.sistemameionorte',
  name: 'Meio Norte',
  description: 'Aplicativo Interativo Sistema Meio Norte de Comunicação',
  version: '0.1.35'
});

App.icons({

  // iOS
  // 'iphone': 'public/icons/ios/Icon-60.png',
  // 'iphone_2x': 'public/icons/ios/AppIcon.appiconset/Icon-60@2x.png',
  // 'ipad': 'public/icons/ios/Icon-72.png',
  // 'ipad_2x': 'public/icons/ios/Icon-72@2x.png',

  // Android
  'android_ldpi': 'public/icons/android/drawable-ldpi/ic_launcher.png',
  'android_mdpi': 'public/icons/android/drawable-mdpi/ic_launcher.png',
  'android_hdpi': 'public/icons/android/drawable-hdpi/ic_launcher.png',
  'android_xhdpi': 'public/icons/android/drawable-xhdpi/ic_launcher.png'

});

App.launchScreens({

  // iOS
  // 'iphone': 'public/images/splash/splash-320x480.png',
  // 'iphone_2x': 'public/images/splash/splash-320x480@2x.png',
  // 'iphone5': 'public/images/splash/splash-320x568@2x.png',
  // 'ipad_portrait': 'public/images/splash/splash-768x1024.png',
  // 'ipad_portrait_2x': 'public/images/splash/splash-768x1024@2x.png',
  // 'ipad_landscape': 'public/images/splash/splash-1024x768.png',
  // 'ipad_landscape_2x': 'public/images/splash/splash-1024x768@2x.png',

  // Android
  'android_ldpi_portrait': 'public/splash/android/drawable-ldpi/screen.png',
  'android_ldpi_landscape': 'public/splash/android/drawable-land-ldpi/screen.png',
  'android_mdpi_portrait': 'public/splash/android/drawable-mdpi/screen.png',
  'android_mdpi_landscape': 'public/splash/android/drawable-land-mdpi/screen.png',
  'android_hdpi_portrait': 'public/splash/android/drawable-hdpi/screen.png',
  'android_hdpi_landscape': 'public/splash/android/drawable-land-hdpi/screen.png',
  'android_xhdpi_portrait': 'public/splash/android/drawable-xhdpi/screen.png',
  'android_xhdpi_landscape': 'public/splash/android/drawable-land-xhdpi/screen.png'
});

App.setPreference('BackgroundColor', '0xffffffff');
App.setPreference('HideKeyboardFormAccessoryBar', true);
App.setPreference('StatusBarOverlaysWebView', 'true');
App.setPreference('StatusBarStyle', 'lightcontent');
App.setPreference("SplashScreenDelay", 20000);

// cordova plugin facebook
App.configurePlugin('com.phonegap.plugins.facebookconnect', {
     APP_ID: '580730978674615',
     APP_NAME: 'sistemamn'
});

// // regras de acesso cross origen
App.accessRule("*");
App.accessRule("*://*.meteor.com");
// //App.accessRule("*://localhost:3010/*");
<commit_msg>versao<commit_after>App.info({
  id: 'br.com.app.sistemameionorte',
  name: 'Meio Norte',
  description: 'Aplicativo Interativo Sistema Meio Norte de Comunicação',
  version: '0.1.39'
});

App.icons({

  // iOS
  // 'iphone': 'public/icons/ios/Icon-60.png',
  // 'iphone_2x': 'public/icons/ios/AppIcon.appiconset/Icon-60@2x.png',
  // 'ipad': 'public/icons/ios/Icon-72.png',
  // 'ipad_2x': 'public/icons/ios/Icon-72@2x.png',

  // Android
  'android_ldpi': 'public/icons/android/drawable-ldpi/ic_launcher.png',
  'android_mdpi': 'public/icons/android/drawable-mdpi/ic_launcher.png',
  'android_hdpi': 'public/icons/android/drawable-hdpi/ic_launcher.png',
  'android_xhdpi': 'public/icons/android/drawable-xhdpi/ic_launcher.png'

});

App.launchScreens({

  // iOS
  // 'iphone': 'public/images/splash/splash-320x480.png',
  // 'iphone_2x': 'public/images/splash/splash-320x480@2x.png',
  // 'iphone5': 'public/images/splash/splash-320x568@2x.png',
  // 'ipad_portrait': 'public/images/splash/splash-768x1024.png',
  // 'ipad_portrait_2x': 'public/images/splash/splash-768x1024@2x.png',
  // 'ipad_landscape': 'public/images/splash/splash-1024x768.png',
  // 'ipad_landscape_2x': 'public/images/splash/splash-1024x768@2x.png',

  // Android
  'android_ldpi_portrait': 'public/splash/android/drawable-ldpi/screen.png',
  'android_ldpi_landscape': 'public/splash/android/drawable-land-ldpi/screen.png',
  'android_mdpi_portrait': 'public/splash/android/drawable-mdpi/screen.png',
  'android_mdpi_landscape': 'public/splash/android/drawable-land-mdpi/screen.png',
  'android_hdpi_portrait': 'public/splash/android/drawable-hdpi/screen.png',
  'android_hdpi_landscape': 'public/splash/android/drawable-land-hdpi/screen.png',
  'android_xhdpi_portrait': 'public/splash/android/drawable-xhdpi/screen.png',
  'android_xhdpi_landscape': 'public/splash/android/drawable-land-xhdpi/screen.png'
});

App.setPreference('BackgroundColor', '0xffffffff');
App.setPreference('HideKeyboardFormAccessoryBar', true);
App.setPreference('StatusBarOverlaysWebView', 'true');
App.setPreference('StatusBarStyle', 'lightcontent');
App.setPreference("SplashScreenDelay", 20000);

// cordova plugin facebook
/*App.configurePlugin('com.phonegap.plugins.facebookconnect', {
     APP_ID: '580730978674615',
     APP_NAME: 'sistemamn'
});*/

/*App.configurePlugin('com.phonegap.plugins.facebookconnect', {
     APP_ID: '638977909535835',
     APP_NAME: 'vtv'
});*/

// // regras de acesso cross origen
App.accessRule("*");
App.accessRule("*://*.meteor.com");
// //App.accessRule("*://localhost:3010/*");
<|endoftext|>


<commit_before>import { pt, Color, Rectangle } from "lively.graphics";
import { Morph } from "lively.morphic";
import { signal, disconnect, connect } from "lively.bindings";
import { obj } from "lively.lang/index.js";

// var b = new Button({label: "test", extent: pt(22,18)}).openInWorld()
// b.scale = 10
// import { Icon } from "./icons.js";
// b.label = [Icon.textAttribute("plus")]
// b.padding = Rectangle.inset(2,4,2,5); b.fit()
// b.relayout()
// b.fit()

export class Button extends Morph {

  static get properties() {
    return {
      padding:      {defaultValue: Rectangle.inset(4,2)},
      borderWidth:  {defaultValue: 1},
      extent:       {defaultValue: pt(100,24)},
      borderRadius: {defaultValue: 15},
      borderColor:  {defaultValue: Color.rgb(204,204,204)},
      draggable:    {defaultValue: false},

      activeMode: {
        after: ["labelMorph", "activeStyle", "inactiveStyle"],
        get() {
           return this._buttonMode || 'active'
        },
        set(m) {
           switch(m) {
             case "active":
               this.activate();
               break;
             case "inactive":
               this.deactivate();
               break;
             case "triggered":
               this.perform();
               break;
           }
           this._buttonMode = m;
        }
      },
      // button label
      labelMorph: {
        after: ["submorphs"], derived: true,
        initialize() {
          this.labelMorph = this.addMorph({
            type: "label", name: "label",
            value: "no label yet", fill: Color.white.withA(0),
            padding: Rectangle.inset(0)
          });
        },
        get() { return this.getSubmorphNamed("label"); },
        set(labelMorph) {
          var existing = this.labelMorph;
          if (existing) {
            disconnect(this.labelMorph, "extent", this, "relayout");
            existing.remove();
          }
          labelMorph.name = "label";
          labelMorph.reactsToPointer = false;
          this.addMorphAt(labelMorph, 0);
          connect(this.labelMorph, 'extent', this, 'relayout');
        }
      },

      label: {
        after: ["labelMorph"], derived: true,
        get() { return this.labelMorph.textString; },
        set(stringOrAttributesOrMorph) {
          if (stringOrAttributesOrMorph.isMorph) {
            this.labelMorph = stringOrAttributesOrMorph;
          } else {
            this.labelMorph.value = stringOrAttributesOrMorph;
          }
        }
      },

      fontFamily: {
        after: ["label"],
        derived: true,
        get() { return this.labelMorph.fontFamily; },
        set(fontFamily) {
          this.labelMorph.fontFamily = fontFamily;
        }
      },

      fontSize: {
        after: ["label"],
        derived: true,
        get() { return this.labelMorph.fontSize; },
        set(fontSize) {
          this.labelMorph.fontSize = fontSize;
        }
      },

      fontStyle: {
        after: ["label"],
        derived: true,
        get() { return this.labelMorph.fontStyle; },
        set(fontStyle) {
          this.labelMorph.fontStyle = fontStyle;
        }
      },

      fontColor: {
        after: ["label"],
        derived: true,
        get() { return this.labelMorph.fontColor; },
        set(fontColor) {
          this.labelMorph.fontColor = fontColor;
        }
      },

      labelWithTextAttributes: {
        after: ["labelMorph"], derived: true,
        get() { return this.labelMorph.textAndAttributes; },
        set(val) { this.labelMorph.textAndAttributes = val; }
      },

      // button styles
      defaultActiveStyle: {
        defaultValue: {
          borderColor: Color.gray,
          fill: Color.rgb(240, 240, 240),
          fontColor: Color.almostBlack,
          nativeCursor: "pointer"
        }
      },
      activeStyle: {
        after: ["labelMorph", "fontSize", "fontColor", "fontFamily"],
        initialize() { this.activeStyle = obj.clone(this.defaultActiveStyle); },
        set(value) {
          this.setProperty("activeStyle", {
            ...this.defaultActiveStyle,
            ...obj.select(this, ["fontSize", "fontFamily", "fontColor"]),
            ...value,
          });
          if (this.isActive) this.updateButtonStyle(this.activeStyle);
        }
      },

      defaultInactiveStyle: {
        defaultValue: {
          borderColor: Color.gray.withA(0.5),
          fill: Color.rgba(240, 240, 240, 0.5),
          fontColor: Color.almostBlack.withA(0.5),
          nativeCursor: "not-allowed"
        }
      },
      inactiveStyle: {
        after: ["labelMorph", "fontSize", "fontColor", "fontFamily"],
        initialize() { this.inactiveStyle = obj.clone(this.defaultInactiveStyle); },
        set(value) {
          this.setProperty("inactiveStyle", {
            ...this.defaultInactiveStyle,
            ...obj.select(this, ["fontSize", "fontFamily", "fontColor"]),
            ...value,
          });
          if (this.activeMode == 'inactive') this.updateButtonStyle(this.inactiveStyle);
        }
      },

      defaultTriggerStyle: {
        defaultValue: {fill: Color.rgb(161,161,161)}
      },
      triggerStyle: {
        after: ["labelMorph", "fontSize", "fontColor", "fontFamily"],
        initialize() { this.triggerStyle = obj.clone(this.defaultTriggerStyle); },
        set(value) {
          this.setProperty("triggerStyle", {
            ...this.defaultTriggerStyle,
            ...obj.select(this, ["fontSize", "fontFamily", "fontColor"]),
            ...value,
          });
          if (this.activeMode == 'triggered') this.updateButtonStyle(this.triggerStyle);
        }
      }

    }
  }

  constructor(props) {
    super(props);
    this.updateButtonStyle(this.activeStyle)
    this.activeMode = 'active';
    this.relayout();
    connect(this, 'extent', this, 'relayout');
    connect(this, 'padding', this, 'relayout');
    connect(this, 'fontSize', this, 'relayout');
    connect(this, 'fontFamily', this, 'relayout');
  }

  get isButton() { return true }

  relayout() {
    var label = this.labelMorph;
    if (!label) return;
    var padding = this.padding,
        padT = padding.top(),
        padB = padding.bottom(),
        padL = padding.left(),
        padR = padding.right(),
        minHeight = label.height + padT + padB,
        minWidth = label.width + padL + padR;
    if (minHeight > this.height) this.height = minHeight;
    if (minWidth > this.width) this.width = minWidth;
    // label.center = pt(padL + label.width/2, padT + label.height/2);
    label.center = this.innerBounds().insetByRect(padding).center();
    return this;
  }

  updateButtonStyle(style) {
    Object.assign(this, style);
    this.labelMorph.nativeCursor = this.nativeCursor;
  }

  fit() {
    var padding = this.padding, label = this.labelMorph;
    label.fit();
    this.extent = padding.bottomLeft().addPt(padding.bottomRight()).addPt(label.extent);
    this.relayout();
    return this;
  }

  get isActive() {
    return this.activeMode == 'active'
  }

  get buttonStyleProps() {
    return ["fontStyle", "fontColor", "fontSize", "fontFamily",
            ...Object.keys(obj.dissoc(Morph.properties, ['position', 'extent', 'scale', 'rotation', 'visible']))]
  }

  cacheStyle() {
     const modeToCache = {
             inactive: 'inactiveStyle',
             active: 'activeStyle',
             triggered: 'triggerStyle'
           },
           cachedStyle = obj.select(this, this.buttonStyleProps);
    this[modeToCache[this.activeMode]] = cachedStyle;
  }

  activate() {
    this.cacheStyle();
    this.updateButtonStyle(this.activeStyle)
  }

  deactivate() {
    this.cacheStyle();
    this.updateButtonStyle(this.inactiveStyle)
  }

  perform() {
    this.cacheStyle();
    this.updateButtonStyle(this.triggerStyle)
  }

  trigger() {
    try {
      signal(this, "fire");
      typeof this.action == "function" && this.action();
    } catch (err) {
      var w = this.world();
      if (w) w.logError(err);
      else console.error(err);
    }
  }

  onMouseDown(evt) {
    if (this.isActive)
      this.activeMode = 'triggered'
  }

  onMouseUp(evt) {
    if (evt.isClickTarget(this) && this.activeMode == 'triggered') {
      this.activeMode = 'active';
      this.trigger();
    }
  }

  onHoverOut(evt) {
    // When leaving the button without mouse up, reset appearance
    if (this.activeMode == 'triggered' && evt.isClickTarget(this))
      this.activeMode = 'active';
  }

  onHoverIn(evt) {
    if (this.isActive && evt.isClickTarget(this))
      this.activeMode = 'triggered';
  }

}
<commit_msg>buttons: remove name from mode styling<commit_after>import { pt, Color, Rectangle } from "lively.graphics";
import { Morph } from "lively.morphic";
import { signal, disconnect, connect } from "lively.bindings";
import { obj } from "lively.lang/index.js";

// var b = new Button({label: "test", extent: pt(22,18)}).openInWorld()
// b.scale = 10
// import { Icon } from "./icons.js";
// b.label = [Icon.textAttribute("plus")]
// b.padding = Rectangle.inset(2,4,2,5); b.fit()
// b.relayout()
// b.fit()

export class Button extends Morph {

  static get properties() {
    return {
      padding:      {defaultValue: Rectangle.inset(4,2)},
      borderWidth:  {defaultValue: 1},
      extent:       {defaultValue: pt(100,24)},
      borderRadius: {defaultValue: 15},
      borderColor:  {defaultValue: Color.rgb(204,204,204)},
      draggable:    {defaultValue: false},

      activeMode: {
        after: ["labelMorph", "activeStyle", "inactiveStyle"],
        get() {
           return this._buttonMode || 'active'
        },
        set(m) {
           switch(m) {
             case "active":
               this.activate();
               break;
             case "inactive":
               this.deactivate();
               break;
             case "triggered":
               this.perform();
               break;
           }
           this._buttonMode = m;
        }
      },
      // button label
      labelMorph: {
        after: ["submorphs"], derived: true,
        initialize() {
          this.labelMorph = this.addMorph({
            type: "label", name: "label",
            value: "no label yet", fill: Color.white.withA(0),
            padding: Rectangle.inset(0)
          });
        },
        get() { return this.getSubmorphNamed("label"); },
        set(labelMorph) {
          var existing = this.labelMorph;
          if (existing) {
            disconnect(this.labelMorph, "extent", this, "relayout");
            existing.remove();
          }
          labelMorph.name = "label";
          labelMorph.reactsToPointer = false;
          this.addMorphAt(labelMorph, 0);
          connect(this.labelMorph, 'extent', this, 'relayout');
        }
      },

      label: {
        after: ["labelMorph"], derived: true,
        get() { return this.labelMorph.textString; },
        set(stringOrAttributesOrMorph) {
          if (stringOrAttributesOrMorph.isMorph) {
            this.labelMorph = stringOrAttributesOrMorph;
          } else {
            this.labelMorph.value = stringOrAttributesOrMorph;
          }
        }
      },

      fontFamily: {
        after: ["label"],
        derived: true,
        get() { return this.labelMorph.fontFamily; },
        set(fontFamily) {
          this.labelMorph.fontFamily = fontFamily;
        }
      },

      fontSize: {
        after: ["label"],
        derived: true,
        get() { return this.labelMorph.fontSize; },
        set(fontSize) {
          this.labelMorph.fontSize = fontSize;
        }
      },

      fontStyle: {
        after: ["label"],
        derived: true,
        get() { return this.labelMorph.fontStyle; },
        set(fontStyle) {
          this.labelMorph.fontStyle = fontStyle;
        }
      },

      fontColor: {
        after: ["label"],
        derived: true,
        get() { return this.labelMorph.fontColor; },
        set(fontColor) {
          this.labelMorph.fontColor = fontColor;
        }
      },

      labelWithTextAttributes: {
        after: ["labelMorph"], derived: true,
        get() { return this.labelMorph.textAndAttributes; },
        set(val) { this.labelMorph.textAndAttributes = val; }
      },

      // button styles
      defaultActiveStyle: {
        defaultValue: {
          borderColor: Color.gray,
          fill: Color.rgb(240, 240, 240),
          fontColor: Color.almostBlack,
          nativeCursor: "pointer"
        }
      },
      activeStyle: {
        after: ["labelMorph", "fontSize", "fontColor", "fontFamily"],
        initialize() { this.activeStyle = obj.clone(this.defaultActiveStyle); },
        set(value) {
          this.setProperty("activeStyle", {
            ...this.defaultActiveStyle,
            ...obj.select(this, ["fontSize", "fontFamily", "fontColor"]),
            ...value,
          });
          if (this.isActive) this.updateButtonStyle(this.activeStyle);
        }
      },

      defaultInactiveStyle: {
        defaultValue: {
          borderColor: Color.gray.withA(0.5),
          fill: Color.rgba(240, 240, 240, 0.5),
          fontColor: Color.almostBlack.withA(0.5),
          nativeCursor: "not-allowed"
        }
      },
      inactiveStyle: {
        after: ["labelMorph", "fontSize", "fontColor", "fontFamily"],
        initialize() { this.inactiveStyle = obj.clone(this.defaultInactiveStyle); },
        set(value) {
          this.setProperty("inactiveStyle", {
            ...this.defaultInactiveStyle,
            ...obj.select(this, ["fontSize", "fontFamily", "fontColor"]),
            ...value,
          });
          if (this.activeMode == 'inactive') this.updateButtonStyle(this.inactiveStyle);
        }
      },

      defaultTriggerStyle: {
        defaultValue: {fill: Color.rgb(161,161,161)}
      },
      triggerStyle: {
        after: ["labelMorph", "fontSize", "fontColor", "fontFamily"],
        initialize() { this.triggerStyle = obj.clone(this.defaultTriggerStyle); },
        set(value) {
          this.setProperty("triggerStyle", {
            ...this.defaultTriggerStyle,
            ...obj.select(this, ["fontSize", "fontFamily", "fontColor"]),
            ...value,
          });
          if (this.activeMode == 'triggered') this.updateButtonStyle(this.triggerStyle);
        }
      }

    }
  }

  constructor(props) {
    super(props);
    this.updateButtonStyle(this.activeStyle)
    this.activeMode = 'active';
    this.relayout();
    connect(this, 'extent', this, 'relayout');
    connect(this, 'padding', this, 'relayout');
    connect(this, 'fontSize', this, 'relayout');
    connect(this, 'fontFamily', this, 'relayout');
  }

  get isButton() { return true }

  relayout() {
    var label = this.labelMorph;
    if (!label) return;
    var padding = this.padding,
        padT = padding.top(),
        padB = padding.bottom(),
        padL = padding.left(),
        padR = padding.right(),
        minHeight = label.height + padT + padB,
        minWidth = label.width + padL + padR;
    if (minHeight > this.height) this.height = minHeight;
    if (minWidth > this.width) this.width = minWidth;
    // label.center = pt(padL + label.width/2, padT + label.height/2);
    label.center = this.innerBounds().insetByRect(padding).center();
    return this;
  }

  updateButtonStyle(style) {
    Object.assign(this, style);
    this.labelMorph.nativeCursor = this.nativeCursor;
  }

  fit() {
    var padding = this.padding, label = this.labelMorph;
    label.fit();
    this.extent = padding.bottomLeft().addPt(padding.bottomRight()).addPt(label.extent);
    this.relayout();
    return this;
  }

  get isActive() {
    return this.activeMode == 'active'
  }

  get buttonStyleProps() {
    return ["fontStyle", "fontColor", "fontSize", "fontFamily",
            ...Object.keys(obj.dissoc(Morph.properties, [
              'name', 'position', 'extent', 'scale', 'rotation', 'visible'
            ]))]
  }

  cacheStyle() {
     const modeToCache = {
             inactive: 'inactiveStyle',
             active: 'activeStyle',
             triggered: 'triggerStyle'
           },
           cachedStyle = obj.select(this, this.buttonStyleProps);
    this[modeToCache[this.activeMode]] = cachedStyle;
  }

  activate() {
    this.cacheStyle();
    this.updateButtonStyle(this.activeStyle)
  }

  deactivate() {
    this.cacheStyle();
    this.updateButtonStyle(this.inactiveStyle)
  }

  perform() {
    this.cacheStyle();
    this.updateButtonStyle(this.triggerStyle)
  }

  trigger() {
    try {
      signal(this, "fire");
      typeof this.action == "function" && this.action();
    } catch (err) {
      var w = this.world();
      if (w) w.logError(err);
      else console.error(err);
    }
  }

  onMouseDown(evt) {
    if (this.isActive)
      this.activeMode = 'triggered'
  }

  onMouseUp(evt) {
    if (evt.isClickTarget(this) && this.activeMode == 'triggered') {
      this.activeMode = 'active';
      this.trigger();
    }
  }

  onHoverOut(evt) {
    // When leaving the button without mouse up, reset appearance
    if (this.activeMode == 'triggered' && evt.isClickTarget(this))
      this.activeMode = 'active';
  }

  onHoverIn(evt) {
    if (this.isActive && evt.isClickTarget(this))
      this.activeMode = 'triggered';
  }

}
<|endoftext|>