<commit_before>/**
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * 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.
 */

// Bootstrap support, discarded after use
foam.boot = {

  // Temporary collection of classes to be updated later.
  classes: [],

  start: function() {
    global.CLASS = this.CLASS.bind(this);
  },

  getClass: (function() {
    /*
      Create or Update a Prototype from a psedo-Model definition.
      (Model is 'this').
    */
    var AbstractClass = {
      prototype: Object.prototype,
      axiomMap_: null,
      create: function create(args) {
        var obj = Object.create(this.prototype);
        obj.instance_ = Object.create(null);

        obj.initArgs(args);

        return obj;
      },
      installModel: function(m) {
        if ( m.axioms )
          for ( var i = 0 ; i < m.axioms.length ; i++ )
            this.installAxiom(m.axioms[i]);

        if ( m.methods )
          for ( var i = 0 ; i < m.methods.length ; i++ ) {
            var a = m.methods[i];
            if ( typeof a === 'function' )
              m.methods[i] = a = { name: a.name, code: a };
            this.prototype[a.name] = a.code;
          }

        if ( global.Property && m.properties )
          for ( var i = 0 ; i < m.properties.length ; i++ ) {
            var a = m.properties[i];
            if ( typeof a === 'string' ) m.properties[i] = a = { name: a };
            var type = global[(a.type || '') + 'Property'] || Property;
            this.installAxiom(type.create(a));
          }
      },
      installAxiom: function(a) {
        this.axiomMap_[a.name] = a;
        this.axiomCache_ = {};
        a.installInClass && a.installInClass(this);
        a.installInProto && a.installInProto(this.prototype);
      },
      isInstance: function isInstance(o) {
        return o.cls_ && this.isSubClass(o.cls_);
      },
      isSubClass: function isSubClass(o) {
        // TODO: switch from 'name' to 'id' when available
        if ( ! o ) return false;

        var subClasses_ = this.hasOwnProperty('subClasses_') ?
          this.subClasses_ :
          this.subClasses_ = {} ;

        if ( ! subClasses_.hasOwnProperty(o.name) )
          subClasses_[o.name] = ( o === this ) || this.isSubClass(o.__proto__);

        return subClasses_[o.name];
      },
      getAxiomByName: function(name) {
        return this.axiomMap_[name];
      },
      // The Following method will eventually change.
      // Would like to have efficient support for:
      //    .where() .orderBy() groupBy
      getAxiomsByClass: function(cls) {
        var as = this.axiomCache_[cls];
        if ( ! as ) {
          as = [];
          for ( var key in this.axiomMap_ ) {
            var a = this.axiomMap_[key];
            if ( cls.isInstance(a) )
              as.push(a);
          }
          this.axiomCache_[cls] = as;
        }
        return as;
      },
      toString: function() {
        return this.name + 'Class';
      }
    };

    return function getClass() {
      var cls = global[this.name];

      if ( ! cls ) {
        var parent = this.extends ? global[this.extends] : AbstractClass ;
        // TODO: make some of these values non-innumerable
        cls                  = Object.create(parent);
        cls.prototype        = Object.create(parent.prototype);
        cls.prototype.cls_   = cls;
        cls.prototype.model_ = this;
        cls.prototype.ID__   = this.name + 'Prototype';
        cls.ID__             = this.name + 'Class';
        cls.axiomMap_        = Object.create(parent.axiomMap_);
        cls.axiomCache_      = {};
        cls.name             = this.name;
        cls.model_           = this;
        global[cls.name]     = cls;
      }

      cls.installModel(this);

      return cls;
    };
  })(),

  // Bootstrap Model definition which records incomplete models
  // so they can be patched at the end of the bootstrap process.
  CLASS: function(m) {
    this.classes.push(this.getClass.call(m));
  },

  endPhase1: function() {
    // Upgrade to final CLASS() definition.
    global.CLASS = function(m) { return Model.create(m).getClass(); };

    // Upgrade existing classes to real classes.
    for ( var i = 0 ; i < this.classes.length ; i++ )
      CLASS(this.classes[i].model_);
  },

  end: function() {
    // Substitute AbstractClass.installModel() with simpler axiom-only version.
    FObject.__proto__.installModel = function installModel(m) {
      for ( var i = 0 ; i < m.axioms.length ; i++ )
        this.installAxiom(m.axioms[i]);
    };

    delete foam['boot'];
  }
};

foam.boot.start();

CLASS({
  name: 'FObject',

  documentation: 'Base model for model hierarchy.',

  methods: [
    function initArgs(args) {
      if ( ! args ) return;

      for ( var key in args )
        if ( key.indexOf('_') == -1 )
          this[key] = args[key];

      if ( args.instance_ )
        for ( var key in args.instance_ )
          this[key] = args.instance_[key];
    },
    function hasOwnProperty(name) {
      return Object.hasOwnProperty.call(this.instance_, name);
    }
  ]
});

CLASS({
  name: 'Model',
  extends: 'FObject', // Isn't the default yet.

  documentation: 'Class/Prototype description.',

  properties: [
    'name',
    {
      name: 'extends',
      defaultValue: 'FObject'
    },
    {
      // type: 'Array',
      name: 'axioms',
      factory: function() { return []; }
    },
    {
      type: 'Array',
      subType: 'Property',
      name: 'properties',
      adaptArrayElement: function(o) {
        var cls = this.type ? global[this.type + 'Property'] : Property;
        return cls.create(o);
      }
    },
    {
      type: 'Array',
      subType: 'Method',
      name: 'methods',
      adaptArrayElement: function(e) {
        if ( typeof e === 'function' ) {
          console.assert(e.name, 'Method must be named');
          return Method.create({name: e.name, code: e});
        }
        return e;
      }
    }
  ],

  methods: [ foam.boot.getClass ]
});

CLASS({
  name: 'Property',
  extends: 'FObject',

  properties: [
    'name', 'type', 'defaultValue', 'factory', 'adapt', 'preSet', 'postSet', 'expression'
  ],

  methods: [
    function installInClass(c) { c[foam.string.constantize(this.name)] = this; },
    function installInProto(proto) {
      /*
        Install a property onto a prototype from a Property definition.
        (Property is 'this').
      */
      var prop            = this;
      var name            = this.name;
      var adapt           = this.adapt
      var preSet          = this.preSet;
      var postSet         = this.postSet;
      var factory         = this.factory;
      var hasDefaultValue = this.hasOwnProperty('defaultValue');
      var defaultValue    = this.defaultValue;

      /* Future: needs events and slot support first.
         var slotName        = name + '$';
         Object.defineProperty(proto, slotName, {
         get: function propSlotGetter() {
         return this.getSlot(name);
         },
         set: function propSlotSetter(value) {
         value.link.link(this.getSlot(name));
         },
         configurable: true
         });
      */

      // TODO: implement 'expression'

      Object.defineProperty(proto, name, {
        get: prop.getter || function propGetter() {
          if ( ( hasDefaultValue || factory ) &&
               ! this.hasOwnProperty(name) )
          {
            if ( hasDefaultValue ) return defaultValue;

            var value = factory.call(this);
            this.instance_[name] = value;
            return value;
          }

          return this.instance_[name];
        },
        set: prop.setter || function propSetter(newValue) {
          // TODO: add logic to not trigger factory
          var oldValue = this[name];

          if ( adapt )  newValue = adapt.call(this, oldValue, newValue, prop);

          if ( preSet ) newValue = preSet.call(this, oldValue, newValue, prop);

          this.instance_[name] = newValue;

          // TODO: fire property change event

          // TODO: call global setter

          if ( postSet ) postSet.call(this, oldValue, newValue, prop);
        },
        configurable: true
      });
    }
  ]
});

CLASS({
  name: 'Method',
  extends: 'FObject',

  properties: [ 'name', 'code' ],

  methods: [
    function installInProto(proto) { proto[this.name] = this.code; }
  ]
});

CLASS({
  name: 'StringProperty',
  extends: 'Property',

  properties: [
    {
      name: 'defaultValue',
      defaultValue: ''
    },
    {
      name: 'preSet',
      defaultValue: function(_, a) { return a ? a.toString() : ''; }
    }
  ]
});

CLASS({
  name: 'ArrayProperty',
  extends: 'Property',

  properties: [
    'subType',
    {
      name: 'factory',
      defaultValue: function() { return []; }
    },
    {
      name: 'adapt',
      defaultValue: function(_, a, prop) {
        if ( ! a ) return [];
        return a.map(prop.adaptArrayElement.bind(prop));
      }
    },
    {
      name: 'adaptArrayElement',
      defaultValue: function(o) {
        return global[this.subType].create(o);
      }
    }
  ]
});

foam.boot.endPhase1();

CLASS({
  name: 'FObject',

  methods: [
    function clearProperty(name) { delete this.instance_[name]; },
    function toString() {
      // Distinguish between prototypes and instances.
      return this.cls_.name + (this.instance_ ? '' : 'Proto')
    }
  ],

  // TODO: insert core/FObject.js functionality

  // TODO: insert EventService and PropertyChangeSupport here
});

CLASS({
  name: 'AxiomArrayProperty',
  extends: 'ArrayProperty',

  properties: [
    {
      name: 'postSet',
      defaultValue: function(_, a) {
        (this.axioms || (this.axioms = [])).push.apply(this.axioms, a); }
    }
  ]
});

CLASS({
  name: 'Constant',

  properties: [
    { name: 'name'  },
    { name: 'value' }
  ],

  methods: [
    function installInClass(cls)   { cls[foam.string.constantize(this.name)]   = this.value; },
    function installInProto(proto) { proto[foam.string.constantize(this.name)] = this.value; }
  ]
});

CLASS({
  name: 'Model',

  properties: [
    {
      type: 'AxiomArray',
      subType: 'Constant',
      name: 'constants',
      adapt: function(_, a, prop) {
        if ( ! a ) return [];
        if ( ! Array.isArray(a) ) {
          var cs = [];
          for ( var key in a )
            cs.push(Constant.create({name: key, value: a[key]}));
          return cs;
        }
        return a.map(prop.adaptArrayElement.bind(prop));
      }
    },
    {
      type: 'AxiomArray',
      subType: 'Property',
      name: 'properties',
      adaptArrayElement: function(o) {
        return typeof o === 'string'     ?
          Property.create({name: o})     :
          global[this.subType].create(o) ;
      }
    },
    {
      type: 'AxiomArray',
      subType: 'Method',
      name: 'methods',
      adaptArrayElement: function(o) {
        if ( typeof o === 'function' ) {
          console.assert(o.name, 'Method must be named');
          return Method.create({name: o.name, code: o});
        }
        return global[this.subType].create(o);
      }
    }
  ],

  methods: [
    function initArgs(args) {
      if ( ! args ) return;

      if ( args.__proto__ === Object.prototype ) {
        for ( var key in args )
          this[key] = args[key];
      } else if ( args.instance_ ) {
        for ( var key in args.instance_ )
          this[key] = args.instance_[key];
      } else {
        // TODO: walk Properties or initAgents?
        for ( var key in args )
          this[key] = args[key];
      }
    }
  ]
});

foam.boot.end();

/*
  TODO:
  - Implement Property Overriding
  - replace initArgs() in boot.end()
  - SUPER support
  - Axiom query support
  - Add package and id to Model and Class
  - Proxy id, name, package, label, plural from Class to Model
*/
<commit_msg>FO3: Added Class.describe().<commit_after>/**
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * 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.
 */

