<commit_before>// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('controllers/controller') ;
require('mixins/array') ;
require('mixins/selection_support') ;
require('foundation/binding') ;

/** @class

An Array Controller provides a way to project the contents of an array
out to a view.  You can use this object any place you might use an
array.  Changes to the array will not propogate to the content array
until you call commitChanges().

@extends SC.Controller
@extends SC.Array
@extends SC.SelectionSupport
@since SproutCore 1.0

*/
SC.ArrayController = SC.Controller.extend(SC.Array, SC.SelectionSupport,
/** @scope SC.ArrayController.prototype */
{
  /**
    If YES the will return controllers for content objects.

    If you want to use an array controller to edit an array contents directly
    but you do not want to wrap the values of the array in controller objects
    then you should set this property to NO.

    @field
    @type {Boolean}
  */
  useControllersForContent: NO,

  /**
    Provides compatibility with CollectionControllers.
    @field
    @type {SC.ArrayController}
  */
  arrangedObjects: function() { return this; }.property('content'),

  /**
    The array content that is being managed by the controller.
    @field
    @type {Array}
  */
  content: null,
  contentBindingDefault: SC.Binding.Multiple,

  /**
    Set to true if you want objects removed from the array to also be
    deleted.  This is a convenient way to manage lists of items owned
    by a parent record object.

    @field
    @type {Boolean}
  */
  destroyOnRemoval: NO,

  /**
    Watches changes to the content property updates the contentClone.
    @private
    @observes content
  */
  _contentObserver: function() {
    var content = this.get('content') ;
    if (SC.isEqual(content, this._content)) return ; // nothing to do

    if (!this._boundContentPropertyObserver) {
      this._boundContentPropertyObserver = this._contentPropertyObserver.bind(this) ;
    }
    var func = this._boundContentPropertyObserver ;

    // remove old observer, add new observer, and trigger content property change
    if (this._content && this._content.removeObserver) this._content.removeObserver('[]', func) ;
    if (content && content.addObserver) content.addObserver('[]', func) ;
    this._content = content; //cache
    this._contentPropertyRevision = null ;

    var rev = (content) ? content.propertyRevision : -1 ;
    this._contentPropertyObserver(this, '[]', content, rev) ;
  }.observes('content'),

  _contentPropertyObserver: function(target, key, value, rev) {

    if (!this._updatingContent && (!rev || (rev != this._contentPropertyRevision))) {
      this._contentPropertyRevision = rev ;

      this._updatingContent = true ;

      this.beginPropertyChanges();
      this.contentCloneReset();
      this.arrayContentDidChange() ;
      this.notifyPropertyChange('length') ;
      this.updateSelectionAfterContentChange();
      this.endPropertyChanges() ;

      this._updatingContent = false ;

    }
  },

  /**
    The array content that (when committed) will be merged back into the
    content property. All array methods will take place on this object.

    @field
    @type {SC.Array}
  */
  contentClone: null,

  /**
    Clones the content property into the contentClone property.
    @private
  */
  contentCloneReset: function() {
    this._changelog = [];
    this.set('contentClone', null);
  },

  /**
   SC.Array interface implimentation.

   @param idx {Number} Starting index in the array to replace.  If idx >=
     length, then append to the end of the array.

   @param amt {Number} Number of elements that should be removed from the
     array, starting at *idx*.

   @param objects {Array} An array of zero or more objects that should be
     inserted into the array at *idx*
  */
  replace: function(idx, amt, objects) {

    var content = this.get('content') ;

    // in case the passed objects are controllers, convert to source objects.
    var copyIdx = objects.length ;
    var sourceObjects = [] ;
    while(--copyIdx >= 0) sourceObjects[copyIdx] = this._sourceObjectFor(objects[copyIdx]) ;

    // create clone of content array if needed
    var contentClone = this.get('contentClone') ;
    if (!contentClone) {
      this.set('contentClone', contentClone = content.clone()) ;
    }

    // now, record the removed objects.  This may be used later.
    if (this.get('destroyOnRemoval')) {
      if (!this._deletions) this._deletions = [] ;
      for (var i=0; i < amt; i++) {
        this._deletions.push(content.objectAt(idx + i));
      }
    }

    // and record additions
    if (!this._changelog) this._changelog = [];
    this._changelog.push({ idx: idx, amt: amt, objects: sourceObjects });

    // then actually perform the edit on the contentClone
    contentClone.replace(idx, amt, sourceObjects);

    this.editorDidChange() ;
    this.arrayContentDidChange();
    this.updateSelectionAfterContentChange();

    return this;
  },
  /**
  * SC.Array interface implimentation.
  *
  * @param {Number} idx
  *   The index of the item to return.  If idx exceeds the current length,
  *   return null.
  */
  objectAt: function(idx) {
    var obj = this._getSourceContent() ;
    obj = (obj && obj.objectAt) ? obj.objectAt(idx) : null;
    return this._objectControllerFor(obj) ;
  },
  /**
  * SC.Array interface implimentation.
  * @field
  * @type {integer}
  */
  length: function( key, value ) {
    var ret = this._getSourceContent() ;
    return (ret && ret.get) ? (ret.get('length') || 0) : 0 ;
  }.property(),

  /**
    Returns the index in the array of the specified object.

    This can handle both controller wrapper objects and source content objects.
  */
  indexOf: function( obj ) {
    return this._getSourceContent().indexOf(this._sourceObjectFor(obj)) ;
  },

  _getSourceContent: function() {
    return this.get('contentClone') || this.get('content') || [];
  },

  /**
  * @private
  */
  performCommitChanges: function()
  {
    var content = this.get('content');
    var ret     = true;

    // cannot commit changes to null content.  Return an error.
    if (!content) {
      return $error("No Content");
    }

    if (content.beginPropertyChanges) content.beginPropertyChanges();

    // apply all the changes made to the clone
    var changelog = this._changelog || [] ;
    for(var idx=0;idx<changelog.length;idx++) {
      var change = changelog[idx];
      content.replace(change.idx, change.amt, change.objects) ;
    }

    // done, flush the changelog
    this._changelog = [] ;

    // finally, destroy any removed objects if necessary.  Make
    // sure the objects have not been re-added before doing this.
    if (this.get('destroyOnRemoval') && this._deletions && this._deletions.length>0) {
      var idx = this._deletions.length;
      while(--idx >= 0) {
        var obj = this._deletions[idx] ;
        if (obj && obj.destroy && (content.indexOf(obj) < 0)) {
          obj.destroy() ;
        }
      }
      this._deletions = [] ; // clear array
    }

     // finish commiting changes.
    if (content.endPropertyChanges) content.endPropertyChanges();
    if (content.commitChanges) ret = content.commitChanges();

    if ($ok(ret)) {
      this.contentCloneReset();
      this.editorDidClearChanges();
    }

    return ret;
  },
  /**
  * @private
  */
  performDiscardChanges: function()
  {
    this.contentCloneReset();
    this.editorDidClearChanges();
    return true;
  },

  /** @private
    Returns the object controller for a source value.
  */
  _objectControllerFor: function(obj) {
    if (!this.useControllersForContent) return obj;

    var controllers = this._objControllers = this._objControllers || {} ;
    var guid = SC.getGUID(obj) ;
    var ret = controllers[guid] ;
    if (!ret) {
      ret = controllers[guid] = this.controllerForValue(obj) ;
      if (ret) ret.__isArrayController = true ;
    }
    return ret ;
  },

  /** @private
    Returns the source object for the passed value.  If the passed value is a
    controller, this will map back to the sourceo object.  Otherwise the object itself
    will be returned.
  */
  _sourceObjectFor: function(obj) {
    return (obj && obj.kindOf && obj.kindOf(SC.Controller)) ? obj.get('content') : obj ;
  },

  init: function() {
    sc_super() ;
    if (this.get('content')) this._contentObserver() ;
  }

});
<commit_msg>Cleaned up some documentation<commit_after>// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('controllers/controller') ;
require('mixins/array') ;
require('mixins/selection_support') ;
require('foundation/binding') ;