// Bootstrap support, discarded after use
foam.boot = {

  // Temporary collection of classes to be updated later.
  classes: [],

  start: function() {
    global.CLASS = this.CLASS.bind(this);
  },

  getClass: (function() {
    /*
      Create or Update a Prototype from a psedo-Model definition.
      (Model is 'this').
    */
    var AbstractClass = {
      prototype: Object.prototype,
      axiomMap_: null,
      create: function create(args) {
        var obj = Object.create(this.prototype);
        obj.instance_ = Object.create(null);

        obj.initArgs(args);

        return obj;
      },
      installModel: function(m) {
        if ( m.axioms )
          for ( var i = 0 ; i < m.axioms.length ; i++ )
            this.installAxiom(m.axioms[i]);

        if ( m.methods )
          for ( var i = 0 ; i < m.methods.length ; i++ ) {
            var a = m.methods[i];
            if ( typeof a === 'function' )
              m.methods[i] = a = { name: a.name, code: a };
            this.prototype[a.name] = a.code;
          }

        if ( global.Property && m.properties )
          for ( var i = 0 ; i < m.properties.length ; i++ ) {
            var a = m.properties[i];
            if ( typeof a === 'string' ) m.properties[i] = a = { name: a };
            var type = global[(a.type || '') + 'Property'] || Property;
            this.installAxiom(type.create(a));
          }
      },
      installAxiom: function(a) {
        this.axiomMap_[a.name] = a;
        this.axiomCache_ = {};
        a.installInClass && a.installInClass(this);
        a.installInProto && a.installInProto(this.prototype);
      },
      isInstance: function isInstance(o) {
        return o.cls_ && this.isSubClass(o.cls_);
      },
      isSubClass: function isSubClass(o) {
        // TODO: switch from 'name' to 'id' when available
        if ( ! o ) return false;

        var subClasses_ = this.hasOwnProperty('subClasses_') ?
          this.subClasses_ :
          this.subClasses_ = {} ;

        if ( ! subClasses_.hasOwnProperty(o.name) )
          subClasses_[o.name] = ( o === this ) || this.isSubClass(o.__proto__);

        return subClasses_[o.name];
      },
      describe: function(opt_name) {
        for ( var key in this.axiomMap_ ) {
          var a = this.axiomMap_[key];
          console.log(a.cls_.name, '\t', a.name);
        }
      },
      getAxiomByName: function(name) {
        return this.axiomMap_[name];
      },
      // The Following method will eventually change.
      // Would like to have efficient support for:
      //    .where() .orderBy() groupBy
      getAxiomsByClass: function(cls) {
        var as = this.axiomCache_[cls];
        if ( ! as ) {
          as = [];
          for ( var key in this.axiomMap_ ) {
            var a = this.axiomMap_[key];
            if ( cls.isInstance(a) )
              as.push(a);
          }
          this.axiomCache_[cls] = as;
        }
        return as;
      },
      toString: function() {
        return this.name + 'Class';
      }
    };

    return function getClass() {
      var cls = global[this.name];

      if ( ! cls ) {
        var parent = this.extends ? global[this.extends] : AbstractClass ;
        // TODO: make some of these values non-innumerable
        cls                  = Object.create(parent);
        cls.prototype        = Object.create(parent.prototype);
        cls.prototype.cls_   = cls;
        cls.prototype.model_ = this;
        cls.prototype.ID__   = this.name + 'Prototype';
        cls.ID__             = this.name + 'Class';
        cls.axiomMap_        = Object.create(parent.axiomMap_);
        cls.axiomCache_      = {};
        cls.name             = this.name;
        cls.model_           = this;
        global[cls.name]     = cls;
      }

      cls.installModel(this);

      return cls;
    };
  })(),

  // Bootstrap Model definition which records incomplete models
  // so they can be patched at the end of the bootstrap process.
  CLASS: function(m) {
    this.classes.push(this.getClass.call(m));
  },

  endPhase1: function() {
    // Upgrade to final CLASS() definition.
    global.CLASS = function(m) { return Model.create(m).getClass(); };

    // Upgrade existing classes to real classes.
    for ( var i = 0 ; i < this.classes.length ; i++ )
      CLASS(this.classes[i].model_);
  },

  end: function() {
    // Substitute AbstractClass.installModel() with simpler axiom-only version.
    FObject.__proto__.installModel = function installModel(m) {
      for ( var i = 0 ; i < m.axioms.length ; i++ )
        this.installAxiom(m.axioms[i]);
    };

    delete foam['boot'];
  }
};

foam.boot.start();

CLASS({
  name: 'FObject',

  documentation: 'Base model for model hierarchy.',

  methods: [
    function initArgs(args) {
      if ( ! args ) return;

      for ( var key in args )
        if ( key.indexOf('_') == -1 )
          this[key] = args[key];

      if ( args.instance_ )
        for ( var key in args.instance_ )
          this[key] = args.instance_[key];
    },
    function hasOwnProperty(name) {
      return Object.hasOwnProperty.call(this.instance_, name);
    }
  ]
});

CLASS({
  name: 'Model',
  extends: 'FObject', // Isn't the default yet.

  documentation: 'Class/Prototype description.',

  properties: [
    'name',
    {
      name: 'extends',
      defaultValue: 'FObject'
    },
    {
      // type: 'Array',
      name: 'axioms',
      factory: function() { return []; }
    },
    {
      type: 'Array',
      subType: 'Property',
      name: 'properties',
      adaptArrayElement: function(o) {
        var cls = this.type ? global[this.type + 'Property'] : Property;
        return cls.create(o);
      }
    },
    {
      type: 'Array',
      subType: 'Method',
      name: 'methods',
      adaptArrayElement: function(e) {
        if ( typeof e === 'function' ) {
          console.assert(e.name, 'Method must be named');
          return Method.create({name: e.name, code: e});
        }
        return e;
      }
    }
  ],

  methods: [ foam.boot.getClass ]
});

CLASS({
  name: 'Property',
  extends: 'FObject',

  properties: [
    'name', 'type', 'defaultValue', 'factory', 'adapt', 'preSet', 'postSet', 'expression'
  ],

  methods: [
    function installInClass(c) { c[foam.string.constantize(this.name)] = this; },
    function installInProto(proto) {
      /*
        Install a property onto a prototype from a Property definition.
        (Property is 'this').
      */
      var prop            = this;
      var name            = this.name;
      var adapt           = this.adapt
      var preSet          = this.preSet;
      var postSet         = this.postSet;
      var factory         = this.factory;
      var hasDefaultValue = this.hasOwnProperty('defaultValue');
      var defaultValue    = this.defaultValue;

      /* Future: needs events and slot support first.
         var slotName        = name + '$';
         Object.defineProperty(proto, slotName, {
         get: function propSlotGetter() {
         return this.getSlot(name);
         },
         set: function propSlotSetter(value) {
         value.link.link(this.getSlot(name));
         },
         configurable: true
         });
      */

      // TODO: implement 'expression'

      Object.defineProperty(proto, name, {
        get: prop.getter || function propGetter() {
          if ( ( hasDefaultValue || factory ) &&
               ! this.hasOwnProperty(name) )
          {
            if ( hasDefaultValue ) return defaultValue;

            var value = factory.call(this);
            this.instance_[name] = value;
            return value;
          }

          return this.instance_[name];
        },
        set: prop.setter || function propSetter(newValue) {
          // TODO: add logic to not trigger factory
          var oldValue = this[name];

          if ( adapt )  newValue = adapt.call(this, oldValue, newValue, prop);

          if ( preSet ) newValue = preSet.call(this, oldValue, newValue, prop);

          this.instance_[name] = newValue;

          // TODO: fire property change event

          // TODO: call global setter

          if ( postSet ) postSet.call(this, oldValue, newValue, prop);
        },
        configurable: true
      });
    }
  ]
});

CLASS({
  name: 'Method',
  extends: 'FObject',

  properties: [ 'name', 'code' ],

  methods: [
    function installInProto(proto) { proto[this.name] = this.code; }
  ]
});

CLASS({
  name: 'StringProperty',
  extends: 'Property',

  properties: [
    {
      name: 'defaultValue',
      defaultValue: ''
    },
    {
      name: 'preSet',
      defaultValue: function(_, a) { return a ? a.toString() : ''; }
    }
  ]
});

CLASS({
  name: 'ArrayProperty',
  extends: 'Property',

  properties: [
    'subType',
    {
      name: 'factory',
      defaultValue: function() { return []; }
    },
    {
      name: 'adapt',
      defaultValue: function(_, a, prop) {
        if ( ! a ) return [];
        return a.map(prop.adaptArrayElement.bind(prop));
      }
    },
    {
      name: 'adaptArrayElement',
      defaultValue: function(o) {
        return global[this.subType].create(o);
      }
    }
  ]
});

foam.boot.endPhase1();

CLASS({
  name: 'FObject',

  methods: [
    function clearProperty(name) { delete this.instance_[name]; },
    function toString() {
      // Distinguish between prototypes and instances.
      return this.cls_.name + (this.instance_ ? '' : 'Proto')
    }
  ],

  // TODO: insert core/FObject.js functionality

  // TODO: insert EventService and PropertyChangeSupport here
});

CLASS({
  name: 'AxiomArrayProperty',
  extends: 'ArrayProperty',

  properties: [
    {
      name: 'postSet',
      defaultValue: function(_, a) {
        (this.axioms || (this.axioms = [])).push.apply(this.axioms, a); }
    }
  ]
});

CLASS({
  name: 'Constant',

  properties: [
    { name: 'name'  },
    { name: 'value' }
  ],

  methods: [
    function installInClass(cls)   { cls[foam.string.constantize(this.name)]   = this.value; },
    function installInProto(proto) { proto[foam.string.constantize(this.name)] = this.value; }
  ]
});

CLASS({
  name: 'Model',

  properties: [
    {
      type: 'AxiomArray',
      subType: 'Constant',
      name: 'constants',
      adapt: function(_, a, prop) {
        if ( ! a ) return [];
        if ( ! Array.isArray(a) ) {
          var cs = [];
          for ( var key in a )
            cs.push(Constant.create({name: key, value: a[key]}));
          return cs;
        }
        return a.map(prop.adaptArrayElement.bind(prop));
      }
    },
    {
      type: 'AxiomArray',
      subType: 'Property',
      name: 'properties',
      adaptArrayElement: function(o) {
        return typeof o === 'string'     ?
          Property.create({name: o})     :
          global[this.subType].create(o) ;
      }
    },
    {
      type: 'AxiomArray',
      subType: 'Method',
      name: 'methods',
      adaptArrayElement: function(o) {
        if ( typeof o === 'function' ) {
          console.assert(o.name, 'Method must be named');
          return Method.create({name: o.name, code: o});
        }
        return global[this.subType].create(o);
      }
    }
  ],

  methods: [
    function initArgs(args) {
      if ( ! args ) return;

      if ( args.__proto__ === Object.prototype ) {
        for ( var key in args )
          this[key] = args[key];
      } else if ( args.instance_ ) {
        for ( var key in args.instance_ )
          this[key] = args.instance_[key];
      } else {
        // TODO: walk Properties or initAgents?
        for ( var key in args )
          this[key] = args[key];
      }
    }
  ]
});

foam.boot.end();

/*
  TODO:
  - Implement Property Overriding
  - replace initArgs() in boot.end()
  - SUPER support
  - Axiom query support
  - Add package and id to Model and Class
  - Proxy id, name, package, label, plural from Class to Model
*/
<|endoftext|>