/** @class

An Array Controller provides a way to project the contents of an array
out to a view.  You can use this object any place you might use an
array.  Changes to the array will not propogate to the content array
until you call commitChanges().

@extends SC.Controller
@extends SC.Array
@extends SC.SelectionSupport
@since SproutCore 1.0

*/
SC.ArrayController = SC.Controller.extend(SC.Array, SC.SelectionSupport,
/** @scope SC.ArrayController.prototype */
{
  /**
    If YES the will return controllers for content objects.

    If you want to use an array controller to edit an array contents directly
    but you do not want to wrap the values of the array in controller objects
    then you should set this property to NO.

    @field
    @type {Boolean}
  */
  useControllersForContent: NO,

  /**
    Provides compatibility with CollectionControllers.
    @field
    @type {SC.ArrayController}
  */
  arrangedObjects: function() { return this; }.property('content'),

  /**
    The array content that is being managed by the controller.
    @field
    @type {Array}
  */
  content: null,
  contentBindingDefault: SC.Binding.Multiple,

  /**
    Set to true if you want objects removed from the array to also be
    deleted.  This is a convenient way to manage lists of items owned
    by a parent record object.

    Note that even if this is set to NO, calling destroyObject() instead of
    removeObject() will still destroy the object in question as well as
    removing it from the parent array.

    @field
    @type {Boolean}
  */
  destroyOnRemoval: NO,

  /**
    Watches changes to the content property updates the contentClone.
    @private
    @observes content
  */
  _contentObserver: function() {
    var content = this.get('content') ;
    if (SC.isEqual(content, this._content)) return ; // nothing to do

    if (!this._boundContentPropertyObserver) {
      this._boundContentPropertyObserver = this._contentPropertyObserver.bind(this) ;
    }
    var func = this._boundContentPropertyObserver ;

    // remove old observer, add new observer, and trigger content property change
    if (this._content && this._content.removeObserver) this._content.removeObserver('[]', func) ;
    if (content && content.addObserver) content.addObserver('[]', func) ;
    this._content = content; //cache
    this._contentPropertyRevision = null ;

    var rev = (content) ? content.propertyRevision : -1 ;
    this._contentPropertyObserver(this, '[]', content, rev) ;
  }.observes('content'),

  _contentPropertyObserver: function(target, key, value, rev) {

    if (!this._updatingContent && (!rev || (rev != this._contentPropertyRevision))) {
      this._contentPropertyRevision = rev ;

      this._updatingContent = true ;

      this.beginPropertyChanges();
      this.contentCloneReset();
      this.arrayContentDidChange() ;
      this.notifyPropertyChange('length') ;
      this.updateSelectionAfterContentChange();
      this.endPropertyChanges() ;

      this._updatingContent = false ;

    }
  },

  /**
    The array content that (when committed) will be merged back into the
    content property. All array methods will take place on this object.

    @field
    @type {SC.Array}
  */
  contentClone: null,

  /**
    Clones the content property into the contentClone property.
    @private
  */
  contentCloneReset: function() {
    this._changelog = [];
    this.set('contentClone', null);
  },

  /**
   SC.Array interface implimentation.

   @param idx {Number} Starting index in the array to replace.  If idx >=
     length, then append to the end of the array.

   @param amt {Number} Number of elements that should be removed from the
     array, starting at *idx*.

   @param objects {Array} An array of zero or more objects that should be
     inserted into the array at *idx*
  */
  replace: function(idx, amt, objects) {

    var content = this.get('content') ;

    // in case the passed objects are controllers, convert to source objects.
    var copyIdx = objects.length ;
    var sourceObjects = [] ;
    while(--copyIdx >= 0) sourceObjects[copyIdx] = this._sourceObjectFor(objects[copyIdx]) ;

    // create clone of content array if needed
    var contentClone = this.get('contentClone') ;
    if (!contentClone) {
      this.set('contentClone', contentClone = content.clone()) ;
    }

    // now, record the removed objects.  This may be used later.
    if (this.get('destroyOnRemoval')) {
      if (!this._deletions) this._deletions = [] ;
      for (var i=0; i < amt; i++) {
        this._deletions.push(content.objectAt(idx + i));
      }
    }

    // and record additions
    if (!this._changelog) this._changelog = [];
    this._changelog.push({ idx: idx, amt: amt, objects: sourceObjects });

    // then actually perform the edit on the contentClone
    contentClone.replace(idx, amt, sourceObjects);

    this.editorDidChange() ;
    this.arrayContentDidChange();
    this.updateSelectionAfterContentChange();

    return this;
  },

  /**
    SC.Array interface implimentation.
    @param idx {Number} The index of the item to return.  If idx exceeds the
      current length, return null.
  */
  objectAt: function(idx) {
    var obj = this._getSourceContent() ;
    obj = (obj && obj.objectAt) ? obj.objectAt(idx) : null;
    return this._objectControllerFor(obj) ;
  },
  /**
    SC.Array interface implimentation.
    @property
    @type {integer}
  */
  length: function( key, value ) {
    var ret = this._getSourceContent() ;
    return (ret && ret.get) ? (ret.get('length') || 0) : 0 ;
  }.property(),

  /**
    Returns the index in the array of the specified object.

    This can handle both controller wrapper objects and source content objects.
  */
  indexOf: function( obj ) {
    return this._getSourceContent().indexOf(this._sourceObjectFor(obj)) ;
  },

  _getSourceContent: function() {
    return this.get('contentClone') || this.get('content') || [];
  },

  /**
  * @private
  */
  performCommitChanges: function()
  {
    var content = this.get('content');
    var ret     = true;

    // cannot commit changes to null content.  Return an error.
    if (!content) {
      return $error("No Content");
    }

    if (content.beginPropertyChanges) content.beginPropertyChanges();

    // apply all the changes made to the clone
    var changelog = this._changelog || [] ;
    for(var idx=0;idx<changelog.length;idx++) {
      var change = changelog[idx];
      content.replace(change.idx, change.amt, change.objects) ;
    }

    // done, flush the changelog
    this._changelog = [] ;

    // finally, destroy any removed objects if necessary.  Make
    // sure the objects have not been re-added before doing this.
    if (this.get('destroyOnRemoval') && this._deletions && this._deletions.length>0) {
      var idx = this._deletions.length;
      while(--idx >= 0) {
        var obj = this._deletions[idx] ;
        if (obj && obj.destroy && (content.indexOf(obj) < 0)) {
          obj.destroy() ;
        }
      }
      this._deletions = [] ; // clear array
    }

     // finish commiting changes.
    if (content.endPropertyChanges) content.endPropertyChanges();
    if (content.commitChanges) ret = content.commitChanges();

    if ($ok(ret)) {
      this.contentCloneReset();
      this.editorDidClearChanges();
    }

    return ret;
  },
  /**
  * @private
  */
  performDiscardChanges: function()
  {
    this.contentCloneReset();
    this.editorDidClearChanges();
    return true;
  },

  /** @private
    Returns the object controller for a source value.
  */
  _objectControllerFor: function(obj) {
    if (!this.useControllersForContent) return obj;

    var controllers = this._objControllers = this._objControllers || {} ;
    var guid = SC.getGUID(obj) ;
    var ret = controllers[guid] ;
    if (!ret) {
      ret = controllers[guid] = this.controllerForValue(obj) ;
      if (ret) ret.__isArrayController = true ;
    }
    return ret ;
  },

  /** @private
    Returns the source object for the passed value.  If the passed value is a
    controller, this will map back to the sourceo object.  Otherwise the object itself
    will be returned.
  */
  _sourceObjectFor: function(obj) {
    return (obj && obj.kindOf && obj.kindOf(SC.Controller)) ? obj.get('content') : obj ;
  },

  init: function() {
    sc_super() ;
    if (this.get('content')) this._contentObserver() ;
  }

});
<|endoftext|>


<commit_before>define(function(require) {
    var Networks = require('models/networks');
    var Network = require('models/network');
    var NetworkCreateView = require('views/networks-create');

    var NetworksTemplate = require('text!tpl/networks.html');
    var NetworksListItemTemplate = require('text!tpl/networks-list-item.html');

    var adminui = require('adminui');

    var NetworksListItem = Backbone.Marionette.ItemView.extend({
        template: NetworksListItemTemplate,
        tagName: 'li',
        events: {
            'click': 'select'
        },

        highlightIfThis: function(model) {
            if (model == this.model) {
                this.highlight();
            }
        },

        unhighlight: function() {
            this.$el.siblings().removeClass('active');
        },

        highlight: function() {
            this.$el.siblings().removeClass('active');
            this.$el.addClass('active');
        },

        select: function() {
            this.highlight();
            this.trigger('select', this.model);
        }
    });

    var NetworksDetailView = require('views/networks-detail');

    var NetworksView = Backbone.Marionette.Layout.extend({
        template: NetworksTemplate,
        name: "networks",
        url: 'networks',
        events: {
            'click button[name=create]': 'showCreateNetworkForm'
        },
        attributes: {
            "id":"page-networks"
        },
        regions: {
            "list": ".list",
            "details": ".details"
        },

        initialize: function(options) {
            this.listView = new NetworksListView();
            options = options || {};
            if (options.uuid) {
                this.network = new Network({uuid: options.uuid});
                this.network.fetch();
            }
        },

        showCreateNetworkForm: function() {
            var view = new NetworkCreateView();
            this.bindTo(view, 'saved', function(n) {
                console.log(n);
                this.listView.collection.add(n);
                this.showNetwork(n);
            }, this);
            this.details.show(view);
        },

        showNetwork: function(network) {
            var view = new NetworksDetailView({model: network});
            this.details.show(view);
        },

        onRender: function() {
            this.bindTo(this.listView, 'select', this.showNetwork, this);
            this.bindTo(this.details, 'show', function(view) {
                adminui.router.applyUrl(view);
            });

            this.list.show(this.listView);

            if (this.network) {
                this.showNetwork(this.network);
            }
        }
    });

    var NetworksListTemplate = require('text!tpl/networks-list.html');
    var NetworksListView = Backbone.Marionette.CompositeView.extend({
        tagName: 'ul',
        attributes: { 'class': 'nav' },
        itemViewContainer: 'ul.items',
        template: NetworksListTemplate,
        itemView: NetworksListItem,

        initialize: function(options) {
            this.collection = new Networks();
            this.collection.fetch();
        },

        onItemAdded: function(itemView) {
            this.bindTo(itemView, 'select', this.onSelect, this);
        },

        onSelect: function(model)  {
            this.trigger('select', model);
        }
    });

    return NetworksView;
});<commit_msg>ADMINUI-1022 emit error events from napi<commit_after>define(function(require) {
    var Networks = require('models/networks');
    var Network = require('models/network');
    var NetworkCreateView = require('views/networks-create');

    var NetworksTemplate = require('text!tpl/networks.html');
    var NetworksListItemTemplate = require('text!tpl/networks-list-item.html');

    var adminui = require('adminui');

    var NetworksListItem = Backbone.Marionette.ItemView.extend({
        template: NetworksListItemTemplate,
        tagName: 'li',
        events: {
            'click': 'select'
        },

        highlightIfThis: function(model) {
            if (model == this.model) {
                this.highlight();
            }
        },

        unhighlight: function() {
            this.$el.siblings().removeClass('active');
        },

        highlight: function() {
            this.$el.siblings().removeClass('active');
            this.$el.addClass('active');
        },

        select: function() {
            this.highlight();
            this.trigger('select', this.model);
        }
    });

    var NetworksDetailView = require('views/networks-detail');

    var NetworksView = Backbone.Marionette.Layout.extend({
        template: NetworksTemplate,
        name: "networks",
        url: 'networks',
        events: {
            'click button[name=create]': 'showCreateNetworkForm'
        },
        attributes: {
            "id":"page-networks"
        },
        regions: {
            "list": ".list",
            "details": ".details"
        },

        initialize: function(options) {
            this.listView = new NetworksListView();
            options = options || {};
            if (options.uuid) {
                this.network = new Network({uuid: options.uuid});
                this.network.fetch();
            }
        },

        showCreateNetworkForm: function() {
            var view = new NetworkCreateView();
            this.bindTo(view, 'saved', function(n) {
                console.log(n);
                this.listView.collection.add(n);
                this.showNetwork(n);
            }, this);
            this.details.show(view);
        },

        showNetwork: function(network) {
            var view = new NetworksDetailView({model: network});
            this.details.show(view);
        },

        onRender: function() {
            this.bindTo(this.listView, 'select', this.showNetwork, this);
            this.bindTo(this.details, 'show', function(view) {
                adminui.router.applyUrl(view);
            });

            this.list.show(this.listView);

            if (this.network) {
                this.showNetwork(this.network);
            }
        }
    });

    var NetworksListTemplate = require('text!tpl/networks-list.html');
    var NetworksListView = Backbone.Marionette.CompositeView.extend({
        tagName: 'ul',
        attributes: { 'class': 'nav' },
        itemViewContainer: 'ul.items',
        template: NetworksListTemplate,
        itemView: NetworksListItem,

        initialize: function(options) {
            this.collection = new Networks();
            this.bindTo(this.collection, 'error', this.onError, this);
        },

        onError: function(model, res) {
            adminui.vent.trigger('error', {
                xhr: res,
                context: 'napi / networks'
            });
        },

        onItemAdded: function(itemView) {
            this.bindTo(itemView, 'select', this.onSelect, this);
        },

        onSelect: function(model)  {
            this.trigger('select', model);
        },

        onRender: function() {
            this.collection.fetch();
        }
    });

    return NetworksView;
});<|endoftext|>