<commit_before>/* @flow strict-local */
import { Clipboard, Share } from 'react-native';
import type { Auth, Dispatch, GetText, Message, Narrow, Subscription } from '../types';
import type { BackgroundData } from '../webview/MessageList';
import { getNarrowFromMessage, isHomeNarrow, isSpecialNarrow } from '../utils/narrow';
import { isTopicMuted } from '../utils/message';
/* eslint-disable import/no-named-as-default-member */
import api, { getMessageContentById, toggleMuteStream, toggleMessageStarred } from '../api';
import { showToast } from '../utils/info';
import { doNarrow, startEditMessage, deleteOutboxMessage, navigateToEmojiPicker } from '../actions';

/** Description of a possible option for the action sheet. */
type ButtonDescription = {
  /** The callback. */
  ({
    auth: Auth,
    message: Message,
    subscriptions: Subscription[],
    dispatch: Dispatch,
    _: GetText,
  }): void | Promise<void>,
  title: string,
};

const isAnOutboxMessage = (message: Message): boolean => message.isOutbox;

//
// Options for the action sheet go below: ...
//

const reply = ({ message, dispatch, auth }) => {
  dispatch(doNarrow(getNarrowFromMessage(message, auth.email), message.id));
};
reply.title = 'Reply';

const copyToClipboard = async ({ _, auth, message }) => {
  const rawMessage = isAnOutboxMessage(message) /* $FlowFixMe: then really type Outbox */
    ? message.markdownContent
    : await getMessageContentById(auth, message.id);
  Clipboard.setString(rawMessage);
  showToast(_('Message copied'));
};
copyToClipboard.title = 'Copy to clipboard';

const editMessage = async ({ message, dispatch }) => {
  dispatch(startEditMessage(message.id, message.subject));
};
editMessage.title = 'Edit message';

const deleteMessage = async ({ auth, message, dispatch }) => {
  if (isAnOutboxMessage(message)) {
    dispatch(deleteOutboxMessage(message.timestamp));
  } else {
    api.deleteMessage(auth, message.id);
  }
};
deleteMessage.title = 'Delete message';

const unmuteTopic = ({ auth, message }) => {
  api.unmuteTopic(auth, message.display_recipient, message.subject);
};
unmuteTopic.title = 'Unmute topic';

const muteTopic = ({ auth, message }) => {
  api.muteTopic(auth, message.display_recipient, message.subject);
};
muteTopic.title = 'Mute topic';

const unmuteStream = ({ auth, message, subscriptions }) => {
  const sub = subscriptions.find(x => x.name === message.display_recipient);
  if (sub) {
    toggleMuteStream(auth, sub.stream_id, false);
  }
};
unmuteStream.title = 'Unmute stream';

const muteStream = ({ auth, message, subscriptions }) => {
  const sub = subscriptions.find(x => x.name === message.display_recipient);
  if (sub) {
    toggleMuteStream(auth, sub.stream_id, true);
  }
};
muteStream.title = 'Mute stream';

const starMessage = ({ auth, message }) => {
  toggleMessageStarred(auth, [message.id], true);
};
starMessage.title = 'Star message';

const unstarMessage = ({ auth, message }) => {
  toggleMessageStarred(auth, [message.id], false);
};
unstarMessage.title = 'Unstar message';

const shareMessage = ({ message }) => {
  Share.share({
    message: message.content.replace(/<(?:.|\n)*?>/gm, ''),
  });
};
shareMessage.title = 'Share message';

const addReaction = ({ message, dispatch }) => {
  dispatch(navigateToEmojiPicker(message.id));
};
addReaction.title = 'Add reaction';

const cancel = params => {};
cancel.title = 'Cancel';

const allButtonsRaw = {
  // For messages
  addReaction,
  reply,
  copyToClipboard,
  shareMessage,
  editMessage,
  deleteMessage,
  starMessage,
  unstarMessage,

  // For headers
  unmuteTopic,
  muteTopic,
  muteStream,
  unmuteStream,

  // All
  cancel,
};

//
// ... End of options for the action sheet.
//

type ButtonCode = $Keys<typeof allButtonsRaw>;

const allButtons: { [ButtonCode]: ButtonDescription } = allButtonsRaw;

type ConstructSheetParams = {
  backgroundData: BackgroundData,
  message: Message,
  narrow: Narrow,
};

export const constructHeaderActionButtons = ({
  backgroundData: { mute, subscriptions },
  message,
}: ConstructSheetParams): ButtonCode[] => {
  const buttons: ButtonCode[] = [];
  if (message.type === 'stream') {
    if (isTopicMuted(message.display_recipient, message.subject, mute)) {
      buttons.push('unmuteTopic');
    } else {
      buttons.push('muteTopic');
    }
    const sub = subscriptions.find(x => x.name === message.display_recipient);
    if (sub && !sub.in_home_view) {
      buttons.push('unmuteStream');
    } else {
      buttons.push('muteStream');
    }
  }
  buttons.push('cancel');
  return buttons;
};

const messageNotDeleted = (message: Message): boolean => message.content !== '
(deleted)
';

export const constructMessageActionButtons = ({
  backgroundData: { auth, flags },
  message,
  narrow,
}: ConstructSheetParams): ButtonCode[] => {
  const buttons = [];
  if (!isAnOutboxMessage(message) && messageNotDeleted(message)) {
    buttons.push('addReaction');
  }
  if (!isAnOutboxMessage(message)) {
    buttons.push('reply');
  }
  if (messageNotDeleted(message)) {
    buttons.push('copyToClipboard');
    buttons.push('shareMessage');
  }
  if (
    !isAnOutboxMessage(message)
    && message.sender_email === auth.email
    && !isHomeNarrow(narrow)
    && !isSpecialNarrow(narrow)
  ) {
    buttons.push('editMessage');
  }
  if (message.sender_email === auth.email && messageNotDeleted(message)) {
    buttons.push('deleteMessage');
  }
  if (!isAnOutboxMessage(message)) {
    if (message.id in flags.starred) {
      buttons.push('unstarMessage');
    } else {
      buttons.push('starMessage');
    }
  }
  buttons.push('cancel');
  return buttons;
};

/** Invoke the given callback to show an appropriate action sheet. */
export const showActionSheet = (
  isHeader: boolean,
  dispatch: Dispatch,
  showActionSheetWithOptions: (
    { options: string[], cancelButtonIndex: number },
    (number) => void,
  ) => void,
  _: GetText,
  params: ConstructSheetParams,
): void => {
  const optionCodes = isHeader
    ? constructHeaderActionButtons(params)
    : constructMessageActionButtons(params);
  const callback = buttonIndex => {
    allButtons[optionCodes[buttonIndex]]({
      dispatch,
      subscriptions: params.backgroundData.subscriptions,
      auth: params.backgroundData.auth,
      _,
      ...params,
    });
  };
  showActionSheetWithOptions(
    {
      options: optionCodes.map(code => _(allButtons[code].title)),
      cancelButtonIndex: optionCodes.length - 1,
    },
    callback,
  );
};
<commit_msg>messageActionSheet: Fix accidentally-changed messages.<commit_after>/* @flow strict-local */
import { Clipboard, Share } from 'react-native';
import type { Auth, Dispatch, GetText, Message, Narrow, Subscription } from '../types';
import type { BackgroundData } from '../webview/MessageList';
import { getNarrowFromMessage, isHomeNarrow, isSpecialNarrow } from '../utils/narrow';
import { isTopicMuted } from '../utils/message';
/* eslint-disable import/no-named-as-default-member */
import api, { getMessageContentById, toggleMuteStream, toggleMessageStarred } from '../api';
import { showToast } from '../utils/info';
import { doNarrow, startEditMessage, deleteOutboxMessage, navigateToEmojiPicker } from '../actions';

/** Description of a possible option for the action sheet. */
type ButtonDescription = {
  /** The callback. */
  ({
    auth: Auth,
    message: Message,
    subscriptions: Subscription[],
    dispatch: Dispatch,
    _: GetText,
  }): void | Promise<void>,
  title: string,
};

const isAnOutboxMessage = (message: Message): boolean => message.isOutbox;

//
// Options for the action sheet go below: ...
//

const reply = ({ message, dispatch, auth }) => {
  dispatch(doNarrow(getNarrowFromMessage(message, auth.email), message.id));
};
reply.title = 'Reply';

const copyToClipboard = async ({ _, auth, message }) => {
  const rawMessage = isAnOutboxMessage(message) /* $FlowFixMe: then really type Outbox */
    ? message.markdownContent
    : await getMessageContentById(auth, message.id);
  Clipboard.setString(rawMessage);
  showToast(_('Message copied'));
};
copyToClipboard.title = 'Copy to clipboard';

const editMessage = async ({ message, dispatch }) => {
  dispatch(startEditMessage(message.id, message.subject));
};
editMessage.title = 'Edit message';

const deleteMessage = async ({ auth, message, dispatch }) => {
  if (isAnOutboxMessage(message)) {
    dispatch(deleteOutboxMessage(message.timestamp));
  } else {
    api.deleteMessage(auth, message.id);
  }
};
deleteMessage.title = 'Delete message';

const unmuteTopic = ({ auth, message }) => {
  api.unmuteTopic(auth, message.display_recipient, message.subject);
};
unmuteTopic.title = 'Unmute topic';

const muteTopic = ({ auth, message }) => {
  api.muteTopic(auth, message.display_recipient, message.subject);
};
muteTopic.title = 'Mute topic';

const unmuteStream = ({ auth, message, subscriptions }) => {
  const sub = subscriptions.find(x => x.name === message.display_recipient);
  if (sub) {
    toggleMuteStream(auth, sub.stream_id, false);
  }
};
unmuteStream.title = 'Unmute stream';

const muteStream = ({ auth, message, subscriptions }) => {
  const sub = subscriptions.find(x => x.name === message.display_recipient);
  if (sub) {
    toggleMuteStream(auth, sub.stream_id, true);
  }
};
muteStream.title = 'Mute stream';

const starMessage = ({ auth, message }) => {
  toggleMessageStarred(auth, [message.id], true);
};
starMessage.title = 'Star message';

const unstarMessage = ({ auth, message }) => {
  toggleMessageStarred(auth, [message.id], false);
};
unstarMessage.title = 'Unstar message';

const shareMessage = ({ message }) => {
  Share.share({
    message: message.content.replace(/<(?:.|\n)*?>/gm, ''),
  });
};
shareMessage.title = 'Share';

const addReaction = ({ message, dispatch }) => {
  dispatch(navigateToEmojiPicker(message.id));
};
addReaction.title = 'Add a reaction';

const cancel = params => {};
cancel.title = 'Cancel';

const allButtonsRaw = {
  // For messages
  addReaction,
  reply,
  copyToClipboard,
  shareMessage,
  editMessage,
  deleteMessage,
  starMessage,
  unstarMessage,

  // For headers
  unmuteTopic,
  muteTopic,
  muteStream,
  unmuteStream,

  // All
  cancel,
};

//
// ... End of options for the action sheet.
//

type ButtonCode = $Keys<typeof allButtonsRaw>;

const allButtons: { [ButtonCode]: ButtonDescription } = allButtonsRaw;

type ConstructSheetParams = {
  backgroundData: BackgroundData,
  message: Message,
  narrow: Narrow,
};