<commit_before>window.OccurrenceView = Backbone.View.extend({
    events: {
      "click .fc-event-closebutton": "deleteOcurrence"
    },
    initialize: function() {
      this.calendar = this.options.calendar;
    },
    deleteOcurrence: function(sth) {
      this.$el.fadeOut(400, this.model.destroy.bind(this.model));
      console.log("Deleting ocurrence", this.model);
    }
});

window.ScheduleView = Backbone.View.extend({
    el: $("#content"),
    get_templateHTML: function () {
        return template.schedule();
    },
    get_collection: function () {
        return this.collection;
    },
    make_event: function(occurrence) {
        // make a Media.Occurrence into a fullCalendar event
        var attribs = _(occurrence.attributes).clone();
        attribs.model = occurrence;
        return attribs;
    },
    all_events: function() {
        return this.get_collection().map(this.make_event);
    },
    historical_events: [
        {
            title: 'WALLKINTUN SALE AL AIRE !',
            start: new Date(2012, 11, 7, 22, 0),
            end:   new Date(2012, 11, 8, 6, 0),
            allDay: false,
        },
    ],
    reload: function() {
        this.calendar.fullCalendar('refetchEvents');
    },
    checkOverlap: function(event) {
        // This checks if an event has any overlapping events on the calendar. It expects the event
        // to have a 'start' and an 'end' properties

        var start = moment(event.start);
        var end = moment(event.end);
        var overlap = this.calendar.fullCalendar('clientEvents', function(ev) {
            if( ev == event)
                return false;
            var estart = moment(ev.start);
            var eend = moment(ev.end);
            return estart.unix() < end.unix() && eend.unix() > start.unix();
        });
        if( overlap.length ) {
            overlap = overlap[0];
            var estart = moment(overlap.start);
            var eend = moment(overlap.end);
            var duration = end - start;
            if( estart > start ) {
                // clamp to beginning
                end = estart.clone();
                start = end.clone();
                start.subtract(duration);
            } else {
                // clamp to end
                start = eend.clone();
                end = start.clone();
                end.add(duration);
            }
        }
        return {start: start, end: end};
    },
    initialize: function () {
        var self = this;
        self.collection = this.get_collection();

        $(this.el).html(this.get_templateHTML());

        new UniverseListView({
            collection: Universe,
            el: $('#universe', this.el),
            draggable: true,
        });

        this.render();
    },
/*    saveEvent: function (event) {
        // we filter out the event to only store what we really need

        console.log ('saving event:', event);
        var nattr = {
            title:  event.get('title'),
            start:  moment(event.get('start')).unix(),
            end:    moment(event.get('end')).unix(),
            allDay: event.get('allDay'),
            list:   event.get('list'),
        };

        event.clear({silent:true});
        event.save(nattr);

    },*/
    render: function () {
        console.log ('render calendar', $(this.el));
        var self = this;
        this.collection = this.get_collection();
        this.opts = {"timestamp":1361784505,
                     "timezoneOffset":"0",
                     "timeScale":"agendaWeek",
                     "timeInterval":"30",
                     "weekStartDay":"0"};
        this.calendar = $('#schedule_calendar', this.$el);

        var mainHeight = document.documentElement.clientHeight - 200 - 50;

        var calendarEventSources = [
            function(start, end, callback) {
                console.log(start, end);
                var unix_start = moment(start).unix();
                var unix_end = moment(end).unix();
                var events = self.get_collection().filter(function(el){
                    return unix_start <= el.get('end') && unix_end >= el.get('start');
                }).map(self.make_event);
                console.log('Returning events #', events.length);
                callback(events);
            }
        ].concat(self.historical_events);

        this.calendar.fullCalendar({
            header: {
                left: 'prev, next, today',
                center: 'title',
                right: 'agendaDay, agendaWeek, month'
            },
            defaultView: this.opts.timeScale,
            slotMinutes: parseInt(this.opts.timeInterval),
            firstDay: this.opts.weekStartDay,
            editable: true,
            droppable: true,
            allDaySlot: false,
            axisFormat: 'H:mm',
            timeFormat: {
                agenda: 'H:mm{ - H:mm}',
                month: 'H:mm{ - H:mm}'
            },
            contentHeight: mainHeight,
            theme: true,
            lazyFetching: false,
            serverTimestamp: parseInt(this.opts.timestamp, 10),
            serverTimezoneOffset: parseInt(this.opts.timezoneOffset, 10),

            eventSources: calendarEventSources,

            //callbacks (in full-calendar-functions.js)
            viewDisplay: function ( view ) {
                view_name = view.name;

                if(view.name === 'agendaDay' || view.name === 'agendaWeek') {

                    var calendarEl = this;

                    var select = $('<select class="schedule_change_slots input_select"/>')
                        .append('<option value="1">1m</option>')
                        .append('<option value="5">5m</option>')
                        .append('<option value="10">10m</option>')
                        .append('<option value="15">15m</option>')
                        .append('<option value="30">30m</option>')
                        .append('<option value="60">60m</option>')
                        .change(function(){
                            var slotMin = $(this).val();
                            var opt = view.calendar.options;
                            var date = $(calendarEl).fullCalendar('getDate');

                            opt.slotMinutes = parseInt(slotMin);
                            opt.defaultView = view.name;
                            // event sources are not kept in the options, I don't know
                            //  if there's a way to retrieve them
                            opt.eventSources = calendarEventSources;

                            //re-initialize calendar with new slotmin options
                            $(calendarEl)
                                .fullCalendar('destroy')
                                .fullCalendar(opt)
                                .fullCalendar( 'gotoDate', date );

                            /** TODO: save view config
                             *
                            //save slotMin value to db
                            var url = '/Schedule/set-time-interval/format/json';
                            $.post(url, {timeInterval: slotMin});
                            */
                        });

                    var topLeft = $(view.element).find("table.fc-agenda-days > thead th:first");

                    select.width(topLeft.width())
                        .height(topLeft.height());

                    topLeft.empty()
                        .append(select);

                    var slotMin = view.calendar.options.slotMinutes;
                    $('.schedule_change_slots option[value="'+slotMin+'"]').attr('selected', 'selected');
                }

                if(($("#add-show-form").length == 1) && ($("#add-show-form").css('display')=='none') && ($('.fc-header-left > span').length == 5)) {

                    //userType is defined in bootstrap.php, and is derived from the currently logged in user.
                    if(userType == "A" || userType == "P"){
                        makeAddShowButton();
                    }
                }

                /* FIXME
                //save view name to db
                var url = '/Schedule/set-time-scale/format/json';
                $.post(url, {timeScale: view.name});
            */
            },
            dayClick: dayClick,
            eventRender: function (event, element, view) {
                var model = self.collection.where({list: event.list})[0];

                element.tooltip({
                    trigger: 'click',
                    placement: 'left',
                    title: function () {
                        console.log ("creating tooltip", model);
                        var target = document.createElement('div');
                        var view = new MediaListView({model: model,
                                                      noSearch:true,
                                                      el: target});
                        return view.render().el;
                    },
                });

                eventRender(event, element, view);
            },
            eventAfterRender: function (event, element, view) {
              var ocurrence = new OccurrenceView({
                el: element,
                id: event._id,
                model: event.model,
                collection: self.collection,
                calendar: self.calendar
              });
              eventAfterRender(event, element, view);
            },
            eventDrop: function(event, dayDelta, minuteDelta, allDay, revertFunc, jsEvent, ui, view) {
                var data = self.checkOverlap(event);
                event.start = data.start.toDate();
                event.end = data.end.toDate();
                self.calendar.fullCalendar('updateEvent', event);
                event.model.save({start: data.start.unix(), end: data.end.unix()});
            },
            eventResize: eventResize,
            drop: function(date, allDay) {
                var list  = Universe.get(this.id);
                console.log ('drop->', self.collection.pluck('end'));

                var start = moment(date);
                var end   = moment(start);

                end.add('ms', list.get('duration'));

                var times = self.checkOverlap({start: start, end: end});

                var event = {
                    title:  list.get('name'),
                    list:   list.get('_id'),
                    start:  times.start.unix(), end: times.end.unix(),
                    allDay: allDay,
                };

                console.log ('to save', event);
                var item = self.collection.create (event);
                console.log (this, list, event, item);
            }
        });

                                             /*
            eventRender: function(event, element) {
                var model = self.collection.where({list: event.list})[0];

                element.tooltip({
                    trigger: 'click',
                    placement: 'left',
                    title: function () {
                        var target = document.createElement('div');
                        var view = new MediaListView({model: model,
                                                      noSearch:true,
                                                      el: target});
                        return view.render().el;
                    },
                });
            }
            });
                                             */

        self.collection.bind('add',   this.reload, this);
        self.collection.bind('reset', this.reload, this);
        self.collection.bind('remove',this.reload, this);
        self.collection.bind('all',   function (e, a) {console.log('got: ' + e, a);}, this);
        self.collection.bind('update',this.reload, this);

        return this;
    },
});
<commit_msg>all this isn't needed<commit_after>window.OccurrenceView = Backbone.View.extend({
    events: {
      "click .fc-event-closebutton": "deleteOcurrence"
    },
    initialize: function() {
      this.calendar = this.options.calendar;
    },
    deleteOcurrence: function(sth) {
      this.$el.fadeOut(400, this.model.destroy.bind(this.model));
      console.log("Deleting ocurrence", this.model);
    }
});