export const constructHeaderActionButtons = ({
  backgroundData: { mute, subscriptions },
  message,
}: ConstructSheetParams): ButtonCode[] => {
  const buttons: ButtonCode[] = [];
  if (message.type === 'stream') {
    if (isTopicMuted(message.display_recipient, message.subject, mute)) {
      buttons.push('unmuteTopic');
    } else {
      buttons.push('muteTopic');
    }
    const sub = subscriptions.find(x => x.name === message.display_recipient);
    if (sub && !sub.in_home_view) {
      buttons.push('unmuteStream');
    } else {
      buttons.push('muteStream');
    }
  }
  buttons.push('cancel');
  return buttons;
};

const messageNotDeleted = (message: Message): boolean => message.content !== '
(deleted)
';

export const constructMessageActionButtons = ({
  backgroundData: { auth, flags },
  message,
  narrow,
}: ConstructSheetParams): ButtonCode[] => {
  const buttons = [];
  if (!isAnOutboxMessage(message) && messageNotDeleted(message)) {
    buttons.push('addReaction');
  }
  if (!isAnOutboxMessage(message)) {
    buttons.push('reply');
  }
  if (messageNotDeleted(message)) {
    buttons.push('copyToClipboard');
    buttons.push('shareMessage');
  }
  if (
    !isAnOutboxMessage(message)
    && message.sender_email === auth.email
    && !isHomeNarrow(narrow)
    && !isSpecialNarrow(narrow)
  ) {
    buttons.push('editMessage');
  }
  if (message.sender_email === auth.email && messageNotDeleted(message)) {
    buttons.push('deleteMessage');
  }
  if (!isAnOutboxMessage(message)) {
    if (message.id in flags.starred) {
      buttons.push('unstarMessage');
    } else {
      buttons.push('starMessage');
    }
  }
  buttons.push('cancel');
  return buttons;
};

/** Invoke the given callback to show an appropriate action sheet. */
export const showActionSheet = (
  isHeader: boolean,
  dispatch: Dispatch,
  showActionSheetWithOptions: (
    { options: string[], cancelButtonIndex: number },
    (number) => void,
  ) => void,
  _: GetText,
  params: ConstructSheetParams,
): void => {
  const optionCodes = isHeader
    ? constructHeaderActionButtons(params)
    : constructMessageActionButtons(params);
  const callback = buttonIndex => {
    allButtons[optionCodes[buttonIndex]]({
      dispatch,
      subscriptions: params.backgroundData.subscriptions,
      auth: params.backgroundData.auth,
      _,
      ...params,
    });
  };
  showActionSheetWithOptions(
    {
      options: optionCodes.map(code => _(allButtons[code].title)),
      cancelButtonIndex: optionCodes.length - 1,
    },
    callback,
  );
};
<|endoftext|>


<commit_before>/*=============================
dromos javascript library core.

This class is loaded by the dromos Bootstrap and should not
be included directly in a page

=============================*/
define(["jquery", "dromos.utilities"], function($jQ, utilities)
{
	// Variables for handling plugins
	var __DROMOS_PLUGIN__ = "dromos-module";
	var __DROMOS_INIT__ = "dromos-init";
	var __DROMOS_CONFIG__ = "dromos-config";

	// Make sure dromos exists, it should exist as normally this would be loaded by the bootstrap
	dromos = dromos || {};

	/**
	* Creates and returns a namespace from tcNamespace
	* if tcDelimiter is defined it will be used to separate the namespaces,
	* otherwise the default delimiter is '.'
	*/
	dromos.namespace = function(tcNamespace, tcDelimiter)
	{
		var laParts = tcNamespace.split(tcDelimiter || '.');
		dromos.base[laParts[0]] = dromos.base[laParts[0]] || {};
		var loCurrent = dromos.base[laParts[0]];
		for (var i=1, lnLength=laParts.length; i<lnLength; i++)
		{
			loCurrent = loCurrent[laParts[i]] || {};
		}
		return loCurrent;
	};

	dromos.addDOMNotifier = function(tnIndex, toElement)
	{
		var loFNBefore = toElement.insertBefore;
		toElement.insertBefore = function()
		{
			dromos.addDOMNotifier._notified = true;
			var loReturn = loFNBefore.apply(this, arguments);
			if (loReturn !== null && $jQ(loReturn).parents(document).length >0)
			{
				dromos.addDOMNotifier(0, loReturn);
				$jQ(this).trigger('domchildadded');
			}
			return loReturn;
		};

		var loFNAppend = toElement.appendChild;
		toElement.appendChild = function()
		{
			dromos.addDOMNotifier._notified = true;
			var loReturn = loFNAppend.apply(this, arguments);
			if (loReturn !== null && $jQ(loReturn).parents(document).length >0)
			{
				dromos.addDOMNotifier(0, loReturn);
				$jQ(this).trigger('domchildadded');
			}
			return loReturn;
		};
	};

	dromos.prepareModule = function(toSelector)
	{
		$jQ(toSelector).find("[" + __DROMOS_PLUGIN__ + "]").each(
			function(tnIndex, toElement)
			{
				var loElement = $jQ(toElement);
				var lcModule = loElement.attr(__DROMOS_PLUGIN__);
				if (lcModule)
				{
					var lcInit = loElement.attr(__DROMOS_INIT__);
					var lcConfig = loElement.attr(__DROMOS_CONFIG__);
					loElement.removeAttr(__DROMOS_PLUGIN__).removeAttr(__DROMOS_INIT__).removeAttr(__DROMOS_CONFIG__);
					require(lcModule, function(toModule)
						{
							dromos.utilities.initialiseModule(toModule, toElement, tnIndex, lcInit, dromos.base[lcConfig]);
						});
				}
			});
	};

	// Update all elements to notify of dom changes
	$jQ('*').each(dromos.addDOMNotifier);
	$jQ('*').on('domchildadded', function()
			{
				dromos.prepareModule(this);
			});
	$jQ(function(){dromos.prepareModule(document);});

	// return the dromos object for future require calls
	return dromos;
});
<commit_msg>Updates for Firefox<commit_after>/*=============================
dromos javascript library core.

This class is loaded by the dromos Bootstrap and should not
be included directly in a page

=============================*/
define(["jquery", "dromos.utilities"], function($jQ, utilities)
{
	// Variables for handling plugins
	var __DROMOS_PLUGIN__ = "dromos-module";
	var __DROMOS_INIT__ = "dromos-init";
	var __DROMOS_CONFIG__ = "dromos-config";

	// Make sure dromos exists, it should exist as normally this would be loaded by the bootstrap
	dromos = dromos || {};

	/**
	* Creates and returns a namespace from tcNamespace
	* if tcDelimiter is defined it will be used to separate the namespaces,
	* otherwise the default delimiter is '.'
	*/
	dromos.namespace = function(tcNamespace, tcDelimiter)
	{
		var laParts = tcNamespace.split(tcDelimiter || '.');
		dromos.base[laParts[0]] = dromos.base[laParts[0]] || {};
		var loCurrent = dromos.base[laParts[0]];
		for (var i=1, lnLength=laParts.length; i<lnLength; i++)
		{
			loCurrent = loCurrent[laParts[i]] || {};
		}
		return loCurrent;
	};

	dromos.addDOMNotifier = function(tnIndex, toElement)
	{
		var loFNBefore = toElement.insertBefore;
		toElement.insertBefore = function()
		{
			dromos.addDOMNotifier._notified = true;
			var loReturn = loFNBefore.apply(this, arguments);
			if (loReturn !== null && $jQ(loReturn).parents(document).length >0)
			{
				dromos.addDOMNotifier(0, loReturn);
				$jQ(this).trigger('domchildadded');
			}
			return loReturn;
		};

		var loFNAppend = toElement.appendChild;
		toElement.appendChild = function()
		{
			dromos.addDOMNotifier._notified = true;
			var loReturn = loFNAppend.apply(this, arguments);
			if (loReturn !== null && $jQ(loReturn).parents(document).length >0)
			{
				dromos.addDOMNotifier(0, loReturn);
				$jQ(this).trigger('domchildadded');
			}
			return loReturn;
		};
	};

	dromos.prepareModule = function(toSelector)
	{
		$jQ(toSelector).find("[" + __DROMOS_PLUGIN__ + "]").each(
			function(tnIndex, toElement)
			{
				var loElement = $jQ(toElement);
				var lcModule = loElement.attr(__DROMOS_PLUGIN__);
				if (lcModule)
				{
					var lcInit = loElement.attr(__DROMOS_INIT__);
					var lcConfig = loElement.attr(__DROMOS_CONFIG__);
					loElement.removeAttr(__DROMOS_PLUGIN__).removeAttr(__DROMOS_INIT__).removeAttr(__DROMOS_CONFIG__);
					require(lcModule, function(toModule)
						{
							dromos.utilities.initialiseModule(toModule, toElement, tnIndex, lcInit, dromos.base[lcConfig]);
						});
				}
			});
	};

	// Update all elements to notify of dom changes
	//
	// Firefox now has a problem with setting properties on
	try
	{
		$jQ('<div>').insertBefore = function(){};
		$jQ('*').each(dromos.addDOMNotifier);
	}
	catch (ex)
	{
		// DO NOTHING
	}
	$jQ('*').on('domchildadded', function()
	{
		dromos.prepareModule(this);
	});
	$jQ(function(){dromos.prepareModule(document);});

	// return the dromos object for future require calls
	return dromos;
});
<|endoftext|>


<commit_before>function CustomError(message, previousError) {
    this.previousError = previousError;
    this.type = this.constructor.name;
    this.message = message || "";
    this.stack = getRelevantStackTrace(new Error().stack, message, this.type);
}

function getRelevantStackTrace(fullStackTrace, message, type) {
    var matchConstructorStackFrame = new RegExp("new " + type + "[^\n]+\n", "g");
    var constructorStackFrameLine = matchConstructorStackFrame.exec(fullStackTrace);

    if(!constructorStackFrameLine) {
        return "";
    }

    var stackTraceHeader = type + ": " + message + "\n";
    var relevantStack = fullStackTrace.slice(matchConstructorStackFrame.lastIndex);
    return stackTraceHeader + relevantStack;
}

CustomError.prototype.is = function(errorType) {
    return this.constructor === errorType;
};

CustomError.prototype.toString = function() {
    return this.stack;
};

module.exports = CustomError;
<commit_msg>add fail-over to type-check<commit_after>function CustomError(message, previousError) {
    this.previousError = previousError;
    this.type = this.constructor.name;
    this.message = message || "";
    this.stack = getRelevantStackTrace(new Error().stack, message, this.type);
}

function getRelevantStackTrace(fullStackTrace, message, type) {
    var matchConstructorStackFrame = new RegExp("new " + type + "[^\n]+\n", "g");
    var constructorStackFrameLine = matchConstructorStackFrame.exec(fullStackTrace);

    if(!constructorStackFrameLine) {
        return "";
    }

    var stackTraceHeader = type + ": " + message + "\n";
    var relevantStack = fullStackTrace.slice(matchConstructorStackFrame.lastIndex);
    return stackTraceHeader + relevantStack;
}

CustomError.prototype.is = function(ErrorType) {
    var equal = true;

    if (this.constructor === ErrorType) {
        return equal;
    }

    var errorType = new ErrorType();
    if (this.type == errorType.type) {
        return equal;
    }

    return !equal;
};

CustomError.prototype.toString = function() {
    return this.stack;
};

module.exports = CustomError;
<|endoftext|>


<commit_before>var DomHandler = require("./DomHandler.js"),
	DomUtils = require("./DomUtils.js");

//TODO: make this a streamable handler
function FeedHandler(callback){
	this.init(callback, { ignoreWhitespace: true });
}

require("util").inherits(FeedHandler, DomHandler);