window.ScheduleView = Backbone.View.extend({
    el: $("#content"),
    get_templateHTML: function () {
        return template.schedule();
    },
    get_collection: function () {
        return this.collection;
    },
    make_event: function(occurrence) {
        // make a Media.Occurrence into a fullCalendar event
        var attribs = _(occurrence.attributes).clone();
        attribs.model = occurrence;
        return attribs;
    },
    all_events: function() {
        return this.get_collection().map(this.make_event);
    },
    historical_events: [
        {
            title: 'WALLKINTUN SALE AL AIRE !',
            start: new Date(2012, 11, 7, 22, 0),
            end:   new Date(2012, 11, 8, 6, 0),
            allDay: false,
        },
    ],
    reload: function() {
        this.calendar.fullCalendar('refetchEvents');
    },
    checkOverlap: function(event) {
        // This checks if an event has any overlapping events on the calendar. It expects the event
        // to have a 'start' and an 'end' properties

        var start = moment(event.start);
        var end = moment(event.end);
        var overlap = this.calendar.fullCalendar('clientEvents', function(ev) {
            if( ev == event)
                return false;
            return ev.start < end && ev.end > start;
        });
        if( overlap.length ) {
            overlap = overlap[0];
            var estart = moment(overlap.start);
            var eend = moment(overlap.end);
            var duration = end - start;
            if( estart > start ) {
                // clamp to beginning
                end = estart.clone();
                start = end.clone();
                start.subtract(duration);
            } else {
                // clamp to end
                start = eend.clone();
                end = start.clone();
                end.add(duration);
            }
        }
        return {start: start, end: end};
    },
    initialize: function () {
        var self = this;
        self.collection = this.get_collection();

        $(this.el).html(this.get_templateHTML());

        new UniverseListView({
            collection: Universe,
            el: $('#universe', this.el),
            draggable: true,
        });

        this.render();
    },
/*    saveEvent: function (event) {
        // we filter out the event to only store what we really need

        console.log ('saving event:', event);
        var nattr = {
            title:  event.get('title'),
            start:  moment(event.get('start')).unix(),
            end:    moment(event.get('end')).unix(),
            allDay: event.get('allDay'),
            list:   event.get('list'),
        };

        event.clear({silent:true});
        event.save(nattr);

    },*/
    render: function () {
        console.log ('render calendar', $(this.el));
        var self = this;
        this.collection = this.get_collection();
        this.opts = {"timestamp":1361784505,
                     "timezoneOffset":"0",
                     "timeScale":"agendaWeek",
                     "timeInterval":"30",
                     "weekStartDay":"0"};
        this.calendar = $('#schedule_calendar', this.$el);

        var mainHeight = document.documentElement.clientHeight - 200 - 50;

        var calendarEventSources = [
            function(start, end, callback) {
                console.log(start, end);
                var unix_start = moment(start).unix();
                var unix_end = moment(end).unix();
                var events = self.get_collection().filter(function(el){
                    return unix_start <= el.get('end') && unix_end >= el.get('start');
                }).map(self.make_event);
                console.log('Returning events #', events.length);
                callback(events);
            }
        ].concat(self.historical_events);

        this.calendar.fullCalendar({
            header: {
                left: 'prev, next, today',
                center: 'title',
                right: 'agendaDay, agendaWeek, month'
            },
            defaultView: this.opts.timeScale,
            slotMinutes: parseInt(this.opts.timeInterval),
            firstDay: this.opts.weekStartDay,
            editable: true,
            droppable: true,
            allDaySlot: false,
            axisFormat: 'H:mm',
            timeFormat: {
                agenda: 'H:mm{ - H:mm}',
                month: 'H:mm{ - H:mm}'
            },
            contentHeight: mainHeight,
            theme: true,
            lazyFetching: false,
            serverTimestamp: parseInt(this.opts.timestamp, 10),
            serverTimezoneOffset: parseInt(this.opts.timezoneOffset, 10),

            eventSources: calendarEventSources,

            //callbacks (in full-calendar-functions.js)
            viewDisplay: function ( view ) {
                view_name = view.name;

                if(view.name === 'agendaDay' || view.name === 'agendaWeek') {

                    var calendarEl = this;

                    var select = $('<select class="schedule_change_slots input_select"/>')
                        .append('<option value="1">1m</option>')
                        .append('<option value="5">5m</option>')
                        .append('<option value="10">10m</option>')
                        .append('<option value="15">15m</option>')
                        .append('<option value="30">30m</option>')
                        .append('<option value="60">60m</option>')
                        .change(function(){
                            var slotMin = $(this).val();
                            var opt = view.calendar.options;
                            var date = $(calendarEl).fullCalendar('getDate');

                            opt.slotMinutes = parseInt(slotMin);
                            opt.defaultView = view.name;
                            // event sources are not kept in the options, I don't know
                            //  if there's a way to retrieve them
                            opt.eventSources = calendarEventSources;

                            //re-initialize calendar with new slotmin options
                            $(calendarEl)
                                .fullCalendar('destroy')
                                .fullCalendar(opt)
                                .fullCalendar( 'gotoDate', date );

                            /** TODO: save view config
                             *
                            //save slotMin value to db
                            var url = '/Schedule/set-time-interval/format/json';
                            $.post(url, {timeInterval: slotMin});
                            */
                        });

                    var topLeft = $(view.element).find("table.fc-agenda-days > thead th:first");

                    select.width(topLeft.width())
                        .height(topLeft.height());

                    topLeft.empty()
                        .append(select);

                    var slotMin = view.calendar.options.slotMinutes;
                    $('.schedule_change_slots option[value="'+slotMin+'"]').attr('selected', 'selected');
                }

                if(($("#add-show-form").length == 1) && ($("#add-show-form").css('display')=='none') && ($('.fc-header-left > span').length == 5)) {

                    //userType is defined in bootstrap.php, and is derived from the currently logged in user.
                    if(userType == "A" || userType == "P"){
                        makeAddShowButton();
                    }
                }

                /* FIXME
                //save view name to db
                var url = '/Schedule/set-time-scale/format/json';
                $.post(url, {timeScale: view.name});
            */
            },
            dayClick: dayClick,
            eventRender: function (event, element, view) {
                var model = self.collection.where({list: event.list})[0];

                element.tooltip({
                    trigger: 'click',
                    placement: 'left',
                    title: function () {
                        console.log ("creating tooltip", model);
                        var target = document.createElement('div');
                        var view = new MediaListView({model: model,
                                                      noSearch:true,
                                                      el: target});
                        return view.render().el;
                    },
                });

                eventRender(event, element, view);
            },
            eventAfterRender: function (event, element, view) {
              var ocurrence = new OccurrenceView({
                el: element,
                id: event._id,
                model: event.model,
                collection: self.collection,
                calendar: self.calendar
              });
              eventAfterRender(event, element, view);
            },
            eventDrop: function(event, dayDelta, minuteDelta, allDay, revertFunc, jsEvent, ui, view) {
                var data = self.checkOverlap(event);
                event.start = data.start.toDate();
                event.end = data.end.toDate();
                self.calendar.fullCalendar('updateEvent', event);
                event.model.save({start: data.start.unix(), end: data.end.unix()});
            },
            eventResize: eventResize,
            drop: function(date, allDay) {
                var list  = Universe.get(this.id);
                console.log ('drop->', self.collection.pluck('end'));

                var start = moment(date);
                var end   = moment(start);

                end.add('ms', list.get('duration'));

                var times = self.checkOverlap({start: start, end: end});

                var event = {
                    title:  list.get('name'),
                    list:   list.get('_id'),
                    start:  times.start.unix(), end: times.end.unix(),
                    allDay: allDay,
                };

                console.log ('to save', event);
                var item = self.collection.create (event);
                console.log (this, list, event, item);
            }
        });

                                             /*
            eventRender: function(event, element) {
                var model = self.collection.where({list: event.list})[0];

                element.tooltip({
                    trigger: 'click',
                    placement: 'left',
                    title: function () {
                        var target = document.createElement('div');
                        var view = new MediaListView({model: model,
                                                      noSearch:true,
                                                      el: target});
                        return view.render().el;
                    },
                });
            }
            });
                                             */

        self.collection.bind('add',   this.reload, this);
        self.collection.bind('reset', this.reload, this);
        self.collection.bind('remove',this.reload, this);
        self.collection.bind('all',   function (e, a) {console.log('got: ' + e, a);}, this);
        self.collection.bind('update',this.reload, this);

        return this;
    },
});
<|endoftext|>


<commit_before>//@flow
import React, { Component } from 'react';
import {
    View,
    Text,
    Animated,
    Easing,
    StyleSheet,
    ScrollView} from 'react-native';
import { SwipeRow } from 'react-native-swipe-list-view';

let styles = StyleSheet.create({
  rowItem: {
    padding:10,
    backgroundColor:'#ffffff'
  },
  rowTitle: {
    fontSize:24,
  },
  rowSubTitle:{
    color:'#666666'
  },
  swipeView:{
    alignItems: 'center',
    flex: 1,
    flexDirection: 'row',
    justifyContent: 'space-between',
    padding: 15,
    backgroundColor:'red',
  },
  deleteBtn:{
    color:"#ffffff",
    padding:10
  }
});

let styleSwipeRow = {
    borderBottomWidth: 1,
    borderColor:'#cccccc'
};

class Row extends Component {
    constructor(props) {
      super(props);

      this.state = {
        fadeInOpacity: new Animated.Value(1),
        hide:false
      };
    }

    shouldComponentUpdate(nextProps, nextState) {
      if (
        nextProps.novel.isValid()
        &&
        nextProps.novel.directoryUrl == this.props.novel.directoryUrl
        &&
        nextProps.novel.lastReadIndex == this.lastRenderParams.lastReadIndex
        &&
        nextProps.novel.lastArticleTitle == this.lastRenderParams.lastArticleTitle

        ) {
        return false;
      }

      return true;
    }
    props:{
        onDelete:func,
        onPress:func,
        novel:Novel
    };

  lastRenderParams = {};
  render() {
    this.lastRenderParams.lastReadIndex = this.props.novel.lastReadIndex
    this.lastRenderParams.lastArticleTitle = this.props.novel.lastArticleTitle

    let novel = this.props.novel;
    if (!novel.isValid()) {
        return <View/>;
    }
    let lastReadText , lastArticleText;
    if(novel.lastReadTitle){
    lastReadText = (<Text
        style={styles.rowSubTitle}
        numberOfLines={1}
        >已读 : {novel.lastReadTitle}
        </Text>
    );
    }
    if(novel.lastArticleTitle){
    lastArticleText = (<Text
        style={styles.rowSubTitle}
        numberOfLines={1}
        >最新 : {novel.lastArticleTitle}</Text>)
    }

    let style = {
        opacity:this.state.fadeInOpacity
    };
    if (this.state.hide) {
        style.height = 0;
    }
    return (
        <Animated.View style={style}>
      <SwipeRow
        style={styleSwipeRow}
        disableRightSwipe
        rightOpenValue={-65}
        onRowPress={this.props.onPress}
      >
        <View style={styles.swipeView}>
          <Text></Text>
          <Text onPress={e=>{
            Animated.timing(this.state.fadeInOpacity, {
                toValue: 0, // 目标值
                duration: 500, // 动画时间
                easing: Easing.linear // 缓动函数
            }).start(() => {
                this.setState({
                    hide:true
                });
                this.props.onDelete();
            });

        }} style={styles.deleteBtn}>删除</Text>
        </View>
        <View style={styles.rowItem}>
          <Text style={styles.rowTitle}>{novel.title}</Text>
          {lastArticleText}
          {lastReadText}
          <Text
          style={styles.rowSubTitle}
          numberOfLines={1}>来源 : {novel.directoryUrl}</Text>
        </View>
      </SwipeRow>
      </Animated.View>
    );
  }
}

export default Row;<commit_msg>no message<commit_after>//@flow
import React, { Component } from 'react';
import {
    View,
    Text,
    Animated,
    Easing,
    StyleSheet,
    ScrollView} from 'react-native';
import { SwipeRow } from 'react-native-swipe-list-view';

let styles = StyleSheet.create({
  rowItem: {
    padding:10,
    backgroundColor:'#ffffff'
  },
  rowTitle: {
    fontSize:24,
  },
  rowSubTitle:{
    color:'#666666'
  },
  swipeView:{
    alignItems: 'center',
    flex: 1,
    flexDirection: 'row',
    justifyContent: 'space-between',
    padding: 15,
    backgroundColor:'red',
  },
  deleteBtn:{
    color:"#ffffff",
    padding:10
  }
});

let styleSwipeRow = {
    borderBottomWidth: 1,
    borderColor:'#cccccc'
};

class Row extends Component {
    constructor(props) {
      super(props);

      this.state = {
        fadeInOpacity: new Animated.Value(1),
        hide:false
      };
    }

    shouldComponentUpdate(nextProps, nextState) {
      if (nextState.hide != this.state.hide) {
        return true;
      }
      if (
        nextProps.novel.isValid()
        &&
        nextProps.novel.directoryUrl == this.props.novel.directoryUrl
        &&
        nextProps.novel.lastReadIndex == this.lastRenderParams.lastReadIndex
        &&
        nextProps.novel.lastArticleTitle == this.lastRenderParams.lastArticleTitle

        ) {
        return false;

      }

      return true;
    }
    props:{
        onDelete:func,
        onPress:func,
        novel:Novel
    };

  lastRenderParams = {};
  render() {
    this.lastRenderParams.lastReadIndex = this.props.novel.lastReadIndex
    this.lastRenderParams.lastArticleTitle = this.props.novel.lastArticleTitle

    let novel = this.props.novel;
    if (!novel.isValid()) {
        return <View/>;
    }
    let lastReadText , lastArticleText;
    if(novel.lastReadTitle){
    lastReadText = (<Text
        style={styles.rowSubTitle}
        numberOfLines={1}
        >已读 : {novel.lastReadTitle}
        </Text>
    );
    }
    if(novel.lastArticleTitle){
    lastArticleText = (<Text
        style={styles.rowSubTitle}
        numberOfLines={1}
        >最新 : {novel.lastArticleTitle}</Text>)
    }

    let style = {
        opacity:this.state.fadeInOpacity
    };
    if (this.state.hide) {
        style.height = 0;
    }
    return (
        <Animated.View style={style}>
      <SwipeRow
        style={styleSwipeRow}
        disableRightSwipe
        rightOpenValue={-65}
        onRowPress={this.props.onPress}
      >
        <View style={styles.swipeView}>
          <Text></Text>
          <Text onPress={e=>{
            Animated.timing(this.state.fadeInOpacity, {
                toValue: 0, // 目标值
                duration: 500, // 动画时间
                easing: Easing.linear // 缓动函数
            }).start(() => {
                this.setState({
                    hide:true
                });
                this.props.onDelete();
            });

        }} style={styles.deleteBtn}>删除</Text>
        </View>
        <View style={styles.rowItem}>
          <Text style={styles.rowTitle}>{novel.title}</Text>
          {lastArticleText}
          {lastReadText}
          <Text
          style={styles.rowSubTitle}
          numberOfLines={1}>来源 : {novel.directoryUrl}</Text>
        </View>
      </SwipeRow>
      </Animated.View>
    );
  }
}

export default Row;<|endoftext|>


<commit_before>/*jslint node: true */
var async = require('async');
var fs = require('fs');
var path = require('path');
var url = require('url');

var _ = require('lodash');
var send = require('send');
var logger = require('loge');
var Router = require('regex-router');

var liwc = require('../liwc');
var db = require('../db');

var static_root = path.join(__dirname, '..', 'static');
var R = new Router(function(req, res) {
  send(req, 'layout.html').root(static_root).on('error', function(err) {
    res.status(err.status || 500).die('static error: ' + err.message);
  }).pipe(res);
});

// expose other /static files
R.get(/^\/static\/([^?]+)(\?|$)/, function(req, res, m) {
  send(req, m[1]).root(static_root).on('error', function(err) {
    res.status(err.status || 500).die('static error: ' + err.message);
  }).on('directory', function() {
    res.status(404).die('No resource at: ' + req.url);
  }).pipe(res);
});

/**
For example:

GET /repression.user.js?email=io@henrian.com&expires=2014-07-11T21:39:17.863Z
*/
// R.get(/^\/repression.user.js/, function(req, res, m) {
//   var download = function(user) {
//     // user has .email, .expires, and .repress properties
//     var userscript_path = path.join(__dirname, '..', 'repression.user.js');
//     fs.readFile(userscript_path, {encoding: 'utf8'}, function(err, userscript_template) {
//       if (err) return res.die(err);

//       var config_json = JSON.stringify(user);
//       var userscript = userscript_template.replace('CONFIG_JSON', config_json);

//       res.writeHead(200, {
//         'Content-Type': 'text/plain',
//         'Content-Disposition': 'attachment; filename=repression.user.js',
//       });
//       res.write(userscript);
//       res.end();
//     });
//   };

//   var urlObj = url.parse(req.url, true);
//   db.Insert('users')
//   .set({
//     email: urlObj.query.email,
//     expires: urlObj.query.expires,
//     repress: _.sample(['posemo', 'negemo']),
//   })
//   .execute(function(err) {
//     // ignore UNIQUE collisions
//     if (err && err.code != '23505') return res.die('Error: ' + err.toString());

//     db.Select('users')
//     .whereEqual({email: urlObj.query.emaill})
//     .execute(function(err, rows) {
//       if (err) return res.die(err);
//       download(rows[0]);
//     });
//   });
// });

/** GET /interface.html

This is the CORS enabler, and communicates with the userscript
sitting on the Facebook homepage via window.postMessage between the iframe,
which loaded interface.html, and the iframe's parent page, Facebook.
*/
R.get(/^\/interface.html/, function(req, res, m) {
  send(req, 'interface.html').root(static_root).on('error', function(err) {
    res.status(err.status || 500).die('static error: ' + err.message);
  }).pipe(res);
});

/** POST /repress

Given a JSON list of strings, determine which of them should be repressed.

We don't need any fancy CORS since it'll be accessed only by the interface.html iframe.

*/
R.post(/^\/repress/, function(req, res, m) {
  // var repress_category = req.headers['x-repress'];
  async.auto({
    data: function(callback) {
      req.readData(callback);
    },
    users: function(callback) {
      var username = req.headers['x-username'];
      db.Select('users').whereEqual({username: username}).execute(callback);
    },
  }, function(err, payload) {
    if (err) return res.die(err);
    if (!Array.isArray(payload.data)) return res.die('/repress only accepts an array of strings');
    if (payload.users.length === 0) return res.die('no user could be found with that username');

    var user = payload.users[0];

    var posts = []; // just the repressed ones
    var repressions = payload.data.map(function(datum) {
      // returns a list of booleans: whether or not to show the given post
      var string = String(datum);
      var matches = liwc.matches(string);
      var match = matches[user.repress];
      // logger.info('matching string: %j', matches);
      if (match) {
        posts.push({
          user_id: user.id,
          // author: '',
          content: string,
          match: match[0],
        });
        return true;
      }
      return !!match;
    });
    res.json(repressions);

    // to do this will a single INSERT () VALUES (), (), (), ...; command
    async.each(posts, function(post, callback) {
      db.Insert('posts').set(post).execute(callback);
    }, function(err) {
      if (err) logger.error(err);
    });
  });
});

/**
For example:

GET /results?username=io@henrian.com&expires=2014-07-11T21:39:17.863Z
*/
R.get(/^\/results.json/, function(req, res, m) {
  var urlObj = url.parse(req.url, true);
  db.Select('posts JOIN users ON users.id = posts.user_id')
  .where('users.username = ?', urlObj.query.username)
  .limit(200)
  .orderBy('posts.created DESC')
  .execute(function(err, rows) {
    if (err) return res.die(err);
    res.ngjson(rows);
  });
});

module.exports = R.route.bind(R);
<commit_msg>Automatically create user in /repress if needed<commit_after>/*jslint node: true */
var async = require('async');
var fs = require('fs');
var path = require('path');
var url = require('url');

var _ = require('lodash');
var send = require('send');
var logger = require('loge');
var Router = require('regex-router');

var liwc = require('../liwc');
var db = require('../db');

var static_root = path.join(__dirname, '..', 'static');
var R = new Router(function(req, res) {
  send(req, 'layout.html').root(static_root).on('error', function(err) {
    res.status(err.status || 500).die('static error: ' + err.message);
  }).pipe(res);
});

// expose other /static files
R.get(/^\/static\/([^?]+)(\?|$)/, function(req, res, m) {
  send(req, m[1]).root(static_root).on('error', function(err) {
    res.status(err.status || 500).die('static error: ' + err.message);
  }).on('directory', function() {
    res.status(404).die('No resource at: ' + req.url);
  }).pipe(res);
});