FeedHandler.prototype.init = DomHandler;

function getElements(what, where, one, recurse){
	if(one) return DomUtils.getElementsByTagName(what, where, recurse, 1)[0];
	else	return DomUtils.getElementsByTagName(what, where, recurse);
}
function fetch(what, where, recurse){
	var ret = getElements(what, where, true, recurse);
	if(ret && (ret = ret.children) && ret.length > 0) return ret[0].data;
	else return false;
}

var isValidFeed = function(value) {
	return value === "rss" || value === "feed" || value === "rdf:RDF";
}

FeedHandler.prototype.onend = function() {
	var feed = {};
	var feedRoot;
	var tmp, items, childs;

	feedRoot = getElements(isValidFeed, this.dom, true);
	if (feedRoot) {
		if(feedRoot.name === "feed"){
			childs = feedRoot.children;
			items = getElements("entry", childs);
		}
		else{
			items = getElements("item", feedRoot.children);
			childs = getElements("channel", feedRoot.children, true).children;
		}

		if (feedRoot.name === "feed"){
			feed.type = "atom";
			if(tmp = fetch("id", childs))
				feed.id = tmp;
			if(tmp = fetch("title", childs))
				feed.title = tmp;
			if((tmp = getElements("link", childs, true)) && (tmp = tmp.attribs) && (tmp = tmp.href))
				feed.link = tmp;
			if(tmp = fetch("subtitle", childs))
				feed.description = tmp;
			if(tmp = fetch("updated", childs))
				feed.updated = new Date(tmp);
			if(tmp = fetch("email", childs, true))
				feed.author = tmp;
			feed.items = Array(items.length);
			items.forEach(function(item, i){
				var entry = {}, tmp;
				if(tmp = fetch("id", item.children))
					entry.id = tmp;
				if(tmp = fetch("title", item.children))
					entry.title = tmp;
				if((tmp = getElements("link", item.children, true)) && (tmp = tmp.attribs) && (tmp = tmp.href))
					entry.link = tmp;
				if(tmp = fetch("summary", item.children))
					entry.description = tmp;
				if(tmp = fetch("updated", item.children))
					entry.pubDate = new Date(tmp);
				feed.items[i] = entry;
			});
		} else {
			feed.type = feedRoot.name;
			feed.id = "";
			if(tmp = fetch("title", childs))
				feed.title = tmp;
			if(tmp = fetch("link", childs))
				feed.link = tmp;
			if(tmp = fetch("description", childs))
				feed.description = tmp;
			if(tmp = fetch("lastBuildDate", childs))
				feed.updated = new Date(tmp);
			if(tmp = fetch("managingEditor", childs))
				feed.author = tmp;
			feed.items = Array(items.length);
			items.forEach(function(item, i){
				var entry = {}, tmp;
				if(tmp = fetch("guid", item.children))
					entry.id = tmp;
				if(tmp = fetch("title", item.children))
					entry.title = tmp;
				if(tmp = fetch("link", item.children))
					entry.link = tmp;
				if(tmp = fetch("description", item.children))
					entry.description = tmp;
				if(tmp = fetch("pubDate", item.children))
					entry.pubDate = new Date(tmp);
				feed.items[i] = entry;
			});
		}
	}
	this.dom = feed;
	DomHandler.prototype._handleCallback.call(this);
};

module.exports = FeedHandler;<commit_msg>Changed compare<commit_after>var DomHandler = require("./DomHandler.js"),
	DomUtils = require("./DomUtils.js");

//TODO: make this a streamable handler
function FeedHandler(callback){
	this.init(callback, { ignoreWhitespace: true });
}

require("util").inherits(FeedHandler, DomHandler);

FeedHandler.prototype.init = DomHandler;

function getElements(what, where, one, recurse){
	if(one) return DomUtils.getElementsByTagName(what, where, recurse, 1)[0];
	else	return DomUtils.getElementsByTagName(what, where, recurse);
}
function fetch(what, where, recurse){
	var ret = getElements(what, where, true, recurse);
	if(ret && (ret = ret.children) && ret.length !== 0) return ret[0].data;
	else return false;
}

var isValidFeed = function(value) {
	return value === "rss" || value === "feed" || value === "rdf:RDF";
}

FeedHandler.prototype.onend = function() {
	var feed = {};
	var feedRoot;
	var tmp, items, childs;

	feedRoot = getElements(isValidFeed, this.dom, true);
	if (feedRoot) {
		if(feedRoot.name === "feed"){
			childs = feedRoot.children;
			items = getElements("entry", childs);
		}
		else{
			items = getElements("item", feedRoot.children);
			childs = getElements("channel", feedRoot.children, true).children;
		}

		if (feedRoot.name === "feed"){
			feed.type = "atom";
			if(tmp = fetch("id", childs))
				feed.id = tmp;
			if(tmp = fetch("title", childs))
				feed.title = tmp;
			if((tmp = getElements("link", childs, true)) && (tmp = tmp.attribs) && (tmp = tmp.href))
				feed.link = tmp;
			if(tmp = fetch("subtitle", childs))
				feed.description = tmp;
			if(tmp = fetch("updated", childs))
				feed.updated = new Date(tmp);
			if(tmp = fetch("email", childs, true))
				feed.author = tmp;
			feed.items = Array(items.length);
			items.forEach(function(item, i){
				var entry = {}, tmp;
				if(tmp = fetch("id", item.children))
					entry.id = tmp;
				if(tmp = fetch("title", item.children))
					entry.title = tmp;
				if((tmp = getElements("link", item.children, true)) && (tmp = tmp.attribs) && (tmp = tmp.href))
					entry.link = tmp;
				if(tmp = fetch("summary", item.children))
					entry.description = tmp;
				if(tmp = fetch("updated", item.children))
					entry.pubDate = new Date(tmp);
				feed.items[i] = entry;
			});
		} else {
			feed.type = feedRoot.name;
			feed.id = "";
			if(tmp = fetch("title", childs))
				feed.title = tmp;
			if(tmp = fetch("link", childs))
				feed.link = tmp;
			if(tmp = fetch("description", childs))
				feed.description = tmp;
			if(tmp = fetch("lastBuildDate", childs))
				feed.updated = new Date(tmp);
			if(tmp = fetch("managingEditor", childs))
				feed.author = tmp;
			feed.items = Array(items.length);
			items.forEach(function(item, i){
				var entry = {}, tmp;
				if(tmp = fetch("guid", item.children))
					entry.id = tmp;
				if(tmp = fetch("title", item.children))
					entry.title = tmp;
				if(tmp = fetch("link", item.children))
					entry.link = tmp;
				if(tmp = fetch("description", item.children))
					entry.description = tmp;
				if(tmp = fetch("pubDate", item.children))
					entry.pubDate = new Date(tmp);
				feed.items[i] = entry;
			});
		}
	}
	this.dom = feed;
	DomHandler.prototype._handleCallback.call(this);
};

module.exports = FeedHandler;<|endoftext|>


<commit_before>'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports['default'] = undefined;

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _dec, _class, _class2, _temp2;

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _formsyReact = require('formsy-react');

var _semanticUiReact = require('semantic-ui-react');

var _lodash = require('lodash.debounce');

var _lodash2 = _interopRequireDefault(_lodash);

var _utils = require('./utils');

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

var FormsyInput = (_dec = (0, _formsyReact.Decorator)(), _dec(_class = (_temp2 = _class2 = function (_Component) {
  _inherits(FormsyInput, _Component);

  function FormsyInput() {
    var _ref;

    var _temp, _this, _ret;

    _classCallCheck(this, FormsyInput);

    for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }

    return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = FormsyInput.__proto__ || Object.getPrototypeOf(FormsyInput)).call.apply(_ref, [this].concat(args))), _this), _this.state = { allowError: false, value: null }, _this.setInputValue = (0, _lodash2['default'])(function (value) {
      return _this.props.setValue(value);
    }, 100), _this.handleChange = function (e, _ref2) {
      var value = _ref2.value;

      _this.setState({ value: value });
      _this.setInputValue(value);
      if (_this.props.instantValidation) _this.showError();
    }, _this.handleBlur = function () {
      _this.showError();
      if (_this.props.onBlur) _this.props.onBlur();
    }, _this.showError = function () {
      return _this.setState({ allowError: true });
    }, _temp), _possibleConstructorReturn(_this, _ret);
  }

  _createClass(FormsyInput, [{
    key: 'componentDidMount',
    value: function () {
      function componentDidMount() {
        var _props = this.props,
            defaultValue = _props.defaultValue,
            setValue = _props.setValue;

        if (defaultValue) setValue(defaultValue);
      }

      return componentDidMount;
    }()
  }, {
    key: 'componentWillReceiveProps',
    value: function () {
      function componentWillReceiveProps(nextProps) {
        if (nextProps.isFormSubmitted()) this.showError();
        this.setState({ value: this.props.getValue() });
      }

      return componentWillReceiveProps;
    }()
  }, {
    key: 'render',
    value: function () {
      function render() {
        var _props2 = this.props,
            as = _props2.as,
            defaultValue = _props2.defaultValue,
            isValid = _props2.isValid,
            isPristine = _props2.isPristine,
            getErrorMessage = _props2.getErrorMessage,
            errorLabel = _props2.errorLabel;
        var _state = this.state,
            allowError = _state.allowError,
            value = _state.value;

        var error = !isPristine() && !isValid() && allowError;

        var inputProps = _extends({
          value: value || isPristine() && defaultValue || '',
          onChange: this.handleChange,
          onBlur: this.handleBlur,
          error: error
        }, (0, _utils.filterSuirElementProps)(this.props));

        return _react2['default'].createElement(
          _semanticUiReact.Form.Field,
          null,
          (0, _react.createElement)(as, _extends({}, inputProps)),
          error && errorLabel && (0, _react.cloneElement)(errorLabel, {}, getErrorMessage())
        );
      }

      return render;
    }()
  }]);

  return FormsyInput;
}(_react.Component), _class2.propTypes = {
  name: _react.PropTypes.string.isRequired,
  as: _react.PropTypes.oneOf([_semanticUiReact.Input, _semanticUiReact.TextArea, _semanticUiReact.Form.Input, _semanticUiReact.Form.TextArea]),
  errorLabel: _react.PropTypes.element,
  instantValidation: _react.PropTypes.bool,
  defaultValue: _react.PropTypes.string,
  onBlur: _react.PropTypes.func,
  isValid: _react.PropTypes.func.isRequired,
  setValue: _react.PropTypes.func.isRequired,
  getValue: _react.PropTypes.func.isRequired,
  isPristine: _react.PropTypes.func.isRequired,
  getErrorMessage: _react.PropTypes.func.isRequired,
  validationError: _react.PropTypes.string,
  validationErrors: _react.PropTypes.object,
  validations: _react.PropTypes.oneOfType([_react.PropTypes.string, _react.PropTypes.object])
}, _class2.defaultProps = { as: _semanticUiReact.Input }, _temp2)) || _class);
exports['default'] = FormsyInput;<commit_msg>Use prop-types package instead of React.PropTypes<commit_after>'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports['default'] = undefined;

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _dec, _class, _class2, _temp2;

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _formsyReact = require('formsy-react');

var _semanticUiReact = require('semantic-ui-react');

var _lodash = require('lodash.debounce');

var _lodash2 = _interopRequireDefault(_lodash);

var _utils = require('./utils');

var _propTypes = require('prop-types');

var _propTypes2 = _interopRequireDefault(_propTypes);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

var FormsyInput = (_dec = (0, _formsyReact.Decorator)(), _dec(_class = (_temp2 = _class2 = function (_Component) {
  _inherits(FormsyInput, _Component);

  function FormsyInput() {
    var _ref;

    var _temp, _this, _ret;

    _classCallCheck(this, FormsyInput);

    for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }

    return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = FormsyInput.__proto__ || Object.getPrototypeOf(FormsyInput)).call.apply(_ref, [this].concat(args))), _this), _this.state = { allowError: false, value: null }, _this.setInputValue = (0, _lodash2['default'])(function (value) {
      return _this.props.setValue(value);
    }, 100), _this.handleChange = function (e, _ref2) {
      var value = _ref2.value;

      _this.setState({ value: value });
      _this.setInputValue(value);
      if (_this.props.instantValidation) _this.showError();
    }, _this.handleBlur = function () {
      _this.showError();
      if (_this.props.onBlur) _this.props.onBlur();
    }, _this.showError = function () {
      return _this.setState({ allowError: true });
    }, _temp), _possibleConstructorReturn(_this, _ret);
  }

  _createClass(FormsyInput, [{
    key: 'componentDidMount',
    value: function () {
      function componentDidMount() {
        var _props = this.props,
            defaultValue = _props.defaultValue,
            setValue = _props.setValue;

        if (defaultValue) setValue(defaultValue);
      }

      return componentDidMount;
    }()
  }, {
    key: 'componentWillReceiveProps',
    value: function () {
      function componentWillReceiveProps(nextProps) {
        if (nextProps.isFormSubmitted()) this.showError();
        this.setState({ value: this.props.getValue() });
      }

      return componentWillReceiveProps;
    }()
  }, {
    key: 'render',
    value: function () {
      function render() {
        var _props2 = this.props,
            as = _props2.as,
            defaultValue = _props2.defaultValue,
            isValid = _props2.isValid,
            isPristine = _props2.isPristine,
            getErrorMessage = _props2.getErrorMessage,
            errorLabel = _props2.errorLabel;
        var _state = this.state,
            allowError = _state.allowError,
            value = _state.value;

        var error = !isPristine() && !isValid() && allowError;

        var inputProps = _extends({
          value: value || isPristine() && defaultValue || '',
          onChange: this.handleChange,
          onBlur: this.handleBlur,
          error: error
        }, (0, _utils.filterSuirElementProps)(this.props));

        return _react2['default'].createElement(
          _semanticUiReact.Form.Field,
          null,
          (0, _react.createElement)(as, _extends({}, inputProps)),
          error && errorLabel && (0, _react.cloneElement)(errorLabel, {}, getErrorMessage())
        );
      }

      return render;
    }()
  }]);

  return FormsyInput;
}(_react.Component), _class2.propTypes = {
  name: _propTypes2['default'].string.isRequired,
  as: _propTypes2['default'].oneOf([_semanticUiReact.Input, _semanticUiReact.TextArea, _semanticUiReact.Form.Input, _semanticUiReact.Form.TextArea]),
  errorLabel: _propTypes2['default'].element,
  instantValidation: _propTypes2['default'].bool,
  defaultValue: _propTypes2['default'].string,
  onBlur: _propTypes2['default'].func,
  isValid: _propTypes2['default'].func.isRequired,
  setValue: _propTypes2['default'].func.isRequired,
  getValue: _propTypes2['default'].func.isRequired,
  isPristine: _propTypes2['default'].func.isRequired,
  getErrorMessage: _propTypes2['default'].func.isRequired,
  validationError: _propTypes2['default'].string,
  validationErrors: _propTypes2['default'].object,
  validations: _propTypes2['default'].oneOfType([_propTypes2['default'].string, _propTypes2['default'].object])
}, _class2.defaultProps = { as: _semanticUiReact.Input }, _temp2)) || _class);
exports['default'] = FormsyInput;<|endoftext|>


<commit_before>var Vectors = require('./Vectors');
var StringUtils = require('./StringUtils');
var rgbToHex = require('./rgbToHex');
var Layer = require('./Layer');
var Tagger = require('./Tagger');

var LayerParser = (function (Vectors, StringUtils, rgbToHex, Layer, Tagger, Math) {
    "use strict";

    function LayerParser(psDocument, animations) {
        this.doc = psDocument;
        this.animations = animations;
    }

    function setOpacity(layer, drawable) {
        if (Layer.isOpacitySet(layer)) {
            drawable.alpha = Layer.getOpacity(layer);
        }
    }

    function setSmartObjectRotation(layer, drawable, topLeft_x, topLeft_y, topRight_x, topRight_y, optionalVector) {
        var isRotation = Layer.isRotated(layer, topLeft_x, topLeft_y);

        if (isRotation) {
            drawable.rotation = Layer.getRotation(optionalVector || topLeft_x, topLeft_y, topRight_x, topRight_y);
        } else {
            drawable.rotation = 0;
        }

        return isRotation;
    }

    function setMatrixRotation(drawable, transformMatrix) {
        if (Layer.isMatrixRotation(transformMatrix)) {
            drawable.rotation = Layer.getMatrixRotation(transformMatrix);
        } else {
            drawable.rotation = 0;
        }
    }

    function setMatrixScale(drawable, transformMatrix) {
        if (transformMatrix)
            drawable.scale = Layer.getMatrixScale(transformMatrix);
    }

    LayerParser.prototype.parseBasicImage = function (layer) {
        var width = layer.bounds.right - layer.bounds.left;
        var height = layer.bounds.bottom - layer.bounds.top;

        var nameParts = layer.name.split(' ');

        var tags = Tagger.parseTags(nameParts);

        var drawable = {
            type: 'image',
            x: Math.floor(layer.bounds.left + width / 2 - this.doc.getArtboard().left),
            y: Math.floor(layer.bounds.top + height / 2 - this.doc.getArtboard().top),
            width: width,
            height: height,
            referenceId: layer.id
        };

        setOpacity(layer, drawable);

        if (StringUtils.includes(nameParts[0], '.png')) {
            drawable.filename = nameParts[0];
        }
        if (tags.length > 0) {
            drawable.tags = tags;
            Tagger.setTaggedId(layer, drawable);
            Tagger.setTaggedZIndex(layer, drawable);
            if (this.animations) {
                Tagger.referenceAnimationTagging(drawable, tags, this.animations, this.doc.getArtboard());
            }
        }

        if (layer.type != 'shapeLayer' && layer.path && layer.path.pathComponents[0].origin.type == 'rect') {
            var mask = {};

            var a = layer.path.pathComponents[0].subpathListKey[0].points[0].anchor;
            var b = layer.path.pathComponents[0].subpathListKey[0].points[1].anchor;
            var c = layer.path.pathComponents[0].subpathListKey[0].points[2].anchor;

            var a_b = Vectors.get(a.x, a.y, b.x, b.y);
            mask.width = Math.floor(Vectors.magnitude(a_b.x, a_b.y));

            var b_c = Vectors.get(b.x, b.y, c.x, c.y);
            mask.height = Math.floor(Vectors.magnitude(b_c.x, b_c.y));

            mask.rotation = Layer.getRotation(a_b);

            var bounds = layer.path.pathComponents[0].origin.bounds;
            var boundsWidth = bounds.right - bounds.left;
            var boundsHeight = bounds.bottom - bounds.top;

            mask.x = Math.floor(bounds.left + boundsWidth / 2 - this.doc.getArtboard().left);
            mask.y = Math.floor(bounds.top + boundsHeight / 2 - this.doc.getArtboard().top);

            drawable.mask = mask;
        }

        return drawable;
    };

    LayerParser.prototype.parseShape = function (layer) {
        var type;
        if (layer.path.pathComponents[0].origin.type == 'line') {
            type = 'line';
        } else if (layer.path.pathComponents[0].origin.type == 'rect') {
            type = 'rectangle';
        } else if (layer.path.pathComponents[0].origin.type == 'ellipse') {
            type = 'circle';
        }

        var drawable = this.parseBasicImage(layer);
        drawable.type = type;

        if (type == 'line') {
            var a = layer.path.pathComponents[0].subpathListKey[0].points[1].anchor;
            var b = layer.path.pathComponents[0].subpathListKey[0].points[2].anchor;
            var v = Vectors.get(a.x, a.y, b.x, b.y);
            drawable.length = Math.floor(Vectors.magnitude(v.x, v.y));
            drawable.rotation = Layer.getRotation(v);
        } else if (type == 'circle') {
            drawable.radius = Math.floor(drawable.width / 2);
        }

        drawable.filled = layer.fill !== undefined;
        if (layer.strokeStyle) {
            drawable.filled = layer.strokeStyle.fillEnabled;
            if (layer.strokeStyle.strokeEnabled)
                drawable.lineWidth = layer.strokeStyle.strokeStyleLineWidth;
        }
        var color = drawable.filled ? layer.fill.color : layer.strokeStyle.strokeStyleContent.color;
        drawable.color = rgbToHex(color.red, color.green, color.blue);

        return drawable;
    };

    LayerParser.prototype.parseText = function (layer) {
        var drawable = this.parseBasicImage(layer);
        drawable.type = 'text';
        drawable.msg = layer.text.textKey;

        setMatrixRotation(drawable, layer.text.transform);
        setMatrixScale(drawable, layer.text.transform);

        var style = layer.text.textStyleRange[0].textStyle; // apply only style of 1st style range
        drawable.font = style.fontName;
        drawable.fontStyle = style.fontStyleName;
        drawable.size = style.size.value; // maybe px vs pt issues
        drawable.color = style.color ? rgbToHex(style.color.red, style.color.green, style.color.blue) : '#000000';

        return drawable;
    };

    LayerParser.prototype.parseSmartObject = function (layer) {
        var drawable = this.parseBasicImage(layer);

        var topLeft_x = Math.floor(layer.smartObject.transform[0]);
        var topLeft_y = Math.floor(layer.smartObject.transform[1]);
        var topRight_x = Math.floor(layer.smartObject.transform[2]);
        var topRight_y = Math.floor(layer.smartObject.transform[3]);
        var vector = Vectors.get(topLeft_x, topLeft_y, topRight_x, topRight_y);

        var isRotation = setSmartObjectRotation(layer, drawable, topLeft_x, topLeft_y, topRight_x, topRight_y, vector);

        var original = this.doc.getPlaced(layer.smartObject);
        // var originalHeight = original.bounds.bottom - original.bounds.top; // not used, atm scale affects w&h equally
        var originalWidth = original.bounds.right - original.bounds.left;

        if (isRotation) {
            drawable.scale = Vectors.magnitude(vector.x, vector.y) / originalWidth;
        } else {
            drawable.scale = drawable.width / originalWidth;
        }
        drawable.scale = Math.round(drawable.scale * 100) / 100;

        return drawable;
    };

    LayerParser.prototype.parseLayer = function (layer) {
        if (layer.smartObject) {
            return this.parseSmartObject(layer);

        } else if (StringUtils.includes(layer.name, '.png')) {
            return this.parseBasicImage(layer);

        } else if (layer.type == 'textLayer') {
            return this.parseText(layer);

        } else if (layer.type == 'shapeLayer') {
            return this.parseShape(layer);
        }
    };

    LayerParser.prototype.parseButton = function (layerGroup) {
        var button = {};
        button.type = 'button';
        Tagger.setTaggedId(layerGroup, button);
        Tagger.setTaggedZIndex(layerGroup, button);

        layerGroup.layers.forEach(function (layer) {
            if (layer.type == 'textLayer') {
                button.text = this.parseText(layer);
            } else if (StringUtils.includes(layer.name, '#input')) {
                button.input = this.parseShape(layer);
            } else if (StringUtils.includes(layer.name, '#background')) {
                button.background = this.parseLayer(layer);
            }
        }, this);

        return button;
    };

    LayerParser.prototype.parseTaggedLayerGroup = function (layerGroup) {
        var drawables = [];
        if (StringUtils.includes(layerGroup.name, '#ignore')) {
            return drawables;

        } else if (StringUtils.includes(layerGroup.name, '#button')) {
            drawables.push(this.parseButton(layerGroup));

        } else if (StringUtils.includes(layerGroup.name, '#')) {
            var tags = Tagger.parseTags(layerGroup.name.split(' '));
            this.parseLayerGroup(layerGroup).forEach(function (elem) {
                if (elem.tags) {
                    elem.tags.push.apply(elem.tags, tags);
                } else {
                    elem.tags = tags.slice();
                }
                if (this.animations) {
                    Tagger.referenceAnimationTagging(elem, tags, this.animations, this.doc.getArtboard());
                }
                Tagger.setGroupTaggedZIndex(elem, tags);

                drawables.push(elem);
            }, this);
        }

        return drawables;
    };

    LayerParser.prototype.parseLayerGroup = function (layerGroup) {
        var drawables = [];
        layerGroup.layers.forEach(function (layer) {
            if (layer.type == 'layer' || layer.type == 'textLayer' || layer.type == 'shapeLayer') {
                var drawable = this.parseLayer(layer);
                if (drawable)
                    drawables.push(drawable);

            } else if (layer.type == 'layerSection') {
                drawables.push.apply(drawables, this.parseGroup(layer));
            }
        }, this);

        return drawables;
    };

    LayerParser.prototype.parseGroup = function (layerGroup) {
        if (StringUtils.includes(layerGroup.name, '.png')) {
            return [this.parseBasicImage(layerGroup)];

        } else if (StringUtils.includes(layerGroup.name, '#')) {
            return this.parseTaggedLayerGroup(layerGroup);

        } else {
            return this.parseLayerGroup(layerGroup);
        }
    };

    return LayerParser;
})(Vectors, StringUtils, rgbToHex, Layer, Tagger, Math);