/**
For example:

GET /repression.user.js?email=io@henrian.com&expires=2014-07-11T21:39:17.863Z
*/
// R.get(/^\/repression.user.js/, function(req, res, m) {
//   var download = function(user) {
//     // user has .email, .expires, and .repress properties
//     var userscript_path = path.join(__dirname, '..', 'repression.user.js');
//     fs.readFile(userscript_path, {encoding: 'utf8'}, function(err, userscript_template) {
//       if (err) return res.die(err);

//       var config_json = JSON.stringify(user);
//       var userscript = userscript_template.replace('CONFIG_JSON', config_json);

//       res.writeHead(200, {
//         'Content-Type': 'text/plain',
//         'Content-Disposition': 'attachment; filename=repression.user.js',
//       });
//       res.write(userscript);
//       res.end();
//     });
//   };

//   var urlObj = url.parse(req.url, true);
//   db.Insert('users')
//   .set({
//     email: urlObj.query.email,
//     expires: urlObj.query.expires,
//     repress: _.sample(['posemo', 'negemo']),
//   })
//   .execute(function(err) {
//     // ignore UNIQUE collisions
//     if (err && err.code != '23505') return res.die('Error: ' + err.toString());

//     db.Select('users')
//     .whereEqual({email: urlObj.query.emaill})
//     .execute(function(err, rows) {
//       if (err) return res.die(err);
//       download(rows[0]);
//     });
//   });
// });

/** GET /interface.html

This is the CORS enabler, and communicates with the userscript
sitting on the Facebook homepage via window.postMessage between the iframe,
which loaded interface.html, and the iframe's parent page, Facebook.
*/
R.get(/^\/interface.html/, function(req, res, m) {
  send(req, 'interface.html').root(static_root).on('error', function(err) {
    res.status(err.status || 500).die('static error: ' + err.message);
  }).pipe(res);
});

/** POST /repress

Given a JSON array of {author: String, text: String} objects, determine which
of them should be repressed. Returns a list of bools exactly as long as the
list of objects received.

We don't need any fancy CORS since it'll be accessed only by the interface.html iframe.

*/
R.post(/^\/repress/, function(req, res, m) {
  var username = req.headers['x-username'];
  if (!username) res.die('You must supply a username via the "x-username" header');

  async.auto({
    data: function(callback) {
      req.readData(callback);
    },
    user: function(callback) {
      db.Select('users')
      .whereEqual({username: username})
      .execute(function(err, rows) {
        if (err) return callback(err);

        if (rows.length === 0) {
          logger.info('no user could be found with the username "%s"; creating one', username);

          var one_week_from_now = new Date(new Date().getTime() + (7 * 86400 * 1000));

          db.Insert('users')
          .set({
            username: username,
            repress: _.sample(['posemo', 'negemo']),
            expires: one_week_from_now,
          })
          .execute(function(err, rows) {
            if (err) return callback(err);

            callback(null, rows[0]);
          });
        }
        else {
          callback(null, rows[0]);
        }
      });
    },
  }, function(err, payload) {
    if (err) return res.die(err);
    if (!Array.isArray(payload.data)) return res.die('/repress only accepts an array of strings');
    // payload is an object: {data: ..., user: Object}

    var posts = []; // just the repressed ones
    var repressions = payload.data.map(function(datum) {
      // returns a list of booleans: whether or not to show the given post
      var author = String(datum.author);
      var content = String(datum.content);

      var matches = liwc.matches(content);
      var match = matches[payload.user.repress];
      if (match) {
        posts.push({
          user_id: payload.user.id,
          author: author,
          content: content,
          match: match[0],
        });
        return true;
      }
      return !!match;
    });
    res.json(repressions);

    // to do this will a single INSERT () VALUES (), (), (), ...; command
    async.each(posts, function(post, callback) {
      db.Insert('posts').set(post).execute(callback);
    }, function(err) {
      if (err) logger.error(err);
    });
  });
});

/**
For example:

GET /results?username=io@henrian.com&expires=2014-07-11T21:39:17.863Z
*/
R.get(/^\/results.json/, function(req, res, m) {
  var urlObj = url.parse(req.url, true);
  db.Select('posts JOIN users ON users.id = posts.user_id')
  .where('users.username = ?', urlObj.query.username)
  .limit(200)
  .orderBy('posts.created DESC')
  .execute(function(err, rows) {
    if (err) return res.die(err);
    res.ngjson(rows);
  });
});

module.exports = R.route.bind(R);
<|endoftext|>


<commit_before>"use strict";

function AnimationTab(parent, closeable, container, index)
{
	TabElement.call(this, parent, closeable, container, index, "Animation", Editor.filePath + "icons/misc/animation.png");

	var self = this;

	this.mixer = null;
	this.object = null;
	this.clock = new THREE.Clock();

	this.zoom = 120.0; //Pixels/sec
	this.timebarHeight = 0;

	//Bar
	this.bar = document.createElement("div");
	this.bar.style.position = "absolute";
	this.bar.style.height = "20px";
	this.bar.style.backgroundColor = Editor.theme.barColor;
	this.element.appendChild(this.bar);

	//Animation
	this.animationButton = new Button(this.bar);
	this.animationButton.position.set(0, 0);
	this.animationButton.size.set(100, 20);
	this.animationButton.setText("Add")
	this.animationButton.updateInterface();
	this.animationButton.setCallback(function()
	{
		if(self.object !== null)
		{
			if(self.object.animations === undefined)
			{
				self.object.animations = [];
			}

			var clip = new AnimationClip("Animation" + self.object.animations.length, 10, []);

			var position = new THREE.VectorKeyframeTrack(".position", [0], self.object.position.toArray());
			position.setInterpolation(THREE.InterpolateLinear);
			position.setColor("#FF0000");
			clip.tracks.push(position);

			var scale = new THREE.VectorKeyframeTrack(".scale", [0], self.object.scale.toArray());
			scale.setInterpolation(THREE.InterpolateLinear);
			scale.setColor("#00FF00");
			clip.tracks.push(scale);

			var quaternion = new THREE.QuaternionKeyframeTrack(".quaternion", [0], self.object.quaternion.toArray());
			quaternion.setInterpolation(THREE.InterpolateLinear);
			quaternion.setColor("#0000FF");
			clip.tracks.push(quaternion);

			var visible = new THREE.BooleanKeyframeTrack(".visible", [0], [self.object.visible]);
			visible.setInterpolation(THREE.InterpolateDiscrete);
			visible.setColor("#FFFF00");
			clip.tracks.push(visible);

			self.object.animations.push(clip);
			self.attach(self.object);
		}
	});

	this.play = new Button(this.bar);
	this.play.position.set(100, 0);
	this.play.size.set(100, 20);
	this.play.setText("Play")
	this.play.updateInterface();
	this.play.setCallback(function()
	{
		if(self.mixer == null)
		{
			alert("No animation found!");
			return;
		}

		if(self.mixer.playing)
		{
			self.mixer.pause();
			self.play.setText("Play");
		}
		else
		{
			self.mixer.play();
			self.play.setText("Pause");
		}
	});

	this.stop = new Button(this.bar);
	this.stop.position.set(200, 0);
	this.stop.size.set(100, 20);
	this.stop.setText("Stop");
	this.stop.updateInterface();
	this.stop.setCallback(function()
	{
		if(self.mixer == null)
		{
			alert("No animation playing!");
			return;
		}

		self.play.setText("Play");
		self.mixer.stop();
	});

	this.zoomSlider = new Slider(this.bar);
	this.zoomSlider.size.set(150, 10);
	this.zoomSlider.position.set(400, 0);
	this.zoomSlider.setStep(10);
	this.zoomSlider.setRange(20, 1000);
	this.zoomSlider.updateInterface();
	this.zoomSlider.setValue(this.zoom);
	this.zoomSlider.setOnChange(function()
	{
		self.zoom = self.zoomSlider.getValue();
		self.createTimeline();
	});

	//Timeline
	this.timeline = document.createElement("div");
	this.timeline.style.position = "absolute";
	this.timeline.style.overflow = "auto";
	this.timeline.style.top = "20px";
	this.element.appendChild(this.timeline);

	//Track information
	this.info = document.createElement("div");
	this.info.style.position = "absolute";
	this.info.style.backgroundColor = Editor.theme.barColor;
	this.timeline.appendChild(this.info);

	//Temporary variables for mouse movement
	var mouse = 0, initial = 0;

	//Resize tab
	this.tab = document.createElement("div");
	this.tab.style.position = "absolute";
	this.tab.style.width = "5px";
	this.tab.style.backgroundColor = Editor.theme.barColor;
	this.tab.style.cursor = "e-resize";
	this.tab.position = 250;
	this.timeline.appendChild(this.tab);

	this.tab.onmousedown = function(event)
	{
		mouse = event.clientX;
		initial = this.position;
		self.tabManager.create();
	};

	this.tabManager = new EventManager();
	this.tabManager.add(window, "mousemove", function(event)
	{
		self.tab.position = initial + (event.clientX - mouse);
		self.updateInterface();
	});

	this.tabManager.add(window, "mouseup", function(event)
	{
		self.tabManager.destroy();
	});

	//Tracks
	this.tracks = document.createElement("div");
	this.tracks.style.position = "absolute";
	this.tracks.style.backgroundColor = Editor.theme.panelColor;
	this.timeline.appendChild(this.tracks);

	//Seekbar
	this.seek = document.createElement("div");
	this.seek.style.position = "absolute";
	this.seek.style.backgroundColor = "#FFFFFF";
	this.seek.style.zIndex = "100";
	this.seek.style.top = "0px";
	this.seek.style.left = "0px";
	this.seek.style.width = "4px";
	this.seek.style.overflow = "hidden";
	this.seek.style.cursor = "e-resize";
	this.seek.onmousedown = function(event)
	{
		if(self.mixer !== null)
		{
			initial = self.mixer._actions[0].time;
			mouse = event.clientX;
			self.manager.create();
		}
	};

	this.manager = new EventManager();
	this.manager.add(window, "mousemove", function(event)
	{
		var time = initial + (event.clientX - mouse) / self.zoom;
		self.mixer.setTime(time > 0 ? time : 0);

		Interface.panel.updatePanel();
	});

	this.manager.add(window, "mouseup", function(event)
	{
		self.manager.destroy();
	});
}

AnimationTab.prototype = Object.create(TabElement.prototype);

AnimationTab.prototype.deactivate = function()
{
	TabElement.prototype.deactivate.call(this);

	if(this.mixer !== null && this.mixer.playing)
	{
		this.play.setText("Play");
		this.mixer.stop();
	}
};