module.exports = LayerParser;<commit_msg>parse info if line has stroke type dash ->stroke style line dash set<commit_after>var Vectors = require('./Vectors');
var StringUtils = require('./StringUtils');
var rgbToHex = require('./rgbToHex');
var Layer = require('./Layer');
var Tagger = require('./Tagger');

var LayerParser = (function (Vectors, StringUtils, rgbToHex, Layer, Tagger, Math) {
    "use strict";

    function LayerParser(psDocument, animations) {
        this.doc = psDocument;
        this.animations = animations;
    }

    function setOpacity(layer, drawable) {
        if (Layer.isOpacitySet(layer)) {
            drawable.alpha = Layer.getOpacity(layer);
        }
    }

    function setSmartObjectRotation(layer, drawable, topLeft_x, topLeft_y, topRight_x, topRight_y, optionalVector) {
        var isRotation = Layer.isRotated(layer, topLeft_x, topLeft_y);

        if (isRotation) {
            drawable.rotation = Layer.getRotation(optionalVector || topLeft_x, topLeft_y, topRight_x, topRight_y);
        } else {
            drawable.rotation = 0;
        }

        return isRotation;
    }

    function setMatrixRotation(drawable, transformMatrix) {
        if (Layer.isMatrixRotation(transformMatrix)) {
            drawable.rotation = Layer.getMatrixRotation(transformMatrix);
        } else {
            drawable.rotation = 0;
        }
    }

    function setMatrixScale(drawable, transformMatrix) {
        if (transformMatrix)
            drawable.scale = Layer.getMatrixScale(transformMatrix);
    }

    LayerParser.prototype.parseBasicImage = function (layer) {
        var width = layer.bounds.right - layer.bounds.left;
        var height = layer.bounds.bottom - layer.bounds.top;

        var nameParts = layer.name.split(' ');

        var tags = Tagger.parseTags(nameParts);

        var drawable = {
            type: 'image',
            x: Math.floor(layer.bounds.left + width / 2 - this.doc.getArtboard().left),
            y: Math.floor(layer.bounds.top + height / 2 - this.doc.getArtboard().top),
            width: width,
            height: height,
            referenceId: layer.id
        };

        setOpacity(layer, drawable);

        if (StringUtils.includes(nameParts[0], '.png')) {
            drawable.filename = nameParts[0];
        }
        if (tags.length > 0) {
            drawable.tags = tags;
            Tagger.setTaggedId(layer, drawable);
            Tagger.setTaggedZIndex(layer, drawable);
            if (this.animations) {
                Tagger.referenceAnimationTagging(drawable, tags, this.animations, this.doc.getArtboard());
            }
        }

        if (layer.type != 'shapeLayer' && layer.path && layer.path.pathComponents[0].origin.type == 'rect') {
            var mask = {};

            var a = layer.path.pathComponents[0].subpathListKey[0].points[0].anchor;
            var b = layer.path.pathComponents[0].subpathListKey[0].points[1].anchor;
            var c = layer.path.pathComponents[0].subpathListKey[0].points[2].anchor;

            var a_b = Vectors.get(a.x, a.y, b.x, b.y);
            mask.width = Math.floor(Vectors.magnitude(a_b.x, a_b.y));

            var b_c = Vectors.get(b.x, b.y, c.x, c.y);
            mask.height = Math.floor(Vectors.magnitude(b_c.x, b_c.y));

            mask.rotation = Layer.getRotation(a_b);

            var bounds = layer.path.pathComponents[0].origin.bounds;
            var boundsWidth = bounds.right - bounds.left;
            var boundsHeight = bounds.bottom - bounds.top;

            mask.x = Math.floor(bounds.left + boundsWidth / 2 - this.doc.getArtboard().left);
            mask.y = Math.floor(bounds.top + boundsHeight / 2 - this.doc.getArtboard().top);

            drawable.mask = mask;
        }

        return drawable;
    };

    LayerParser.prototype.parseShape = function (layer) {
        var type;
        if (layer.path.pathComponents[0].origin.type == 'line') {
            type = 'line';
        } else if (layer.path.pathComponents[0].origin.type == 'rect') {
            type = 'rectangle';
        } else if (layer.path.pathComponents[0].origin.type == 'ellipse') {
            type = 'circle';
        }

        var drawable = this.parseBasicImage(layer);
        drawable.type = type;

        if (type == 'line') {
            var a = layer.path.pathComponents[0].subpathListKey[0].points[1].anchor;
            var b = layer.path.pathComponents[0].subpathListKey[0].points[2].anchor;
            var v = Vectors.get(a.x, a.y, b.x, b.y);
            drawable.length = Math.floor(Vectors.magnitude(v.x, v.y));
            drawable.rotation = Layer.getRotation(v);
        } else if (type == 'circle') {
            drawable.radius = Math.floor(drawable.width / 2);
        }

        drawable.filled = layer.fill !== undefined;
        if (layer.strokeStyle) {
            drawable.filled = layer.strokeStyle.fillEnabled;
            if (layer.strokeStyle.strokeEnabled)
                drawable.lineWidth = layer.strokeStyle.strokeStyleLineWidth;
            if (layer.strokeStyle.strokeStyleLineDashSet)
                drawable.lineDash = layer.strokeStyle.strokeStyleLineDashSet;
        }
        var color = drawable.filled ? layer.fill.color : layer.strokeStyle.strokeStyleContent.color;
        drawable.color = rgbToHex(color.red, color.green, color.blue);

        return drawable;
    };

    LayerParser.prototype.parseText = function (layer) {
        var drawable = this.parseBasicImage(layer);
        drawable.type = 'text';
        drawable.msg = layer.text.textKey;

        setMatrixRotation(drawable, layer.text.transform);
        setMatrixScale(drawable, layer.text.transform);

        var style = layer.text.textStyleRange[0].textStyle; // apply only style of 1st style range
        drawable.font = style.fontName;
        drawable.fontStyle = style.fontStyleName;
        drawable.size = style.size.value; // maybe px vs pt issues
        drawable.color = style.color ? rgbToHex(style.color.red, style.color.green, style.color.blue) : '#000000';

        return drawable;
    };

    LayerParser.prototype.parseSmartObject = function (layer) {
        var drawable = this.parseBasicImage(layer);

        var topLeft_x = Math.floor(layer.smartObject.transform[0]);
        var topLeft_y = Math.floor(layer.smartObject.transform[1]);
        var topRight_x = Math.floor(layer.smartObject.transform[2]);
        var topRight_y = Math.floor(layer.smartObject.transform[3]);
        var vector = Vectors.get(topLeft_x, topLeft_y, topRight_x, topRight_y);

        var isRotation = setSmartObjectRotation(layer, drawable, topLeft_x, topLeft_y, topRight_x, topRight_y, vector);

        var original = this.doc.getPlaced(layer.smartObject);
        // var originalHeight = original.bounds.bottom - original.bounds.top; // not used, atm scale affects w&h equally
        var originalWidth = original.bounds.right - original.bounds.left;

        if (isRotation) {
            drawable.scale = Vectors.magnitude(vector.x, vector.y) / originalWidth;
        } else {
            drawable.scale = drawable.width / originalWidth;
        }
        drawable.scale = Math.round(drawable.scale * 100) / 100;

        return drawable;
    };

    LayerParser.prototype.parseLayer = function (layer) {
        if (layer.smartObject) {
            return this.parseSmartObject(layer);

        } else if (StringUtils.includes(layer.name, '.png')) {
            return this.parseBasicImage(layer);

        } else if (layer.type == 'textLayer') {
            return this.parseText(layer);

        } else if (layer.type == 'shapeLayer') {
            return this.parseShape(layer);
        }
    };

    LayerParser.prototype.parseButton = function (layerGroup) {
        var button = {};
        button.type = 'button';
        Tagger.setTaggedId(layerGroup, button);
        Tagger.setTaggedZIndex(layerGroup, button);

        layerGroup.layers.forEach(function (layer) {
            if (layer.type == 'textLayer') {
                button.text = this.parseText(layer);
            } else if (StringUtils.includes(layer.name, '#input')) {
                button.input = this.parseShape(layer);
            } else if (StringUtils.includes(layer.name, '#background')) {
                button.background = this.parseLayer(layer);
            }
        }, this);

        return button;
    };

    LayerParser.prototype.parseTaggedLayerGroup = function (layerGroup) {
        var drawables = [];
        if (StringUtils.includes(layerGroup.name, '#ignore')) {
            return drawables;

        } else if (StringUtils.includes(layerGroup.name, '#button')) {
            drawables.push(this.parseButton(layerGroup));

        } else if (StringUtils.includes(layerGroup.name, '#')) {
            var tags = Tagger.parseTags(layerGroup.name.split(' '));
            this.parseLayerGroup(layerGroup).forEach(function (elem) {
                if (elem.tags) {
                    elem.tags.push.apply(elem.tags, tags);
                } else {
                    elem.tags = tags.slice();
                }
                if (this.animations) {
                    Tagger.referenceAnimationTagging(elem, tags, this.animations, this.doc.getArtboard());
                }
                Tagger.setGroupTaggedZIndex(elem, tags);

                drawables.push(elem);
            }, this);
        }

        return drawables;
    };

    LayerParser.prototype.parseLayerGroup = function (layerGroup) {
        var drawables = [];
        layerGroup.layers.forEach(function (layer) {
            if (layer.type == 'layer' || layer.type == 'textLayer' || layer.type == 'shapeLayer') {
                var drawable = this.parseLayer(layer);
                if (drawable)
                    drawables.push(drawable);

            } else if (layer.type == 'layerSection') {
                drawables.push.apply(drawables, this.parseGroup(layer));
            }
        }, this);

        return drawables;
    };

    LayerParser.prototype.parseGroup = function (layerGroup) {
        if (StringUtils.includes(layerGroup.name, '.png')) {
            return [this.parseBasicImage(layerGroup)];

        } else if (StringUtils.includes(layerGroup.name, '#')) {
            return this.parseTaggedLayerGroup(layerGroup);

        } else {
            return this.parseLayerGroup(layerGroup);
        }
    };

    return LayerParser;
})(Vectors, StringUtils, rgbToHex, Layer, Tagger, Math);