//Attach object to animation editor
AnimationTab.prototype.attach = function(object)
{
	this.object = object;
	this.createAnimationMixer();

	//Create timeline and animation mixer
	if(this.object.animations !== undefined)
	{
		this.createTimeline();
	}
	else
	{
		this.clearTimeline();
	}
};

//Create a new animation mixer
AnimationTab.prototype.createAnimationMixer = function(keepTime)
{
	var time = 0;

	//Remove old mixer
	if(this.mixer !== null)
	{
		if(keepTime)
		{
			time = this.mixer.time;
		}

		this.play.setText("Play");
		this.mixer.stop();
		this.mixer.dispose();
		this.mixer = null;
	}

	//Check if the object has animations
	if(this.object.animations !== undefined)
	{
		this.mixer = new AnimationMixer(this.object);
		this.mixer.createActions(this.object.animations);
		this.mixer.setTime(time);
	}
};

AnimationTab.prototype.update = function()
{
	if(this.mixer !== null)
	{
		if(this.mixer._actions.length > 0)
		{
			this.seek.style.left = (this.mixer._actions[0].time * this.zoom) + "px";
		}
		else
		{
			this.seek.style.left = (this.mixer.time * this.zoom) + "px";
		}

		this.mixer.update(this.clock.getDelta());

		//Update object panel when playing
		if(this.mixer.playing)
		{
			Interface.panel.updatePanel();
		}
	}
};

//Clear timeline division
AnimationTab.prototype.clearTimeline = function()
{
	while(this.tracks.firstChild)
	{
		this.tracks.removeChild(this.tracks.firstChild);
	}
	while(this.info.firstChild)
	{
		this.info.removeChild(this.info.firstChild);
	}
};

//Create new timeline elements
AnimationTab.prototype.createTimeline = function()
{
	this.clearTimeline();

	if(this.object === null || this.object.animations === undefined)
	{
		return;
	}

	this.tracks.appendChild(this.seek);

	var self = this;

	var animations = this.object.animations;
	var duration = 0;

	this.timebarHeight = 0;

	//Animations
	for(var i = 0; i < animations.length; i++)
	{
		var button = new AnimationButton(this.info, this, animations[i]);
		button.position.set(0, this.timebarHeight);
		button.size.set(0, 30);
		button.updateInterface();

		var tracks = animations[i].tracks;
		var duration = animations[i].duration;
		var width = this.zoom * duration + 1;
		var height = 30 * tracks.length + 1;

		var block = new AnimationOptions(this.tracks, this, animations[i]);
		block.position.set(0, this.timebarHeight);
		block.size.set(width, 30);
		block.updateInterface();

		this.timebarHeight += 30;

		//Timegrid
		var timegrid = document.createElement("canvas");
		timegrid.style.position = "absolute";
		timegrid.style.top = this.timebarHeight + "px";
		timegrid.style.left = "0px";
		timegrid.style.width = width + "px";
		timegrid.style.height = height + "px";
		timegrid.width = width;
		timegrid.height = height;
		this.tracks.appendChild(timegrid);

		var context = timegrid.getContext("2d");
		context.fillStyle = Editor.theme.barColor;

		//Horizontal lines
		for(var l = 0; l <= height; l += 30)
		{
			context.fillRect(0, l, width, 1);
		}

		//Vertical lines
		for(var l = 0, step = this.zoom / 10; l <= width; l += step)
		{
			context.fillRect(l, 0, 1, height);
		}

		//Tracks
		for(var j = 0; j < tracks.length; j++)
		{
			var track = new AnimationTrack(this.tracks, this, tracks[j]);
			track.position.set(0, this.timebarHeight);
			track.size.set(this.zoom * animations[i].duration, 30);
			track.updateInterface();

			var button = new AnimationTrackButton(this.info, this, animations[i], tracks[j], track);
			button.position.set(0, this.timebarHeight);
			button.size.set(0, 30);
			button.updateInterface();

			this.timebarHeight += 30;
		}
	}

	this.updateInterface();
};

AnimationTab.prototype.addKeyFrame = function(track, object)
{
	var attributes = track.name.split(".");
	var value = object;

	for(var i = 0; i < attributes.length; i++)
	{
		if(attributes !== "")
		{
			var newValue = value[attributes[i]];

			if(newValue !== undefined)
			{
				value = newValue;
			}
		}
	}

	value = (value.toArray !== undefined) ? value.toArray() : [value];

	//Check if there is already a keyframe with same time
	for(var i = 0; i < track.times.length; i++)
	{
		if(track.times[i] === this.mixer.time)
		{
			break;
		}
	}

	//If there is already a keyframe with time update values
	if(i < track.times.length)
	{
		var valueSize = track.getValueSize();
		var index = i * valueSize;

		for(var i = 0; i < valueSize; i++)
		{
			track.values[index] = value[i];
			index++;
		}
	}
	//Add new keyframe to track
	else
	{
		var times = [];
		for(var i = 0; i < track.times.length; i++)
		{
			times.push(track.times[i]);
		}
		times.push(this.mixer.time);

		var values = [];
		for(var i = 0; i < track.values.length; i++)
		{
			values.push(track.values[i]);
		}
		values = values.concat(value);

		track.times = new Float32Array(times);
		track.values = new Float32Array(values);

		track.sort();
	}
};

AnimationTab.prototype.updateInterface = function()
{
	if(this.visible)
	{
		this.element.style.display = "block";
		this.element.style.top = this.position.y + "px";
		this.element.style.left = this.position.x + "px";
		this.element.style.width = this.size.x + "px";
		this.element.style.height = this.size.y + "px";

		this.timeline.style.width = this.size.x + "px";
		this.timeline.style.height = (this.size.y - 20) + "px";

		this.bar.style.width = this.size.x + "px";

		//Resizable division
		this.info.style.height = this.timeline.scrollHeight + "px";
		this.info.style.width = this.tab.position + "px";

		this.tab.style.height = this.timeline.scrollHeight + "px";
		this.tab.style.left = this.info.style.width;

		this.tracks.style.left = (this.tab.position + 5) + "px";
		this.tracks.style.width = (this.size.x - this.tab.position - 5) + "px";

		//Seekbar
		this.seek.style.height = this.timeline.scrollHeight + "px";
	}
	else
	{
		this.element.style.display = "none";
	}
};
<commit_msg>Zoom slider position<commit_after>"use strict";

function AnimationTab(parent, closeable, container, index)
{
	TabElement.call(this, parent, closeable, container, index, "Animation", Editor.filePath + "icons/misc/animation.png");

	var self = this;

	this.mixer = null;
	this.object = null;
	this.clock = new THREE.Clock();

	this.zoom = 120.0; //Pixels/sec
	this.timebarHeight = 0;

	//Bar
	this.bar = document.createElement("div");
	this.bar.style.position = "absolute";
	this.bar.style.height = "20px";
	this.bar.style.backgroundColor = Editor.theme.barColor;
	this.element.appendChild(this.bar);

	//Animation
	this.animationButton = new Button(this.bar);
	this.animationButton.position.set(0, 0);
	this.animationButton.size.set(100, 20);
	this.animationButton.setText("Add")
	this.animationButton.updateInterface();
	this.animationButton.setCallback(function()
	{
		if(self.object !== null)
		{
			if(self.object.animations === undefined)
			{
				self.object.animations = [];
			}

			var clip = new AnimationClip("Animation" + self.object.animations.length, 10, []);

			var position = new THREE.VectorKeyframeTrack(".position", [0], self.object.position.toArray());
			position.setInterpolation(THREE.InterpolateLinear);
			position.setColor("#FF0000");
			clip.tracks.push(position);

			var scale = new THREE.VectorKeyframeTrack(".scale", [0], self.object.scale.toArray());
			scale.setInterpolation(THREE.InterpolateLinear);
			scale.setColor("#00FF00");
			clip.tracks.push(scale);

			var quaternion = new THREE.QuaternionKeyframeTrack(".quaternion", [0], self.object.quaternion.toArray());
			quaternion.setInterpolation(THREE.InterpolateLinear);
			quaternion.setColor("#0000FF");
			clip.tracks.push(quaternion);

			var visible = new THREE.BooleanKeyframeTrack(".visible", [0], [self.object.visible]);
			visible.setInterpolation(THREE.InterpolateDiscrete);
			visible.setColor("#FFFF00");
			clip.tracks.push(visible);

			self.object.animations.push(clip);
			self.attach(self.object);
		}
	});

	this.play = new Button(this.bar);
	this.play.position.set(100, 0);
	this.play.size.set(100, 20);
	this.play.setText("Play")
	this.play.updateInterface();
	this.play.setCallback(function()
	{
		if(self.mixer == null)
		{
			alert("No animation found!");
			return;
		}

		if(self.mixer.playing)
		{
			self.mixer.pause();
			self.play.setText("Play");
		}
		else
		{
			self.mixer.play();
			self.play.setText("Pause");
		}
	});

	this.stop = new Button(this.bar);
	this.stop.position.set(200, 0);
	this.stop.size.set(100, 20);
	this.stop.setText("Stop");
	this.stop.updateInterface();
	this.stop.setCallback(function()
	{
		if(self.mixer == null)
		{
			alert("No animation playing!");
			return;
		}

		self.play.setText("Play");
		self.mixer.stop();
	});

	this.zoomSlider = new Slider(this.bar);
	this.zoomSlider.size.set(100, 10);
	this.zoomSlider.position.set(5, 2);
	this.zoomSlider.setStep(10);
	this.zoomSlider.setRange(20, 1000);
	this.zoomSlider.updatePosition(Element.TOP_RIGHT);
	this.zoomSlider.updateSize();
	this.zoomSlider.setValue(this.zoom);
	this.zoomSlider.setOnChange(function()
	{
		self.zoom = self.zoomSlider.getValue();
		self.createTimeline();
	});

	this.zoomText = new Text(this.bar);
	this.zoomText.setText("Zoom");
	this.zoomText.size.set(50, 20);
	this.zoomText.position.set(110, 0);
	this.zoomText.updatePosition(Element.TOP_RIGHT);
	this.zoomText.updateSize();

	//Timeline
	this.timeline = document.createElement("div");
	this.timeline.style.position = "absolute";
	this.timeline.style.overflow = "auto";
	this.timeline.style.top = "20px";
	this.element.appendChild(this.timeline);

	//Track information
	this.info = document.createElement("div");
	this.info.style.position = "absolute";
	this.info.style.backgroundColor = Editor.theme.barColor;
	this.timeline.appendChild(this.info);

	//Temporary variables for mouse movement
	var mouse = 0, initial = 0;

	//Resize tab
	this.tab = document.createElement("div");
	this.tab.style.position = "absolute";
	this.tab.style.width = "5px";
	this.tab.style.backgroundColor = Editor.theme.barColor;
	this.tab.style.cursor = "e-resize";
	this.tab.position = 250;
	this.timeline.appendChild(this.tab);

	this.tab.onmousedown = function(event)
	{
		mouse = event.clientX;
		initial = this.position;
		self.tabManager.create();
	};

	this.tabManager = new EventManager();
	this.tabManager.add(window, "mousemove", function(event)
	{
		self.tab.position = initial + (event.clientX - mouse);
		self.updateInterface();
	});

	this.tabManager.add(window, "mouseup", function(event)
	{
		self.tabManager.destroy();
	});

	//Tracks
	this.tracks = document.createElement("div");
	this.tracks.style.position = "absolute";
	this.tracks.style.backgroundColor = Editor.theme.panelColor;
	this.timeline.appendChild(this.tracks);

	//Seekbar
	this.seek = document.createElement("div");
	this.seek.style.position = "absolute";
	this.seek.style.backgroundColor = "#FFFFFF";
	this.seek.style.zIndex = "100";
	this.seek.style.top = "0px";
	this.seek.style.left = "0px";
	this.seek.style.width = "4px";
	this.seek.style.overflow = "hidden";
	this.seek.style.cursor = "e-resize";
	this.seek.onmousedown = function(event)
	{
		if(self.mixer !== null)
		{
			initial = self.mixer._actions[0].time;
			mouse = event.clientX;
			self.manager.create();
		}
	};

	this.manager = new EventManager();
	this.manager.add(window, "mousemove", function(event)
	{
		var time = initial + (event.clientX - mouse) / self.zoom;
		self.mixer.setTime(time > 0 ? time : 0);

		Interface.panel.updatePanel();
	});

	this.manager.add(window, "mouseup", function(event)
	{
		self.manager.destroy();
	});
}