module.exports = LayerParser;<|endoftext|>


<commit_before>/** LIC stuff.

 Ordnance Survey Open Space - Crown Copyright and database right 2012. All rights reserved. http://openspace.ordnancesurvey.co.uk/openspace/developeragreement.html#enduserlicense

--

OSOpenspace.js project

Copyright (C) 2012 Rob Murray

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

**/

/**
 * @requires <OpenLayers.Layer.WMS> +?
 */

/**
 * Class: OpenLayers.Layer.OSOpenSpace
 * A implementation of an Openlayers WMS layer to access Ordnance Survey's
 * map tile service of Great Britain.
 * This service requires an APIKEY
 *
 * Inherits from:
 *  - <OpenLayers.Layer.WMS>
 */
OpenLayers.Layer.OSOpenSpace = OpenLayers.Class(OpenLayers.Layer.WMS, {

    FOO: "foo",

    /**
     * Property: serverResolutions. NOTE: this needs to change if OS make more layers available
     * {Array} the resolutions provided by the Openspace Free service.
     */
    serverResolutions: [2500, 1000, 500, 200, 100, 50, 25, 10, 5, 4, 2.5, 2, 1],

    /**
     * Property: bounds
     * {OpenLayers.Bounds} the limits of GB.
     */
    bounds: null,

    /**
     * Property: osUrl
     * {String} The URL of the Ordnance Survey map tile server
     */
    osUrl: "http://openspace.ordnancesurvey.co.uk/osmapapi/ts",

    /**
     * APIProperty: format
     * {String} The image MIME type.  Default is "image/png".
     */
    format: "image/png",

    /**
     * APIProperty: projection
     * {OpenLayers.Projection} The Openlayers.Projection EPSG code - OSGB36 datum code is EPSG:27700 .
     */
    projection: null,

    /**
     * Property: osTileSize
     * {Integer} A value to hold the size of tile currently displayed
     */
    osTileSize: null,

    /**
     * Constructor: OpenLayers.Layer.OSOpenSpace
     * Create a new Ordnance Survey Openspace layer object
     *
     * Parameters:
     * name - {String} A name for the layer
     * apiKey - {String} Your domain name linked API Key for OS service,
     *          get a key at http://www.ordnancesurvey.co.uk/oswebsite/web-services/os-openspace/
     * params - {Object} An object with key/value pairs representing the
     *          GetMap query string parameters and parameter values.
     * options - {Object} Hashtable of extra options to tag onto the layer
     */
    initialize: function(name, apiKey, params, options) {

        var attributionStr, newOptions, authParams, newArguments;

        if(!apiKey) {
            throw new Error("This OSOpenspace layer requires API credentials");
        }

        this.projection = new OpenLayers.Projection("EPSG:27700");
        this.bounds = new OpenLayers.Bounds(0, 0, 700000, 1300000);

        //TODO this appears 50px or so up from the bottom, should be level with bottom border -> fix with css
        attributionStr = "© Crown Copyright and database right 2012. All rights reserved. End User License Agreement";

        //Extend the options passed from user with Openspace related params
        newOptions = OpenLayers.Util.extend({
            maxExtent: this.bounds,
            projection: this.projection,
            resolutions: this.serverResolutions,
            units: "m",
            attribution: attributionStr
        }, options);

        //Create obj for the API key and URL
        authParams = {
            "KEY": apiKey,
            "URL": "file:///"
        };

        //Apply new arguments to WMS implementation
        newArguments = [name, this.osUrl, authParams, newOptions];
        OpenLayers.Layer.WMS.prototype.initialize.apply(this, newArguments);

    },

    /**
    * APIMethod: getFullRequestString
    * Override the method that appends the WMS url with new params to add the &LAYERS=
    *
    * Parameters:
    * newParams - {Object}
    * altUrl - {String} Use this as the url instead of the layer's url
    *
    * Returns:
    * {String}
    */
    getFullRequestString: function(newParams, altUrl) {
        this.params.LAYERS = this.resolutions[this.map.getZoom()];

        return OpenLayers.Layer.WMS.prototype.getFullRequestString.apply(this, arguments);
    },

    /**
    * Method: initGriddedTiles
    * Add functionality to this callback from http request to adjust the tile size. OS
    *   have different tile sizes for certain layers (in fact all are non standard sizes)
    *   so we have to update and refresh grid if a change
    *
    * Parameters:
    * newParams - {Object}
    * altUrl - {String} Use this as the url instead of the layer's url
    *
    * Returns:
    * {String}
    */
    initGriddedTiles: function(bounds) {

        var osTileSize, res;

        osTileSize = new OpenLayers.Size(200,200);
        res = this.map.getResolution();

        //The OS tilesizes are awkward: http://www.ordnancesurvey.co.uk/oswebsite/support/web-services/configuring-os-ondemand-wmts.html
        // All resolutions less than 5 but not 2.5 are 250
        if(res < 5 && res !== 2.5) {
            osTileSize = new OpenLayers.Size(250,250);
        }

        //If the size is different then clear the grid
        if(!osTileSize.equals(this.tileSize)) {
            this.tileSize = osTileSize;
            this.clearGrid();
        }

        //Call original method
        OpenLayers.Layer.WMS.prototype.initGriddedTiles.apply(this, [bounds]);
    },

    CLASS_NAME: "OpenLayers.Layer.OSOpenSpace"
});

<commit_msg>Remove a random FOO!<commit_after>/** LIC stuff.

 Ordnance Survey Open Space - Crown Copyright and database right 2012. All rights reserved. http://openspace.ordnancesurvey.co.uk/openspace/developeragreement.html#enduserlicense

--

OSOpenspace.js project

Copyright (C) 2012 Rob Murray

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

**/

/**
 * @requires <OpenLayers.Layer.WMS> +?
 */

/**
 * Class: OpenLayers.Layer.OSOpenSpace
 * A implementation of an Openlayers WMS layer to access Ordnance Survey's
 * map tile service of Great Britain.
 * This service requires an APIKEY
 *
 * Inherits from:
 *  - <OpenLayers.Layer.WMS>
 */
OpenLayers.Layer.OSOpenSpace = OpenLayers.Class(OpenLayers.Layer.WMS, {

    /**
     * Property: serverResolutions. NOTE: this needs to change if OS make more layers available
     * {Array} the resolutions provided by the Openspace Free service.
     */
    serverResolutions: [2500, 1000, 500, 200, 100, 50, 25, 10, 5, 4, 2.5, 2, 1],

    /**
     * Property: bounds
     * {OpenLayers.Bounds} the limits of GB.
     */
    bounds: null,

    /**
     * Property: osUrl
     * {String} The URL of the Ordnance Survey map tile server
     */
    osUrl: "http://openspace.ordnancesurvey.co.uk/osmapapi/ts",

    /**
     * APIProperty: format
     * {String} The image MIME type.  Default is "image/png".
     */
    format: "image/png",

    /**
     * APIProperty: projection
     * {OpenLayers.Projection} The Openlayers.Projection EPSG code - OSGB36 datum code is EPSG:27700 .
     */
    projection: null,

    /**
     * Property: osTileSize
     * {Integer} A value to hold the size of tile currently displayed
     */
    osTileSize: null,

    /**
     * Constructor: OpenLayers.Layer.OSOpenSpace
     * Create a new Ordnance Survey Openspace layer object
     *
     * Parameters:
     * name - {String} A name for the layer
     * apiKey - {String} Your domain name linked API Key for OS service,
     *          get a key at http://www.ordnancesurvey.co.uk/oswebsite/web-services/os-openspace/
     * params - {Object} An object with key/value pairs representing the
     *          GetMap query string parameters and parameter values.
     * options - {Object} Hashtable of extra options to tag onto the layer
     */
    initialize: function(name, apiKey, params, options) {

        var attributionStr, newOptions, authParams, newArguments;

        if(!apiKey) {
            throw new Error("This OSOpenspace layer requires API credentials");
        }

        this.projection = new OpenLayers.Projection("EPSG:27700");
        this.bounds = new OpenLayers.Bounds(0, 0, 700000, 1300000);

        //TODO this appears 50px or so up from the bottom, should be level with bottom border -> fix with css
        attributionStr = "© Crown Copyright and database right 2012. All rights reserved. End User License Agreement";

        //Extend the options passed from user with Openspace related params
        newOptions = OpenLayers.Util.extend({
            maxExtent: this.bounds,
            projection: this.projection,
            resolutions: this.serverResolutions,
            units: "m",
            attribution: attributionStr
        }, options);

        //Create obj for the API key and URL
        authParams = {
            "KEY": apiKey,
            "URL": "file:///"
        };

        //Apply new arguments to WMS implementation
        newArguments = [name, this.osUrl, authParams, newOptions];
        OpenLayers.Layer.WMS.prototype.initialize.apply(this, newArguments);

    },

    /**
    * APIMethod: getFullRequestString
    * Override the method that appends the WMS url with new params to add the &LAYERS=
    *
    * Parameters:
    * newParams - {Object}
    * altUrl - {String} Use this as the url instead of the layer's url
    *
    * Returns:
    * {String}
    */
    getFullRequestString: function(newParams, altUrl) {
        this.params.LAYERS = this.resolutions[this.map.getZoom()];

        return OpenLayers.Layer.WMS.prototype.getFullRequestString.apply(this, arguments);
    },

    /**
    * Method: initGriddedTiles
    * Add functionality to this callback from http request to adjust the tile size. OS
    *   have different tile sizes for certain layers (in fact all are non standard sizes)
    *   so we have to update and refresh grid if a change
    *
    * Parameters:
    * newParams - {Object}
    * altUrl - {String} Use this as the url instead of the layer's url
    *
    * Returns:
    * {String}
    */
    initGriddedTiles: function(bounds) {

        var osTileSize, res;

        osTileSize = new OpenLayers.Size(200,200);
        res = this.map.getResolution();

        //The OS tilesizes are awkward: http://www.ordnancesurvey.co.uk/oswebsite/support/web-services/configuring-os-ondemand-wmts.html
        // All resolutions less than 5 but not 2.5 are 250
        if(res < 5 && res !== 2.5) {
            osTileSize = new OpenLayers.Size(250,250);
        }

        //If the size is different then clear the grid
        if(!osTileSize.equals(this.tileSize)) {
            this.tileSize = osTileSize;
            this.clearGrid();
        }

        //Call original method
        OpenLayers.Layer.WMS.prototype.initGriddedTiles.apply(this, [bounds]);
    },

    CLASS_NAME: "OpenLayers.Layer.OSOpenSpace"
});

<|endoftext|>