AnimationTab.prototype = Object.create(TabElement.prototype);

AnimationTab.prototype.deactivate = function()
{
	TabElement.prototype.deactivate.call(this);

	if(this.mixer !== null && this.mixer.playing)
	{
		this.play.setText("Play");
		this.mixer.stop();
	}
};

//Attach object to animation editor
AnimationTab.prototype.attach = function(object)
{
	this.object = object;
	this.createAnimationMixer();

	//Create timeline and animation mixer
	if(this.object.animations !== undefined)
	{
		this.createTimeline();
	}
	else
	{
		this.clearTimeline();
	}
};

//Create a new animation mixer
AnimationTab.prototype.createAnimationMixer = function(keepTime)
{
	var time = 0;

	//Remove old mixer
	if(this.mixer !== null)
	{
		if(keepTime)
		{
			time = this.mixer.time;
		}

		this.play.setText("Play");
		this.mixer.stop();
		this.mixer.dispose();
		this.mixer = null;
	}

	//Check if the object has animations
	if(this.object.animations !== undefined)
	{
		this.mixer = new AnimationMixer(this.object);
		this.mixer.createActions(this.object.animations);
		this.mixer.setTime(time);
	}
};

AnimationTab.prototype.update = function()
{
	if(this.mixer !== null)
	{
		if(this.mixer._actions.length > 0)
		{
			this.seek.style.left = (this.mixer._actions[0].time * this.zoom) + "px";
		}
		else
		{
			this.seek.style.left = (this.mixer.time * this.zoom) + "px";
		}

		this.mixer.update(this.clock.getDelta());

		//Update object panel when playing
		if(this.mixer.playing)
		{
			Interface.panel.updatePanel();
		}
	}
};

//Clear timeline division
AnimationTab.prototype.clearTimeline = function()
{
	while(this.tracks.firstChild)
	{
		this.tracks.removeChild(this.tracks.firstChild);
	}
	while(this.info.firstChild)
	{
		this.info.removeChild(this.info.firstChild);
	}
};

//Create new timeline elements
AnimationTab.prototype.createTimeline = function()
{
	this.clearTimeline();

	if(this.object === null || this.object.animations === undefined)
	{
		return;
	}

	this.tracks.appendChild(this.seek);

	var self = this;

	var animations = this.object.animations;
	var duration = 0;

	this.timebarHeight = 0;

	//Animations
	for(var i = 0; i < animations.length; i++)
	{
		var button = new AnimationButton(this.info, this, animations[i]);
		button.position.set(0, this.timebarHeight);
		button.size.set(0, 30);
		button.updateInterface();

		var tracks = animations[i].tracks;
		var duration = animations[i].duration;
		var width = this.zoom * duration + 1;
		var height = 30 * tracks.length + 1;

		var block = new AnimationOptions(this.tracks, this, animations[i]);
		block.position.set(0, this.timebarHeight);
		block.size.set(width, 30);
		block.updateInterface();

		this.timebarHeight += 30;

		//Timegrid
		var timegrid = document.createElement("canvas");
		timegrid.style.position = "absolute";
		timegrid.style.top = this.timebarHeight + "px";
		timegrid.style.left = "0px";
		timegrid.style.width = width + "px";
		timegrid.style.height = height + "px";
		timegrid.width = width;
		timegrid.height = height;
		this.tracks.appendChild(timegrid);

		var context = timegrid.getContext("2d");
		context.fillStyle = Editor.theme.barColor;

		//Horizontal lines
		for(var l = 0; l <= height; l += 30)
		{
			context.fillRect(0, l, width, 1);
		}

		//Vertical lines
		for(var l = 0, step = this.zoom / 10; l <= width; l += step)
		{
			context.fillRect(l, 0, 1, height);
		}

		//Tracks
		for(var j = 0; j < tracks.length; j++)
		{
			var track = new AnimationTrack(this.tracks, this, tracks[j]);
			track.position.set(0, this.timebarHeight);
			track.size.set(this.zoom * animations[i].duration, 30);
			track.updateInterface();

			var button = new AnimationTrackButton(this.info, this, animations[i], tracks[j], track);
			button.position.set(0, this.timebarHeight);
			button.size.set(0, 30);
			button.updateInterface();

			this.timebarHeight += 30;
		}
	}

	this.updateInterface();
};

AnimationTab.prototype.addKeyFrame = function(track, object)
{
	var attributes = track.name.split(".");
	var value = object;

	for(var i = 0; i < attributes.length; i++)
	{
		if(attributes !== "")
		{
			var newValue = value[attributes[i]];

			if(newValue !== undefined)
			{
				value = newValue;
			}
		}
	}

	value = (value.toArray !== undefined) ? value.toArray() : [value];

	//Check if there is already a keyframe with same time
	for(var i = 0; i < track.times.length; i++)
	{
		if(track.times[i] === this.mixer.time)
		{
			break;
		}
	}

	//If there is already a keyframe with time update values
	if(i < track.times.length)
	{
		var valueSize = track.getValueSize();
		var index = i * valueSize;

		for(var i = 0; i < valueSize; i++)
		{
			track.values[index] = value[i];
			index++;
		}
	}
	//Add new keyframe to track
	else
	{
		var times = [];
		for(var i = 0; i < track.times.length; i++)
		{
			times.push(track.times[i]);
		}
		times.push(this.mixer.time);

		var values = [];
		for(var i = 0; i < track.values.length; i++)
		{
			values.push(track.values[i]);
		}
		values = values.concat(value);

		track.times = new Float32Array(times);
		track.values = new Float32Array(values);

		track.sort();
	}
};

AnimationTab.prototype.updateInterface = function()
{
	if(this.visible)
	{
		this.element.style.display = "block";
		this.element.style.top = this.position.y + "px";
		this.element.style.left = this.position.x + "px";
		this.element.style.width = this.size.x + "px";
		this.element.style.height = this.size.y + "px";

		this.timeline.style.width = this.size.x + "px";
		this.timeline.style.height = (this.size.y - 20) + "px";

		this.bar.style.width = this.size.x + "px";

		//Resizable division
		this.info.style.height = this.timeline.scrollHeight + "px";
		this.info.style.width = this.tab.position + "px";

		this.tab.style.height = this.timeline.scrollHeight + "px";
		this.tab.style.left = this.info.style.width;

		this.tracks.style.left = (this.tab.position + 5) + "px";
		this.tracks.style.width = (this.size.x - this.tab.position - 5) + "px";

		//Seekbar
		this.seek.style.height = this.timeline.scrollHeight + "px";
	}
	else
	{
		this.element.style.display = "none";
	}
};
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import {
  //Badge,
  IconButton,
  Avatar,
  Menu,
  MenuItem,
  Popover,
} from 'material-ui';
import {
  // SocialNotificationsNone,
  // CommunicationMailOutline,
  // ActionDateRange,
  HardwareKeyboardArrowDown,
} from 'material-ui/svg-icons';
import { images } from '../../../../theme';
import style from './style.css';
import { Link } from 'react-router-dom';
import { withRouter } from 'react-router';
import { withApollo, compose } from 'react-apollo';
//import { graphql, gql } from 'react-apollo';

class BadgeExampleSimple extends Component {
  constructor(props) {
    super(props);
    this.state = {
      openUser: false,
      openNotification: false,
      openMail: false,
      openCalendar: false,
    };
    this.handleSignOut = this.handleSignOut.bind(this);
  }

  handleTouchTapUser = event => {
    event.preventDefault();

    this.setState({
      openUser: true,
      anchorEl: event.currentTarget,
    });
  };
  handleTouchTapNotification = event => {
    event.preventDefault();

    this.setState({
      openNotification: true,
      anchorEl: event.currentTarget,
    });
  };
  handleTouchTapMail = event => {
    event.preventDefault();
    this.setState({
      openMail: true,
      anchorEl: event.currentTarget,
    });
  };
  handleTouchTapCalendar = event => {
    event.preventDefault();

    this.setState({
      openCalendar: true,
      anchorEl: event.currentTarget,
    });
  };

  handleRequestClose = () => {
    this.setState({
      openUser: false,
      openNotification: false,
      openMail: false,
      openCalendar: false,
    });
  };

  handleSignOut() {
    localStorage.clear();
    this.props.client.resetStore();
    this.props.history.replace('/login');
  }
  render() {
    // const { loading } = this.props.data;
    // if (loading) return
Loading...
;
    return (