<commit_before>'use strict';

require('purecss');
require('highlight.js/styles/github.css');
require('react-ghfork/gh-fork-ribbon.ie.css');
require('react-ghfork/gh-fork-ribbon.css');
require('./main.css');
require('../style.css');

var React = require('react');
var App = require('./app.jsx');

main();

function main() {
  var app = document.getElementsByClassName('demonstration')[0];

  React.render(<App />, app);
}
<commit_msg>Render through ReactDOM<commit_after>'use strict';

require('purecss');
require('highlight.js/styles/github.css');
require('react-ghfork/gh-fork-ribbon.ie.css');
require('react-ghfork/gh-fork-ribbon.css');
require('./main.css');
require('../style.css');

var React = require('react');
var ReactDOM = require('react-dom');
var App = require('./app.jsx');

main();

function main() {
  var app = document.getElementsByClassName('demonstration')[0];

  ReactDOM.render(<App />, app);
}
<|endoftext|>


<commit_before>var    _ = require('underscore'),
     mbc = require('mbc-common'),
    conf = mbc.config.Caspa,
    search_options = mbc.config.Search,
    collections = mbc.config.Common.Collections,
    uuid = require('node-uuid'),
    logger = mbc.logger().addLogger('caspa_backends'),
    backboneio = require('backbone.io');

// Override mongoStore read method with custom
var searchWrapper = require('./searchWrapper.js');

var iobackends = module.exports = exports = function (db, publisher) {
    var self = this;
    this.middlewares = {
        debug: function (req, res, next) {
            logger.debug('Backend:', req.backend);
            logger.debug('Method: ', req.method);
            logger.debug('Channel:', req.channel);
            logger.debug('Options:', JSON.stringify(req.options));
            logger.debug('Model:  ', JSON.stringify(req.model));
            next();
        },

        id: function (req, res, next) {
            if( req.method == 'create' && req.model._id === undefined) {
                req.model._id = uuid.v1();
            }
            next();
        },

        publishJSON: function (req, res, next) {
            publisher.publishJSON([req.backend, req.method].join('.'), { model: req.model });
            next();
        }
    };

    this.backends = {
        app: {
            use: [backboneio.middleware.configStore()]
        },
        transform: {
            use: [this.middlewares.id],
            mongo: {
                db: db,
                collection: collections.Transforms,
                opts: { search: search_options.Transforms },
            }},
        media: {
            mongo: {
                db: db,
                collection: collections.Medias,
                opts: { search: search_options.Medias },
            }},
        piece: {
            use: [this.middlewares.id],
            mongo: {
                db: db,
                collection: collections.Pieces,
                opts: { search: search_options.Pieces },
            }},
        list: {
            use: [this.middlewares.id],
            mongo: {
                db: db,
                collection: collections.Lists,
                opts: { search: search_options.Lists },
            }},
        sched: {
            use: [this.middlewares.id, this.middlewares.publishJSON],
            mongo: {
                db: db,
                collection: collections.Scheds,
                opts: { search: search_options.Scheds },
            }},
        status: {
            use: [this.middlewares.id],
            mongo: {
                db: db,
                collection: collections.Status,
                opts: { search: search_options.Status },
            }},
        frame: {
            use: [backboneio.middleware.memoryStore(db, 'progress', {})],
        },
        mostomessages: {
            mongo: {
                db: db,
                collection: collections.Mostomessages,
                opts: { search: search_options.Mostomessages },
            }},
        sketch: {
            use: [this.middlewares.id],
            mongo: {
                db: db,
                collection: collections.Sketchs,
                opts: { search: search_options.Sketchs },
            }},
    };

    /* process the backends object to streamline code */
    var binded = [];
    _(this.backends).each (function (backend) {
        backend.io = backboneio.createBackend();
        if (backend.use) {
            _(backend.use).each (function (usefn) {
                backend.io.use(usefn);
            });
        }
        if (backend.mongo) {
            var mongo = _.extend ({db: db, opts: {}}, backend.mongo);
            var fn = _.identity;
            binded.push (backend.mongo.collection);

            if (_.has(mongo.opts, 'search'))
                fn = searchWrapper;
            backend.io.use(
                fn(backboneio.middleware.mongoStore(mongo.db,
                                                    mongo.collection,
                                                    mongo.opts)));
        }
        backend.io.use (self.middlewares.debug);
    });

    logger.info ('binding to mongo collections:', binded.join(', ') + '.');
};

iobackends.prototype.register_sync = function (collection, name) {
    var backend = this.get_io (name);
    function sync (method, model, options) {
        var event = {create: 'created', read: 'updated', update: 'updated', delete: 'removed'};

        logger.warn ('in sync for backend', name, method, event[method]);

        backend.emit (event[method], model.toJSON());
    };

    collection.osync = sync;
};

iobackends.prototype.emit = function (name, args) {
    _.apply(this.backends[name].emit, args);
};

iobackends.prototype.get_ios = function () {
    var ret = {};
    var self = this;
    _(_.keys(this.backends)).each (function (backend) {
        ret[backend + 'backend'] = self.backends[backend].io;
    });
    return ret;
};

iobackends.prototype.get = function (name) {
    return this.backends[name];
};

iobackends.prototype.get_io = function (name) {
    return this.get(name).io;
};
<commit_msg>middlewares -> middleware<commit_after>var    _ = require('underscore'),
     mbc = require('mbc-common'),
    conf = mbc.config.Caspa,
    search_options = mbc.config.Search,
    collections = mbc.config.Common.Collections,
    uuid = require('node-uuid'),
    logger = mbc.logger().addLogger('caspa_backends'),
    backboneio = require('backbone.io');

// Override mongoStore read method with custom
var searchWrapper = require('./searchWrapper.js');

var iobackends = module.exports = exports = function (db, publisher) {
    var self = this;
    this.middleware = {
        debug: function (req, res, next) {
            logger.debug('Backend:', req.backend);
            logger.debug('Method: ', req.method);
            logger.debug('Channel:', req.channel);
            logger.debug('Options:', JSON.stringify(req.options));
            logger.debug('Model:  ', JSON.stringify(req.model));
            next();
        },

        id: function (req, res, next) {
            if( req.method == 'create' && req.model._id === undefined) {
                req.model._id = uuid.v1();
            }
            next();
        },

        publishJSON: function (req, res, next) {
            publisher.publishJSON([req.backend, req.method].join('.'), { model: req.model });
            next();
        }
    };

    this.backends = {
        app: {
            use: [backboneio.middleware.configStore()]
        },
        transform: {
            use: [this.middleware.id],
            mongo: {
                db: db,
                collection: collections.Transforms,
                opts: { search: search_options.Transforms },
            }},
        media: {
            mongo: {
                db: db,
                collection: collections.Medias,
                opts: { search: search_options.Medias },
            }},
        piece: {
            use: [this.middleware.id],
            mongo: {
                db: db,
                collection: collections.Pieces,
                opts: { search: search_options.Pieces },
            }},
        list: {
            use: [this.middleware.id],
            mongo: {
                db: db,
                collection: collections.Lists,
                opts: { search: search_options.Lists },
            }},
        sched: {
            use: [this.middleware.id, this.middleware.publishJSON],
            mongo: {
                db: db,
                collection: collections.Scheds,
                opts: { search: search_options.Scheds },
            }},
        status: {
            use: [this.middleware.id],
            mongo: {
                db: db,
                collection: collections.Status,
                opts: { search: search_options.Status },
            }},
        frame: {
            use: [backboneio.middleware.memoryStore(db, 'progress', {})],
        },
        mostomessages: {
            mongo: {
                db: db,
                collection: collections.Mostomessages,
                opts: { search: search_options.Mostomessages },
            }},
        sketch: {
            use: [this.middleware.id],
            mongo: {
                db: db,
                collection: collections.Sketchs,
                opts: { search: search_options.Sketchs },
            }},
    };

    /* process the backends object to streamline code */
    var binded = [];
    _(this.backends).each (function (backend) {
        backend.io = backboneio.createBackend();
        if (backend.use) {
            _(backend.use).each (function (usefn) {
                backend.io.use(usefn);
            });
        }
        if (backend.mongo) {
            var mongo = _.extend ({db: db, opts: {}}, backend.mongo);
            var fn = _.identity;
            binded.push (backend.mongo.collection);

            if (_.has(mongo.opts, 'search'))
                fn = searchWrapper;
            backend.io.use(
                fn(backboneio.middleware.mongoStore(mongo.db,
                                                    mongo.collection,
                                                    mongo.opts)));
        }
        backend.io.use (self.middleware.debug);
    });

    logger.info ('binding to mongo collections:', binded.join(', ') + '.');
};

iobackends.prototype.register_sync = function (collection, name) {
    var backend = this.get_io (name);
    function sync (method, model, options) {
        var event = {create: 'created', read: 'updated', update: 'updated', delete: 'removed'};

        logger.warn ('in sync for backend', name, method, event[method]);

        backend.emit (event[method], model.toJSON());
    };

    collection.osync = sync;
};

iobackends.prototype.emit = function (name, args) {
    _.apply(this.backends[name].emit, args);
};

iobackends.prototype.get_ios = function () {
    var ret = {};
    var self = this;
    _(_.keys(this.backends)).each (function (backend) {
        ret[backend + 'backend'] = self.backends[backend].io;
    });
    return ret;
};

iobackends.prototype.get = function (name) {
    return this.backends[name];
};

iobackends.prototype.get_io = function (name) {
    return this.get(name).io;
};
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2015 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.
 */

CLASS({
  package: 'com.google.watlobby',
  name: 'Lobby',
  extends: 'foam.graphics.CView',
  traits: [ 'com.google.misc.Colors' ],

  requires: [
    'com.google.watlobby.SmallRemote',
    'com.google.watlobby.Remote',
    'com.google.watlobby.TopicApp',
    'com.google.watlobby.TopicDAO',
    'com.google.watlobby.Bubble',
    'com.google.watlobby.TopicBubble',
    'com.google.watlobby.AlbumBubble',
    'com.google.watlobby.PhotoBubble',
    'com.google.watlobby.Topic',
    'com.google.watlobby.VideoBubble',
//    'foam.demos.ClockView',
    'foam.demos.physics.PhysicalCircle',
    'foam.graphics.ImageCView',
    'foam.physics.PhysicsEngine as Collider',
    'foam.util.Timer'
  ],

  imports: [ 'timer', 'clearTimeout', 'setTimeout' ],
  exports: [ 'as lobby' ],

  properties: [
    { name: 'timer' },
    { name: 'clientMode', defaultValue: false, model_: 'BooleanProperty' },
    { name: 'n',          defaultValue: 25 },
    { name: 'slideshowDelay', model_: 'IntProperty' },
    { name: 'airBubbles', defaultValue: 0, model_: 'IntProperty' },
    { name: 'width',      defaultValue: window.innerWidth },
    { name: 'height',     defaultValue: window.innerHeight },
    { name: 'background', defaultValue: '#ccf' },
    { name: 'collider',   factory: function() {
      var c = this.Collider.create();
      var w = this.width;
      var h = this.height;
      var self = this;
      c.detectCollisions = function() {
        var cs = this.children;
        for ( var i = 0 ; i < cs.length ; i++ ) {
          var c1 = cs[i];
          this.updateChild(c1);
          var r = c1.r + 10;

          // Add Coriolis Effect
          var a = Math.atan2(c1.y-h/2, c1.x-w/2);
          var d = Movement.distance(c1.y-h/2, c1.x-w/2);
          // Keeps topic bubbles from going too far off screen
          // if ( c1.topic && d > h / 2 - r) c1.out_ = false;
          if ( d > w / 2 - r ) c1.out_ = false;
          if ( d < h/4 ) c1.out_ = true;
          // c1.color = c1.out_ ? 'orange' : 'blue';

          // The 0.9 gives it a slight outward push
          if ( c1.mass != c1.INFINITE_MASS )
            c1.applyMomentum((0.5+0.4*c1.$UID%11/10) * c1.mass/8, a+(c1.out_ ? 0.9 : 1.1)*Math.PI/2);

          // Make collision detection 5X faster by only checking every fifth time.
          if ( ( self.timer.i + i ) % 5 == 0 ) for ( var j = i+1 ; j < cs.length ; j++ ) {
            var c2 = cs[j];
            if ( c1.intersects(c2) ) this.collide(c1, c2);
          }
        }
      };
      return c;
    }},
    {
      name: 'topics',
      lazyFactory: function() {
        return this.TopicDAO.create({ clientMode: this.clientMode });
      }
    },
    { name: 'root', defaultValue: '' },
    {
      name: 'dir',
      defaultValue: '',
      postSet: function(_, d) {
        /*
        if ( ! n ) {
          this.topics.select({put: this.putTopic});
          return;
        }
        var ts = this.topics;
        var Topic = this.Topic;
        var self = this;
        ts.find(EQ(Topic.PARENT, n.topic.topic), {
          put: function() {
            self.topics.where(
              AND(
                NEQ(self.Topic.PARENT, n.topic.topic),
                NEQ(self.Topic.TOPIC, n.topic.topic))
            ).select({put: self.removeTopic});
            self.topics.where(
                EQ(self.Topic.PARENT, n.topic.topic)
            ).select({put: self.putTopic});
          },
          error: function() {
            // TODO: Only show parent and siblings
            self.topics.select({put: self.putTopic});
          }
        });
        */

      }
    },
    {
      name: 'selected',
      preSet: function(o, n) {
debugger;
        if ( o === n ) return o;

        var oc = this.findTopic(o);
        if ( oc ) oc.setSelected(false);

        var nc = this.findTopic(n);

        if ( nc && nc.setSelected ) {
          // Move-to-Front
          var i = this.children.indexOf(n);
          this.children[i] = this.children[this.children.length-1];
          this.children[this.children.length-1] = nc;

          nc.setSelected(true);

          this.clearTimeout(this.timeout_);
          this.timeout_ = this.setTimeout(function() { this.selected = null; }.bind(this), nc.topic.timeout * 1000);

          return n;
        }

        return null;
      },
      postSet: function(o, n) {
        if ( o === n || !n ) return;

        this.topics.find(EQ(this.Topic.PARENT, n), {
          put: function() {
            this.dir = n;
          }.bind(this)
        });
      },

      xxxpostSet: function(o, n) {
        if ( o === n ) return;
        if ( o ) {
          this.topics.where(
            AND(
              EQ(this.Topic.PARENT, o.topic.topic),
              EQ(this.Topic.PRIORITY, 0))
            ).select({put: this.removeTopic});
//          this.topics.select({put: this.putTopic});
        }
        if ( n && n.setSelected ) {
          this.topics.where(
            AND(
              EQ(this.Topic.PARENT, n.topic.topic),
              EQ(this.Topic.PRIORITY, 0))
          ).select({put: this.putTopic});
          this.topics.where(AND(NEQ(this.Topic.PARENT, n.topic.topic), NEQ(this.Topic.TOPIC, n.topic.topic))).select({put: this.removeTopic.bind(this)});
        } else if ( o ) {
          this.topics.where(
            NEQ(this.Topic.TOPIC, o.topic.topic)
          ).select({put: this.putTopic});
        }
      }
    }
  ],

  listeners: [
    function onClick(evt) {
      var t = this.findChildAt(evt.clientX, evt.clientY);
      this.selected = t && t.topic && t.topic.topic;
    },
    function putTopic(t) {
      if ( t.model === 'Background' ) {
        document.body.style.backgroundImage = 'url(' + t.image + ')';
        return;
      }
      if ( ! ( t.parent == this.dir || ( this.selected && t.topic == this.selected ) ) ) return;
//      console.log('***** putTopic: ', t.topic);
      var i = this.findTopicIndex(t);
      if ( i != -1 ) {
        if ( t.selected ) {
          this.selected = t;
          return;
        } else {
          this.children.splice(i, 1);
        }
      }

      if ( ! t.enabled ) return;

      var c = this.X.lookup('com.google.watlobby.' + t.model + 'Bubble').create({
        x: Math.random() * this.width,
        y: Math.random() * this.height,
        border: t.color,
        scaleX: 0,
        scaleY: 0,
        alpha: 0
      }, this.Y);
      c.topic = t;
      c.image = t.image;
      Movement.animate(1000, function() { c.alpha = 1.0; c.scaleX = c.scaleY = 1; })();
      c.roundImage = t.roundImage;
     // if ( t.color ) c.border = t.color;
      if ( t.background ) c.color = t.background;
      this.addChild(c);
      if ( this.children.length > 1 ) {
        var cs = this.children;
        var l = cs.length;
        var tmp = cs[l-1];
        cs[l-1] = cs[l-2];
        cs[l-2] = tmp;
      }
      c.mass = t.r/150;
      c.gravity = 0;
      c.friction = 0.94;
      this.collider.add(c);
    },
    function removeTopic(t) {
      var i = this.findTopicIndex(t);
      if ( i != -1 ) {
        var child = this.children[i];
        Movement.compile([
          [ 800, function() { child.alpha = 0; child.scaleX = child.scaleY = 0.1; } ],
          function() {
            this.removeChild(child);
            this.collider.remove(child);
          }.bind(this)
        ])();
      }
    }
  ],

  methods: [
    function initCView() {
      this.SUPER();

      GLOBAL.lobby = this;

      if ( ! this.timer ) {
        this.timer = this.Timer.create();
        this.timer.start();
      }

      this.addBubbles();

      this.topics.pipe({
        put:    this.putTopic,
        remove: this.removeTopic
      });

      document.body.addEventListener('click', this.onClick);

      var foam = this.ImageCView.create({x: 5, y: this.height-5-269/4, width: 837/4, height: 269/4, src: 'img/foampowered_red.png'});
      this.addChild(foam);

      /*
      var clock = this.ClockView.create({
        drawTicks: true,
        x: this.width-250,
        y: 250,
        r: (120-10)/2,
        scaleX: 4,
        scaleY: 4});
      this.addChild(clock);
      */

      this.collider.start();

      if ( this.slideshowDelay ) {
        var i = 0;
        var l = function() {
          var t = this.topics[i++];
          this.selected = t;
          if ( i == this.topics.length ) i = 0;
          this.X.setTimeout(l, t.hasOwnProperty('timeout') ? t.timeout*1000 : this.slideshowDelay*1000);
        }.bind(this);

        l();
      }
    },
    function findTopicIndex(t) {
      for ( var i = 0 ; i < this.children.length ; i++ ) {
        var c = this.children[i];
        if ( c.topic && c.topic.topic === t ) return i;
      }
      return -1;
    },
    function findTopic(t) {
      for ( var i = 0 ; i < this.children.length ; i++ ) {
        var c = this.children[i];
        if ( c.topic && c.topic.topic === t ) return c;
      }
    },
    function addBubbles() {
      var N = this.n;
      for ( var i = 0 ; i < N ; i++ ) {
        var color = this.COLORS[Math.floor(i / N * this.COLORS.length)];
        var c = this.Bubble.create({
          r: 10 + Math.random() * 60,
          x: Math.random() * this.width,
          y: Math.random() * this.height,
          color: null,
          border: color
        });
        this.addChild(c);

        c.mass = c.r/150;
        c.gravity = 0;
        c.friction = 0.94;
        this.collider.add(c);
      }
    },
    function openSmallRemoteUI() {
      var w = foam.ui.Window.create({window: window.open("", "Remote", "width=800, height=600, location=no, menubar=no, resizable=no, status=no, titlebar=no")});
      w.document.innerHTML = '';
      w.document.write('<title>Wat Lobby Remote</title>');
      var r = this.SmallRemote.create({topics: this.topics}, w.Y);
      r.write(w.Y);
    },
    function openRemoteUI() {
      var w = foam.ui.Window.create({window: window.open("", "Remote", "width=800, height=600, location=no, menubar=no, resizable=no, status=no, titlebar=no")});
      w.document.innerHTML = '';
      w.document.write('<title>Wat Lobby Remote</title>');
      var r = this.Remote.create({topics: this.topics}, w.Y);
      r.write(w.Y);
    },
    function openAdminUI() {
      var w = foam.ui.Window.create({window: window.open("", "Admin", "width=1200, height=700, location=no, menubar=no")});
      w.document.innerHTML = '';
      w.document.write('<title>Wat Lobby Admin</title><base href="/js/com/google/watlobby/">');
      var r = this.TopicApp.create({dao: this.topics}, w.Y);
      r.write(w.Y);
    },
    function destroy() {
      this.SUPER();
      this.collider.destroy();
    }
  ]
});
<commit_msg>WatLobby: Work on directory support.<commit_after>/**
 * @license
 * Copyright 2015 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.
 */

CLASS({
  package: 'com.google.watlobby',
  name: 'Lobby',
  extends: 'foam.graphics.CView',
  traits: [ 'com.google.misc.Colors' ],

  requires: [
    'com.google.watlobby.SmallRemote',
    'com.google.watlobby.Remote',
    'com.google.watlobby.TopicApp',
    'com.google.watlobby.TopicDAO',
    'com.google.watlobby.Bubble',
    'com.google.watlobby.TopicBubble',
    'com.google.watlobby.AlbumBubble',
    'com.google.watlobby.PhotoBubble',
    'com.google.watlobby.Topic',
    'com.google.watlobby.VideoBubble',
//    'foam.demos.ClockView',
    'foam.demos.physics.PhysicalCircle',
    'foam.graphics.ImageCView',
    'foam.physics.PhysicsEngine as Collider',
    'foam.util.Timer'
  ],

  imports: [ 'timer', 'clearTimeout', 'setTimeout' ],
  exports: [ 'as lobby' ],

  properties: [
    { name: 'timer' },
    { name: 'clientMode', defaultValue: false, model_: 'BooleanProperty' },
    { name: 'n',          defaultValue: 25 },
    { name: 'slideshowDelay', model_: 'IntProperty' },
    { name: 'airBubbles', defaultValue: 0, model_: 'IntProperty' },
    { name: 'width',      defaultValue: window.innerWidth },
    { name: 'height',     defaultValue: window.innerHeight },
    { name: 'background', defaultValue: '#ccf' },
    { name: 'collider',   factory: function() {
      var c = this.Collider.create();
      var w = this.width;
      var h = this.height;
      var self = this;
      c.detectCollisions = function() {
        var cs = this.children;
        for ( var i = 0 ; i < cs.length ; i++ ) {
          var c1 = cs[i];
          this.updateChild(c1);
          var r = c1.r + 10;

          // Add Coriolis Effect
          var a = Math.atan2(c1.y-h/2, c1.x-w/2);
          var d = Movement.distance(c1.y-h/2, c1.x-w/2);
          // Keeps topic bubbles from going too far off screen
          // if ( c1.topic && d > h / 2 - r) c1.out_ = false;
          if ( d > w / 2 - r ) c1.out_ = false;
          if ( d < h/4 ) c1.out_ = true;
          // c1.color = c1.out_ ? 'orange' : 'blue';

          // The 0.9 gives it a slight outward push
          if ( c1.mass != c1.INFINITE_MASS )
            c1.applyMomentum((0.5+0.4*c1.$UID%11/10) * c1.mass/8, a+(c1.out_ ? 0.9 : 1.1)*Math.PI/2);

          // Make collision detection 5X faster by only checking every fifth time.
          if ( ( self.timer.i + i ) % 5 == 0 ) for ( var j = i+1 ; j < cs.length ; j++ ) {
            var c2 = cs[j];
            if ( c1.intersects(c2) ) this.collide(c1, c2);
          }
        }
      };
      return c;
    }},
    {
      name: 'topics',
      lazyFactory: function() {
        return this.TopicDAO.create({ clientMode: this.clientMode });
      }
    },
    { name: 'root', defaultValue: '' },
    {
      name: 'dir',
      defaultValue: '',
      postSet: function(_, d) {
        /*
        if ( ! n ) {
          this.topics.select({put: this.putTopic});
          return;
        }
        var ts = this.topics;
        var Topic = this.Topic;
        var self = this;
        ts.find(EQ(Topic.PARENT, n.topic.topic), {
          put: function() {
            self.topics.where(
              AND(
                NEQ(self.Topic.PARENT, n.topic.topic),
                NEQ(self.Topic.TOPIC, n.topic.topic))
            ).select({put: self.removeTopic});
            self.topics.where(
                EQ(self.Topic.PARENT, n.topic.topic)
            ).select({put: self.putTopic});
          },
          error: function() {
            // TODO: Only show parent and siblings
            self.topics.select({put: self.putTopic});
          }
        });
        */

      }
    },
    {
      name: 'selected',
      preSet: function(o, n) {
        if ( o === n ) return o;

        var oc = this.findTopic(o);
        if ( oc ) oc.setSelected(false);

        var nc = this.findTopic(n);

        if ( nc && nc.setSelected ) {
          // Move-to-Front
          var i = this.children.indexOf(n);
          this.children[i] = this.children[this.children.length-1];
          this.children[this.children.length-1] = nc;

          nc.setSelected(true);

          this.clearTimeout(this.timeout_);
          this.timeout_ = this.setTimeout(function() { this.selected = null; }.bind(this), nc.topic.timeout * 1000);

          return n;
        }

        return null;
      },
      postSet: function(o, n) {
        if ( o === n || !n ) return;

        this.topics.find(EQ(this.Topic.PARENT, n), {
          put: function() {
            this.dir = n;
          }.bind(this)
        });
      },

      xxxpostSet: function(o, n) {
        if ( o === n ) return;
        if ( o ) {
          this.topics.where(
            AND(
              EQ(this.Topic.PARENT, o.topic.topic),
              EQ(this.Topic.PRIORITY, 0))
            ).select({put: this.removeTopic});
//          this.topics.select({put: this.putTopic});
        }
        if ( n && n.setSelected ) {
          this.topics.where(
            AND(
              EQ(this.Topic.PARENT, n.topic.topic),
              EQ(this.Topic.PRIORITY, 0))
          ).select({put: this.putTopic});
          this.topics.where(AND(NEQ(this.Topic.PARENT, n.topic.topic), NEQ(this.Topic.TOPIC, n.topic.topic))).select({put: this.removeTopic.bind(this)});
        } else if ( o ) {
          this.topics.where(
            NEQ(this.Topic.TOPIC, o.topic.topic)
          ).select({put: this.putTopic});
        }
      }
    }
  ],

  listeners: [
    function onClick(evt) {
      var t = this.findChildAt(evt.clientX, evt.clientY);
      if ( t ) {
        this.selected = t && t.topic && t.topic.topic;
      } else if ( this.dir ) {
        // CD up a directory
        this.selected = null;
        this.dir = this.findTopic(this.dir).topic.parent;
      }
    },
    function putTopic(t) {
      if ( t.model === 'Background' ) {
        document.body.style.backgroundImage = 'url(' + t.image + ')';
        return;
      }
      if ( ! ( t.parent == this.dir || ( this.selected && t.topic == this.selected ) ) ) return;
//      console.log('***** putTopic: ', t.topic);
      var i = this.findTopicIndex(t);
      if ( i != -1 ) {
        if ( t.selected ) {
          this.selected = t;
          return;
        } else {
          this.children.splice(i, 1);
        }
      }

      if ( ! t.enabled ) return;

      var c = this.X.lookup('com.google.watlobby.' + t.model + 'Bubble').create({
        x: Math.random() * this.width,
        y: Math.random() * this.height,
        border: t.color,
        scaleX: 0,
        scaleY: 0,
        alpha: 0
      }, this.Y);
      c.topic = t;
      c.image = t.image;
      Movement.animate(1000, function() { c.alpha = 1.0; c.scaleX = c.scaleY = 1; })();
      c.roundImage = t.roundImage;
     // if ( t.color ) c.border = t.color;
      if ( t.background ) c.color = t.background;
      this.addChild(c);
      if ( this.children.length > 1 ) {
        var cs = this.children;
        var l = cs.length;
        var tmp = cs[l-1];
        cs[l-1] = cs[l-2];
        cs[l-2] = tmp;
      }
      c.mass = t.r/150;
      c.gravity = 0;
      c.friction = 0.94;
      this.collider.add(c);
    },
    function removeTopic(t) {
      var i = this.findTopicIndex(t);
      if ( i != -1 ) {
        var child = this.children[i];
        Movement.compile([
          [ 800, function() { child.alpha = 0; child.scaleX = child.scaleY = 0.1; } ],
          function() {
            this.removeChild(child);
            this.collider.remove(child);
          }.bind(this)
        ])();
      }
    }
  ],

  methods: [
    function initCView() {
      this.SUPER();

      GLOBAL.lobby = this;

      if ( ! this.timer ) {
        this.timer = this.Timer.create();
        this.timer.start();
      }

      this.addBubbles();

      this.topics.pipe({
        put:    this.putTopic,
        remove: this.removeTopic
      });

      document.body.addEventListener('click', this.onClick);

      var foam = this.ImageCView.create({x: 5, y: this.height-5-269/4, width: 837/4, height: 269/4, src: 'img/foampowered_red.png'});
      this.addChild(foam);

      /*
      var clock = this.ClockView.create({
        drawTicks: true,
        x: this.width-250,
        y: 250,
        r: (120-10)/2,
        scaleX: 4,
        scaleY: 4});
      this.addChild(clock);
      */

      this.collider.start();

      if ( this.slideshowDelay ) {
        var i = 0;
        var l = function() {
          var t = this.topics[i++];
          this.selected = t;
          if ( i == this.topics.length ) i = 0;
          this.X.setTimeout(l, t.hasOwnProperty('timeout') ? t.timeout*1000 : this.slideshowDelay*1000);
        }.bind(this);

        l();
      }
    },
    function findTopicIndex(t) {
      for ( var i = 0 ; i < this.children.length ; i++ ) {
        var c = this.children[i];
        if ( c.topic && c.topic.topic === t ) return i;
      }
      return -1;
    },
    function findTopic(t) {
      for ( var i = 0 ; i < this.children.length ; i++ ) {
        var c = this.children[i];
        if ( c.topic && c.topic.topic === t ) return c;
      }
    },
    function addBubbles() {
      var N = this.n;
      for ( var i = 0 ; i < N ; i++ ) {
        var color = this.COLORS[Math.floor(i / N * this.COLORS.length)];
        var c = this.Bubble.create({
          r: 10 + Math.random() * 60,
          x: Math.random() * this.width,
          y: Math.random() * this.height,
          color: null,
          border: color
        });
        this.addChild(c);

        c.mass = c.r/150;
        c.gravity = 0;
        c.friction = 0.94;
        this.collider.add(c);
      }
    },
    function openSmallRemoteUI() {
      var w = foam.ui.Window.create({window: window.open("", "Remote", "width=800, height=600, location=no, menubar=no, resizable=no, status=no, titlebar=no")});
      w.document.innerHTML = '';
      w.document.write('<title>Wat Lobby Remote</title>');
      var r = this.SmallRemote.create({topics: this.topics}, w.Y);
      r.write(w.Y);
    },
    function openRemoteUI() {
      var w = foam.ui.Window.create({window: window.open("", "Remote", "width=800, height=600, location=no, menubar=no, resizable=no, status=no, titlebar=no")});
      w.document.innerHTML = '';
      w.document.write('<title>Wat Lobby Remote</title>');
      var r = this.Remote.create({topics: this.topics}, w.Y);
      r.write(w.Y);
    },
    function openAdminUI() {
      var w = foam.ui.Window.create({window: window.open("", "Admin", "width=1200, height=700, location=no, menubar=no")});
      w.document.innerHTML = '';
      w.document.write('<title>Wat Lobby Admin</title><base href="/js/com/google/watlobby/">');
      var r = this.TopicApp.create({dao: this.topics}, w.Y);
      r.write(w.Y);
    },
    function destroy() {
      this.SUPER();
      this.collider.destroy();
    }
  ]
});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2015 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.
 */

CLASS({
  package: 'com.google.watlobby',
  name: 'Topic',

  properties: [
    {
      name: 'topic'
    },
    {
      name: 'image'
    },
    {
      name: 'colour'
    },
    {
      name: 'r'
    },
    {
      name: 'roundImage'
    }
  ]
});

CLASS({
  package: 'com.google.watlobby',
  name: 'Bubble',

  extendsModel: 'foam.demos.physics.PhysicalCircle',

  requires: [ 'foam.graphics.ImageCView' ],

  imports: [ 'lobby' ],

  properties: [
    {
      name: 'topic'
    },
    {
      name: 'image'
    },
    {
      name: 'roundImage'
    }
  ],

  methods: [
    function initCView() {
      this.SUPER();

      if ( this.image ) {
        var img = this.ImageCView.create({src: this.image});
        this.addChild(img);
        this.img = img;
      }
    },
    function setSelected(selected) {
      if ( this.cancel_ ) {
        this.cancel_();
        this.cancel_ = null;
      }
      if ( selected ) {
        this.oldMass_ = this.mass;
        this.oldR_ = this.r;

        this.mass = this.INFINITE_MASS;
        this.vx = this.vy = 0;
        this.cancel_ = Movement.animate(2000, function() {
          var width = this.lobby.width;
          var height = this.lobby.height;
          this.r = Math.min(width, height)/2.4;
          this.x = width/2;
          this.y = height/2;
        }.bind(this), null, function() { /*self.collider.stop(); self.timer.stop();*/ })();
      } else {
        this.mass = this.oldMass_;
        this.cancel_ = Movement.animate(1000, function() {
          this.r = this.oldR_;
        }.bind(this), null, function() { /*self.collider.stop(); self.timer.stop();*/ })();
      }
    },
    function paintSelf() {
      if ( this.image ) {
        var d, s;
        if ( this.roundImage ) {
          this.borderWidth = 0;
          d = 2 * this.r;
          s = -this.r;
        } else {
          d = 2 * this.r * Math.SQRT1_2;
          s = -this.r * Math.SQRT1_2;
        }
        this.img.x = this.img.y = s;
        this.img.width = this.img.height = d;
      }
      this.SUPER();
    }
  ]
});

CLASS({
  package: 'com.google.watlobby',
  name: 'Lobby',
  extendsModel: 'foam.graphics.CView',

  requires: [
    'com.google.watlobby.Bubble',
    'foam.demos.physics.PhysicalCircle',
    'foam.physics.Collider',
    'foam.demos.ClockView',
    'foam.util.Timer'
  ],

  imports: [ 'timer' ],
  exports: [ 'as lobby' ],

  constants: {
    COLOURS: ['#33f','#f00','#fc0', '#3c0']
  },

  properties: [
    { name: 'timer' },
    { name: 'n',          defaultValue: 30 },
    { name: 'width',      defaultValue: window.innerWidth },
    { name: 'height',     defaultValue: window.innerHeight },
    { name: 'background', defaultValue: '#ccf' },
    { name: 'collider',   factory: function() {
      return this.Collider.create();
    }},
    {
      name: 'topics',   factory: function() {
      return JSONUtil.arrayToObjArray(this.X, [
        { topic: 'chrome',       image: 'chrome.png',       r: 180, roundImage: true, colour: 'red' },
        { topic: 'flip',         image: 'flip.jpg',         r: 100, colour: 'red' },
        { topic: 'googlecanada', image: 'googlecanada.gif', r: 200 },
        { topic: 'inbox',        image: 'inbox.png',        r: 160 },
        { topic: 'gmailoffline', image: 'gmailoffline.jpg', r: 160 },
        { topic: 'fiber',        image: 'fiber.jpg',        r: 180 },
        { topic: 'foam',         image: 'foampowered.png',  r: 100 },
        { topic: 'inwatvideo',   image: 'inwatvideo.png', roundImage: true, r: 100 },
        // chromebook, mine sweeper, calculator, I'm feeling lucky
        // thtps://www.youtube.com/watch?v=1Bb29KxXzDs, https://youtu.be/1Bb29KxXzDs

      ], this.Topic);
    }}
  ],

  listeners: [
    {
      name: 'onClick',
      code: function(evt) {
        var self = this;
        // console.log('********************* onClick', evt);
        var child = this.collider.findChildAt(evt.clientX, evt.clientY);
        if ( child === this.selected ) return;

        if ( this.selected ) {
          this.selected.setSelected(false);
          this.selected = null;
        }

        if ( child && child.setSelected ) {
          this.selected = child
          child.setSelected(true);
        }
      }
    }
  ],

  methods: [
    function initCView() {
      this.SUPER();

      if ( ! this.timer ) {
        this.timer = this.Timer.create();
        this.timer.start();
      }

      var N = this.n;
      for ( var i = 0 ; i < N ; i++ ) {
        var colour = this.COLOURS[i % this.COLOURS.length];
        var c = this.Bubble.create({
          r: 20 + Math.random() * 50,
          x: Math.random() * this.width,
          y: Math.random() * this.height,
          borderWidth: 6,
          color: 'white',
          border: colour
        });
        if ( i < this.topics.length ) {
          var t = this.topics[i];
          c.topic = t;
          c.image = t.image;
          c.r = t.r;
          c.roundImage = t.roundImage;
          if ( t.colour ) c.border = t.colour;
        }
        this.addChild(c);

        c.mass = c.r/50;
        Movement.gravity(c, 0.03);
        Movement.inertia(c);
        Movement.friction(c, 0.96);
        this.bounceOnWalls(c, this.width, this.height);
        this.collider.add(c);
      }

      for ( var i = 0 ; i < 200 ; i++ ) {
        var b = this.PhysicalCircle.create({
          r: 5,
          x: Math.random() * this.width,
          y: Math.random() * this.height,
          borderWidth: 0.5,
          color: 'rgba(0,0,255,0.2)',
          border: '#blue',
//          color: 'rgba(100,100,200,0.2)',
//          border: '#55a',
          mass: 0.6
        });

        b.y$.addListener(function(b) {
          if ( b.y < 1 ) {
            b.y = this.height;
            b.x = this.width * Math.random();
          }
        }.bind(this, b));

        b.vy = -4;
        Movement.inertia(b);
        Movement.gravity(b, -0.2);
        Movement.friction(b);
        this.collider.add(b);

        this.addChild(b);

//        this.view.$.addEventListener('click', this.onClick);
       document.body.addEventListener('click', this.onClick);

      }

      var clock = this.ClockView.create({x:this.width-70,y:70, r:60});
      this.addChild(clock);

      this.collider.start();
    },

    function bounceOnWalls(c, w, h) {
      Events.dynamic(function() { c.x; c.y; }, function() {
        var r = c.r + c.borderWidth;
        if ( c.x < r     ) { c.vx += 0.2; c.vy -= 0.19; }
        if ( c.x > w - r ) { c.vx -= 0.2; c.vy += 0.19; }
        if ( c.y < r     ) { c.vy += 0.2; c.vx += 0.19; }
        if ( c.y > h - r ) { c.vy -= 0.2; c.vx -= 0.19; }
      });
    },

    function destroy() {
      this.SUPER();
      this.collider.destroy();
    }
  ]
});
<commit_msg>Lobby.<commit_after>/**
 * @license
 * Copyright 2015 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.
 */

CLASS({
  package: 'com.google.watlobby',
  name: 'Topic',

  properties: [
    {
      name: 'topic'
    },
    {
      name: 'image'
    },
    {
      name: 'colour'
    },
    {
      name: 'r'
    },
    {
      name: 'roundImage'
    }
  ]
});

CLASS({
  package: 'com.google.watlobby',
  name: 'Bubble',

  extendsModel: 'foam.demos.physics.PhysicalCircle',

  requires: [ 'foam.graphics.ImageCView' ],

  imports: [ 'lobby' ],

  properties: [
    {
      name: 'topic'
    },
    {
      name: 'image'
    },
    {
      name: 'roundImage'
    }
  ],

  methods: [
    function initCView() {
      this.SUPER();

      if ( this.image ) {
        var img = this.ImageCView.create({src: this.image});
        this.addChild(img);
        this.img = img;
      }
    },
    function setSelected(selected) {
      if ( this.cancel_ ) {
        this.cancel_();
        this.cancel_ = null;
      }
      if ( selected ) {
        this.oldMass_ = this.oldMass_ || this.mass;
        this.oldR_ = this.oldR_ || this.r;

        this.mass = this.INFINITE_MASS;
        this.vx = this.vy = 0;
        this.cancel_ = Movement.animate(2000, function() {
          var width = this.lobby.width;
          var height = this.lobby.height;
          this.r = Math.min(width, height)/2.4;
          this.x = width/2;
          this.y = height/2;
        }.bind(this), null, function() { /*self.collider.stop(); self.timer.stop();*/ })();
      } else {
        this.mass = this.oldMass_;
        this.cancel_ = Movement.animate(1000, function() {
          this.r = this.oldR_;
        }.bind(this), null, function() { /*self.collider.stop(); self.timer.stop();*/ })();
      }
    },
    function paintSelf() {
      if ( this.image ) {
        var d, s;
        if ( this.roundImage ) {
          this.borderWidth = 0;
          d = 2 * this.r;
          s = -this.r;
        } else {
          d = 2 * this.r * Math.SQRT1_2;
          s = -this.r * Math.SQRT1_2;
        }
        this.img.x = this.img.y = s;
        this.img.width = this.img.height = d;
      }
      this.SUPER();
    }
  ]
});

CLASS({
  package: 'com.google.watlobby',
  name: 'Lobby',
  extendsModel: 'foam.graphics.CView',

  requires: [
    'com.google.watlobby.Bubble',
    'foam.demos.physics.PhysicalCircle',
    'foam.physics.Collider',
    'foam.demos.ClockView',
    'foam.util.Timer'
  ],

  imports: [ 'timer' ],
  exports: [ 'as lobby' ],

  constants: {
    COLOURS: ['#33f','#f00','#fc0', '#3c0']
  },

  properties: [
    { name: 'timer' },
    { name: 'n',          defaultValue: 30 },
    { name: 'width',      defaultValue: window.innerWidth },
    { name: 'height',     defaultValue: window.innerHeight },
    { name: 'background', defaultValue: '#ccf' },
    { name: 'collider',   factory: function() {
      return this.Collider.create();
    }},
    {
      name: 'topics',   factory: function() {
      return JSONUtil.arrayToObjArray(this.X, [
        { topic: 'chrome',       image: 'chrome.png',       r: 180, roundImage: true, colour: 'red' },
        { topic: 'flip',         image: 'flip.jpg',         r: 100, colour: 'red' },
        { topic: 'googlecanada', image: 'googlecanada.gif', r: 200 },
        { topic: 'inbox',        image: 'inbox.png',        r: 160 },
        { topic: 'gmailoffline', image: 'gmailoffline.jpg', r: 160 },
        { topic: 'fiber',        image: 'fiber.jpg',        r: 180 },
        { topic: 'foam',         image: 'foampowered.png',  r: 100 },
        { topic: 'inwatvideo',   image: 'inwatvideo.png', roundImage: true, r: 100 },
        // chromebook, mine sweeper, calculator, I'm feeling lucky
        // thtps://www.youtube.com/watch?v=1Bb29KxXzDs, https://youtu.be/1Bb29KxXzDs

      ], this.Topic);
    }}
  ],

  listeners: [
    {
      name: 'onClick',
      code: function(evt) {
        var self = this;
        // console.log('********************* onClick', evt);
        var child = this.collider.findChildAt(evt.clientX, evt.clientY);
        if ( child === this.selected ) return;

        if ( this.selected ) {
          this.selected.setSelected(false);
          this.selected = null;
        }

        if ( child && child.setSelected ) {
          this.selected = child
          child.setSelected(true);
        }
      }
    }
  ],

  methods: [
    function initCView() {
      this.SUPER();

      if ( ! this.timer ) {
        this.timer = this.Timer.create();
        this.timer.start();
      }

      var N = this.n;
      for ( var i = 0 ; i < N ; i++ ) {
        var colour = this.COLOURS[i % this.COLOURS.length];
        var c = this.Bubble.create({
          r: 20 + Math.random() * 50,
          x: Math.random() * this.width,
          y: Math.random() * this.height,
          borderWidth: 6,
          color: 'white',
          border: colour
        });
        if ( i < this.topics.length ) {
          var t = this.topics[i];
          c.topic = t;
          c.image = t.image;
          c.r = t.r;
          c.roundImage = t.roundImage;
          if ( t.colour ) c.border = t.colour;
        }
        this.addChild(c);

        c.mass = c.r/50;
        Movement.gravity(c, 0.03);
        Movement.inertia(c);
        Movement.friction(c, 0.96);
        this.bounceOnWalls(c, this.width, this.height);
        this.collider.add(c);
      }

      for ( var i = 0 ; i < 200 ; i++ ) {
        var b = this.PhysicalCircle.create({
          r: 5,
          x: Math.random() * this.width,
          y: Math.random() * this.height,
          borderWidth: 0.5,
          color: 'rgba(0,0,255,0.2)',
          border: '#blue',
//          color: 'rgba(100,100,200,0.2)',
//          border: '#55a',
          mass: 0.6
        });

        b.y$.addListener(function(b) {
          if ( b.y < 1 ) {
            b.y = this.height;
            b.x = this.width * Math.random();
          }
        }.bind(this, b));

        b.vy = -4;
        Movement.inertia(b);
        Movement.gravity(b, -0.2);
        Movement.friction(b);
        this.collider.add(b);

        this.addChild(b);

//        this.view.$.addEventListener('click', this.onClick);
       document.body.addEventListener('click', this.onClick);

      }

      var clock = this.ClockView.create({x:this.width-70,y:70, r:60});
      this.addChild(clock);

      this.collider.start();
    },

    function bounceOnWalls(c, w, h) {
      Events.dynamic(function() { c.x; c.y; }, function() {
        var r = c.r + c.borderWidth;
        if ( c.x < r     ) { c.vx += 0.2; c.vy -= 0.19; }
        if ( c.x > w - r ) { c.vx -= 0.2; c.vy += 0.19; }
        if ( c.y < r     ) { c.vy += 0.2; c.vx += 0.19; }
        if ( c.y > h - r ) { c.vy -= 0.2; c.vx -= 0.19; }
      });
    },

    function destroy() {
      this.SUPER();
      this.collider.destroy();
    }
  ]
});
<|endoftext|>


<commit_before>angular.module('ionic.service.core', [])

/**
 * @private
 * Provides a safe interface to store objects in persistent memory
 */
.provider('persistentStorage', function() {
  return {
    $get: ['$q', '$window', function($q, $window) {
      var objectCache = {};
      var memoryLocks = {};

      var persistenceStrategy = {
        get: function(key) {
          return $window.localStorage.getItem(key);
        },
        remove: function(key) {
          return $window.localStorage.removeItem(key);
        },
        set: function(key, value) {
          return $window.localStorage.setItem(key, value);
        }
      };

      return {
        /**
         * Stores an object in local storage under the given key
        */
        storeObject: function(key, object) {

          // Convert object to JSON and store in localStorage
          var json = JSON.stringify(object);
          persistenceStrategy.set(key, json);

          // Then store it in the object cache
          objectCache[key] = object;
        },

        /**
         * Either retrieves the cached copy of an object,
         * or the object itself from localStorage.
         * Returns null if the object couldn't be found.
        */
        retrieveObject: function(key) {

          // First check to see if it's the object cache
          var cached = objectCache[key];
          if (cached) {
            return cached;
          }

          // Deserialize the object from JSON
          var json = persistenceStrategy.get(key);

          // null or undefined --> return null.
          if (json == null) {
            return null;
          }

          try {
            return JSON.parse(json);
          } catch (err) {
            return null;
          }
        },

        /**
         * Locks the async call represented by the given promise and lock key.
         * Only one asyncFunction given by the lockKey can be running at any time.
         *
         * @param lockKey should be a string representing the name of this async call.
         *        This is required for persistence.
         * @param asyncFunction Returns a promise of the async call.
         * @returns A new promise, identical to the one returned by asyncFunction,
         *          but with two new errors: 'in_progress', and 'last_call_interrupted'.
        */
        lockedAsyncCall: function(lockKey, asyncFunction) {

          var deferred = $q.defer();

          // If the memory lock is set, error out.
          if (memoryLocks[lockKey]) {
            deferred.reject('in_progress');
            return deferred.promise;
          }

          // If there is a stored lock but no memory lock, flag a persistence error
          if (persistenceStrategy.get(lockKey) === 'locked') {
            deferred.reject('last_call_interrupted');
            deferred.promise.then(null, function() {
              persistenceStrategy.remove(lockKey);
            });
            return deferred.promise;
          }

          // Set stored and memory locks
          memoryLocks[lockKey] = true;
          persistenceStrategy.set(lockKey, 'locked');

          // Perform the async operation
          asyncFunction().then(function(successData) {
            deferred.resolve(successData);

            // Remove stored and memory locks
            delete memoryLocks[lockKey];
            persistenceStrategy.remove(lockKey);
          }, function(errorData) {
            deferred.reject(errorData);

            // Remove stored and memory locks
            delete memoryLocks[lockKey];
            persistenceStrategy.remove(lockKey);
          }, function(notifyData) {
            deferred.notify(notifyData);
          });

          return deferred.promise;
        }
      };
    }]
  };
})

/**
 * A core Ionic account identity provider.
 *
 * Usage:
 * angular.module('myApp', ['ionic', 'ionic.service.core'])
 * .config(['$ionicAppProvider', function($ionicAccountProvider) {
 *   $ionicAppProvider.identify({
 *     app_id: 'x34dfxjydi23dx'
 *   });
 * }]);
 */
.provider('$ionicApp', function($httpProvider) {
  var app = {};

  var settings = {
    'api_server': 'https://apps.ionic.io',
    'push_api_server': 'https://push.ionic.io'
  };

  this.identify = function(opts) {
  if (!opts.gcm_id){
    opts.gcm_id = 'None';
  }
    app = opts;
  };

  /**
   * Set a config property.
   */
  this.set = function(k, v) {
    settings[k] = v;
  };

  this.setApiServer = function(server) {
    settings.api_server = server;
  };

  this.$get = [function() {
    return {
      getId: function() {
        return app.app_id;
      },
      getGcmId: function(){
        return app.gcm_id;
      },
      getValue: function(k) {
        return settings[k];
      },
      getApiWriteKey: function() {
        return app.api_write_key;
      },
      getApiReadKey: function() {
        return app.api_read_key;
      },
      getApiUrl: function() {
        return this.getValue('api_server');
      },
      getApiKey: function() {
        return app.api_key;
      },
      getApiEndpoint: function(service) {
        var app = this.getApp();
        if(!app) return null;

        return this.getApiUrl() + '/api/v1/' + app.app_id + '/' + service;
      },

      /**
       * Get the registered app for all commands.
       */
      getApp: function() {
        return app;
      }
    }
  }];
})

/**
* @ngdoc service
* @name $ionicUser
* @module ionic.service.core
* @description
*
* An interface for storing data to a user object which will be sent with many ionic services
*
* Add tracking data to the user by passing objects in to the identify function.
* The _id property identifies the user on this device and cannot be overwritten.
*
* @usage
* ```javascript
* $ionicUser.get();
*
* // Add info to user object
* $ionicUser.identify({
*   username: "Timmy"
* });
*
* ```
*/
.factory('$ionicUser', [
  '$q',
  '$timeout',
  '$http',
  'persistentStorage',
  '$ionicApp',
function($q, $timeout, $http, persistentStorage, $ionicApp) {
      // User object we'll use to store all our user info

  var storageKeyName = 'ionic_analytics_user_' + $ionicApp.getApp().app_id,
      user = persistentStorage.retrieveObject(storageKeyName) || {},
      deviceCordova = ionic.Platform.device(),
      device = {
          screen_width: window.innerWidth * (window.devicePixelRatio || 1),
          screen_height: window.innerHeight * (window.devicePixelRatio || 1)
      };

  if (deviceCordova.model) device.model = deviceCordova.model;
  if (deviceCordova.platform) device.platform = deviceCordova.platform;
  if (deviceCordova.version) device.version = deviceCordova.version;
  if (deviceCordova.uuid) device.uuid = deviceCordova.uuid;

  // Flag if we've changed anything on our user
  var dirty = false;
  dirty = storeOrDirty('is_on_device', ionic.Platform.isWebView());
  dirty = storeOrDirty('device', device);
  if (!user._id) {
    user._id = generateGuid();
    dirty = true;
  }

  if (dirty) {
      persistentStorage.storeObject(storageKeyName, user);
  }

  function generateGuid() {
    // Some crazy bit-twiddling to generate a random guid
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
      var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
      return v.toString(16);
    });
  }

  function storeOrDirty(key, value) {
    // Store the key on the user object and return whether something changed
    if (!angular.equals(user[key], value)) {
      user[key] = value;
      return true;
    }
    return false;
  }

  return {
    /**
     * Push a value to the array with the given key.
     * @param key the key
     * @param value the value
     * @param isUnique whether to only push if it doesn't exist in the set
     *
     */

    _op: function(key, value, type) {
      var u = user.user_id;
      if(!u) {
        throw new Error("Please call identify with a user_id before calling push");
      }
      var o = {};
      o['user_id'] = u;
      o[key] = value;

      return $http.post($ionicApp.getApiUrl() + '/api/v1/app/' + $ionicApp.getId() + '/users/' + type, o);
    },
    /**
     * Push the given value into the array field identified by the key.
     * Pass true to isUnique to only push the value if the value does not
     * already exist in the array.
     */
    push: function(key, value, isUnique) {
      if(isUnique) {
        return this._op(key, value, 'pushUnique');
      } else {
        return this._op(key, value, 'push');
      }
    },
    /**
     * Pull a given value out of the array identified by key.
     */
    pull: function(key, value) {
      return this._op(key, value, 'pull');
    },
    /**
     * Set the given value under the key in the user. This overwrites
     * any other data under that field. To append data to list, use push above.
     */
    set: function(key, value) {
      return this._op(key, value, 'set');
    },
    /**
     * Remove the field for the given key.
     */
    unset: function(key) {
      return this._op(key, '', 'unset');
    },
    generateGUID: function() {
      return generateGuid();
    },
    identify: function(userData) {
      if (!userData.user_id) {
        var msg = 'You must supply a unique user_id field.';
        throw new Error(msg)
      }

      // Copy all the data into our user object
      angular.extend(user, userData);

      // Write the user object to our local storage
      persistentStorage.storeObject(storageKeyName, user);

      return $http.post($ionicApp.getApiUrl() + '/api/v1/app/' + $ionicApp.getId() + '/users/identify', userData);
    },
    get: function() {
      return user;
    }
  }
}])
<commit_msg>remove API endpoints that we won't use anymore<commit_after>angular.module('ionic.service.core', [])

/**
 * @private
 * Provides a safe interface to store objects in persistent memory
 */
.provider('persistentStorage', function() {
  return {
    $get: ['$q', '$window', function($q, $window) {
      var objectCache = {};
      var memoryLocks = {};

      var persistenceStrategy = {
        get: function(key) {
          return $window.localStorage.getItem(key);
        },
        remove: function(key) {
          return $window.localStorage.removeItem(key);
        },
        set: function(key, value) {
          return $window.localStorage.setItem(key, value);
        }
      };

      return {
        /**
         * Stores an object in local storage under the given key
        */
        storeObject: function(key, object) {

          // Convert object to JSON and store in localStorage
          var json = JSON.stringify(object);
          persistenceStrategy.set(key, json);

          // Then store it in the object cache
          objectCache[key] = object;
        },

        /**
         * Either retrieves the cached copy of an object,
         * or the object itself from localStorage.
         * Returns null if the object couldn't be found.
        */
        retrieveObject: function(key) {

          // First check to see if it's the object cache
          var cached = objectCache[key];
          if (cached) {
            return cached;
          }

          // Deserialize the object from JSON
          var json = persistenceStrategy.get(key);

          // null or undefined --> return null.
          if (json == null) {
            return null;
          }

          try {
            return JSON.parse(json);
          } catch (err) {
            return null;
          }
        },

        /**
         * Locks the async call represented by the given promise and lock key.
         * Only one asyncFunction given by the lockKey can be running at any time.
         *
         * @param lockKey should be a string representing the name of this async call.
         *        This is required for persistence.
         * @param asyncFunction Returns a promise of the async call.
         * @returns A new promise, identical to the one returned by asyncFunction,
         *          but with two new errors: 'in_progress', and 'last_call_interrupted'.
        */
        lockedAsyncCall: function(lockKey, asyncFunction) {

          var deferred = $q.defer();

          // If the memory lock is set, error out.
          if (memoryLocks[lockKey]) {
            deferred.reject('in_progress');
            return deferred.promise;
          }

          // If there is a stored lock but no memory lock, flag a persistence error
          if (persistenceStrategy.get(lockKey) === 'locked') {
            deferred.reject('last_call_interrupted');
            deferred.promise.then(null, function() {
              persistenceStrategy.remove(lockKey);
            });
            return deferred.promise;
          }

          // Set stored and memory locks
          memoryLocks[lockKey] = true;
          persistenceStrategy.set(lockKey, 'locked');

          // Perform the async operation
          asyncFunction().then(function(successData) {
            deferred.resolve(successData);

            // Remove stored and memory locks
            delete memoryLocks[lockKey];
            persistenceStrategy.remove(lockKey);
          }, function(errorData) {
            deferred.reject(errorData);

            // Remove stored and memory locks
            delete memoryLocks[lockKey];
            persistenceStrategy.remove(lockKey);
          }, function(notifyData) {
            deferred.notify(notifyData);
          });

          return deferred.promise;
        }
      };
    }]
  };
})

/**
 * A core Ionic account identity provider.
 *
 * Usage:
 * angular.module('myApp', ['ionic', 'ionic.service.core'])
 * .config(['$ionicAppProvider', function($ionicAccountProvider) {
 *   $ionicAppProvider.identify({
 *     app_id: 'x34dfxjydi23dx'
 *   });
 * }]);
 */
.provider('$ionicApp', function($httpProvider) {
  var app = {};

  var settings = {
    'api_server': 'https://apps.ionic.io',
    'push_api_server': 'https://push.ionic.io'
  };

  this.identify = function(opts) {
  if (!opts.gcm_id){
    opts.gcm_id = 'None';
  }
    app = opts;
  };

  /**
   * Set a config property.
   */
  this.set = function(k, v) {
    settings[k] = v;
  };

  this.setApiServer = function(server) {
    settings.api_server = server;
  };

  this.$get = [function() {
    return {
      getId: function() {
        return app.app_id;
      },
      getGcmId: function(){
        return app.gcm_id;
      },
      getValue: function(k) {
        return settings[k];
      },
      getApiUrl: function() {
        return this.getValue('api_server');
      },
      getApiKey: function() {
        return app.api_key;
      },
      getApiEndpoint: function(service) {
        var app = this.getApp();
        if(!app) return null;

        return this.getApiUrl() + '/api/v1/' + app.app_id + '/' + service;
      },

      /**
       * Get the registered app for all commands.
       */
      getApp: function() {
        return app;
      }
    }
  }];
})

/**
* @ngdoc service
* @name $ionicUser
* @module ionic.service.core
* @description
*
* An interface for storing data to a user object which will be sent with many ionic services
*
* Add tracking data to the user by passing objects in to the identify function.
* The _id property identifies the user on this device and cannot be overwritten.
*
* @usage
* ```javascript
* $ionicUser.get();
*
* // Add info to user object
* $ionicUser.identify({
*   username: "Timmy"
* });
*
* ```
*/
.factory('$ionicUser', [
  '$q',
  '$timeout',
  '$http',
  'persistentStorage',
  '$ionicApp',
function($q, $timeout, $http, persistentStorage, $ionicApp) {
      // User object we'll use to store all our user info

  var storageKeyName = 'ionic_analytics_user_' + $ionicApp.getApp().app_id,
      user = persistentStorage.retrieveObject(storageKeyName) || {},
      deviceCordova = ionic.Platform.device(),
      device = {
          screen_width: window.innerWidth * (window.devicePixelRatio || 1),
          screen_height: window.innerHeight * (window.devicePixelRatio || 1)
      };

  if (deviceCordova.model) device.model = deviceCordova.model;
  if (deviceCordova.platform) device.platform = deviceCordova.platform;
  if (deviceCordova.version) device.version = deviceCordova.version;
  if (deviceCordova.uuid) device.uuid = deviceCordova.uuid;

  // Flag if we've changed anything on our user
  var dirty = false;
  dirty = storeOrDirty('is_on_device', ionic.Platform.isWebView());
  dirty = storeOrDirty('device', device);
  if (!user._id) {
    user._id = generateGuid();
    dirty = true;
  }

  if (dirty) {
      persistentStorage.storeObject(storageKeyName, user);
  }

  function generateGuid() {
    // Some crazy bit-twiddling to generate a random guid
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
      var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
      return v.toString(16);
    });
  }

  function storeOrDirty(key, value) {
    // Store the key on the user object and return whether something changed
    if (!angular.equals(user[key], value)) {
      user[key] = value;
      return true;
    }
    return false;
  }

  return {
    /**
     * Push a value to the array with the given key.
     * @param key the key
     * @param value the value
     * @param isUnique whether to only push if it doesn't exist in the set
     *
     */

    _op: function(key, value, type) {
      var u = user.user_id;
      if(!u) {
        throw new Error("Please call identify with a user_id before calling push");
      }
      var o = {};
      o['user_id'] = u;
      o[key] = value;

      return $http.post($ionicApp.getApiUrl() + '/api/v1/app/' + $ionicApp.getId() + '/users/' + type, o);
    },
    /**
     * Push the given value into the array field identified by the key.
     * Pass true to isUnique to only push the value if the value does not
     * already exist in the array.
     */
    push: function(key, value, isUnique) {
      if(isUnique) {
        return this._op(key, value, 'pushUnique');
      } else {
        return this._op(key, value, 'push');
      }
    },
    /**
     * Pull a given value out of the array identified by key.
     */
    pull: function(key, value) {
      return this._op(key, value, 'pull');
    },
    /**
     * Set the given value under the key in the user. This overwrites
     * any other data under that field. To append data to list, use push above.
     */
    set: function(key, value) {
      return this._op(key, value, 'set');
    },
    /**
     * Remove the field for the given key.
     */
    unset: function(key) {
      return this._op(key, '', 'unset');
    },
    generateGUID: function() {
      return generateGuid();
    },
    identify: function(userData) {
      if (!userData.user_id) {
        var msg = 'You must supply a unique user_id field.';
        throw new Error(msg)
      }

      // Copy all the data into our user object
      angular.extend(user, userData);

      // Write the user object to our local storage
      persistentStorage.storeObject(storageKeyName, user);

      return $http.post($ionicApp.getApiUrl() + '/api/v1/app/' + $ionicApp.getId() + '/users/identify', userData);
    },
    get: function() {
      return user;
    }
  }
}])
<|endoftext|>


<commit_before>/**
 * Developer: Grigory Kuznetsov
 * Date: 23.07.2015
 * Copyright: 2009-2016 Comindware®
 *       All Rights Reserved
 * Published under the MIT license
 */

"use strict";

import { objectPropertyTypes } from '../Meta';
import { helpers } from '../utils/utilsApi';
import { Handlebars } from '../libApi';

let factory = {
    getCellViewByDataType: function(type) {
        var result;

        switch (type) {
        case objectPropertyTypes.STRING:
            result = factory.getTextCellView();
            break;
        case objectPropertyTypes.INSTANCE:
            result = factory.getReferenceCellView();
            break;
        case objectPropertyTypes.ACCOUNT:
            result = factory.getUserCellView();
            break;
        case objectPropertyTypes.ENUM:
            result = factory.getEnumCellView();
            break;
        case objectPropertyTypes.INTEGER:
        case objectPropertyTypes.DOUBLE:
        case objectPropertyTypes.DECIMAL:
            result = factory.getNumberCellView();
            break;
        case objectPropertyTypes.DURATION:
            result = factory.getDurationCellView();
            break;
        case objectPropertyTypes.BOOLEAN:
            result = factory.getBooleanCellView();
            break;
        case objectPropertyTypes.DATETIME:
            result = factory.getDateTimeCellView();
            break;
        case objectPropertyTypes.DOCUMENT:
            result = factory.getDocumentCellView();
            break;
        default:
            helpers.throwNotSupportedError('Data type' + type + 'is not supported');
            break;
        }

        return result;
    },

    getTextCellView: function () {
        return factory.__getSimpleView('{{{highlightFragment value highlightedFragment}}}');
    },

    getReferenceCellView: function () {
        return factory.__getSimpleView('{{#if value}}{{#if value.name}}{{{highlightFragment value.name highlightedFragment}}}{{/if}}{{/if}}');
    },

    getUserCellView: function () {
        return factory.__getAccountView();
    },

    getEnumCellView: function () {
        return factory.__getEnumView();
    },

    getNumberCellView: function () {
        return factory.__getSimpleView('{{value}}');
    },

    getDurationCellView: function () {
        return factory.__getSimpleView('{{renderShortDuration value}}');
    },

    getBooleanCellView: function () {
        return factory.__getSimpleView(
            "{{#if value}}{{localize 'CORE.GRID.CELLVIEWFACTORY.BOOLEANCELLVIEW.YES'}}{{/if}}" +
            "{{#unless value}}{{localize 'CORE.GRID.CELLVIEWFACTORY.BOOLEANCELLVIEW.NO'}}{{/unless}}");
    },

    getDateTimeCellView: function () {
        return factory.__getSimpleView('{{#if value}}{{renderFullDate value}}{{/if}}');
    },

    getDocumentCellView: function () {
        return factory.__getDocumentView();
    },

    __getSimpleView: function (template, templateHelpers) {
        return Marionette.ItemView.extend({
            template: Handlebars.compile(template),
            modelEvents: {
                'change:highlightedFragment': '__handleHighlightedFragmentChange'
            },
            __handleHighlightedFragmentChange: function () {
                this.render();
            },
            className: 'grid-cell',
            templateHelpers: templateHelpers
        });
    },

    __getAccountView: function () {
        return Marionette.ItemView.extend({
            template: Handlebars.compile('{{text}}'),
            templateHelpers: function () {
                var value = this.model.get('value');
                var text = '';
                if (value && value.length > 0) {
                    text = _.chain(value).
                        map(function (item) {
                            return {
                                id: item.id,
                                text: item.columns[0]
                            };
                        }).
                        sortBy(function (member) {
                            return member.text;
                        }).
                        reduce(function (memo, member) {
                            if (memo) {
                                return memo + ', ' + member.text;
                            } else {
                                return member.text;
                            }
                        }, null).
                        value();
                }
                else if (value && value.name) {
                    text = value.name;
                }

                return {
                    text: text
                };
            },
            modelEvents: {
                'change:highlightedFragment': '__handleHighlightedFragmentChange'
            },
            __handleHighlightedFragmentChange: function () {
                this.render();
            },
            className: 'grid-cell'
        });
    },

    __getDocumentView: function () {
        return Marionette.ItemView.extend({
            template: Handlebars.compile('{{#each documents}}{{text}}{{#unless @last}}, {{/unless}}{{/each}}'),

            templateHelpers: function () {
                var value = this.model.get('value');
                var documents = [];
                if (value && value.length > 0) {
                    documents = _.chain(value).
                        map(function (item) {
                            return {
                                id: item.id,
                                text: item.columns[0],
                                url: item.columns[1]
                            };
                        }).
                        sortBy(function (document) {
                            return document.text;
                        }).
                        value();
                }

                return {
                    documents: documents
                };
            },
            modelEvents: {
                'change:highlightedFragment': '__handleHighlightedFragmentChange'
            },
            __handleHighlightedFragmentChange: function () {
                this.render();
            },
            className: 'grid-cell'
        });
    },

    __getEnumView: function() {
        return Marionette.ItemView.extend({
            template: Handlebars.compile("{{#if value}}{{valueExplained}}{{/if}}"),
            modelEvents: {
                'change:highlightedFragment': '__handleHighlightedFragmentChange'
            },
            __handleHighlightedFragmentChange: function () {
                this.render();
            },
            templateHelpers: function () {
                var value = this.model.get("value");
                return {
                    value: value,
                    valueExplained: value.valueExplained
                };
            },
            className: 'grid-cell'
        });
    }
};
export default factory;
<commit_msg>Cell view templates fix<commit_after>/**
 * Developer: Grigory Kuznetsov
 * Date: 23.07.2015
 * Copyright: 2009-2016 Comindware®
 *       All Rights Reserved
 * Published under the MIT license
 */

"use strict";

import { objectPropertyTypes } from '../Meta';
import { helpers } from '../utils/utilsApi';
import { Handlebars } from '../libApi';

let factory = {
    getCellViewByDataType: function(type) {
        var result;

        switch (type) {
        case objectPropertyTypes.STRING:
            result = factory.getTextCellView();
            break;
        case objectPropertyTypes.INSTANCE:
            result = factory.getReferenceCellView();
            break;
        case objectPropertyTypes.ACCOUNT:
            result = factory.getUserCellView();
            break;
        case objectPropertyTypes.ENUM:
            result = factory.getEnumCellView();
            break;
        case objectPropertyTypes.INTEGER:
        case objectPropertyTypes.DOUBLE:
        case objectPropertyTypes.DECIMAL:
            result = factory.getNumberCellView();
            break;
        case objectPropertyTypes.DURATION:
            result = factory.getDurationCellView();
            break;
        case objectPropertyTypes.BOOLEAN:
            result = factory.getBooleanCellView();
            break;
        case objectPropertyTypes.DATETIME:
            result = factory.getDateTimeCellView();
            break;
        case objectPropertyTypes.DOCUMENT:
            result = factory.getDocumentCellView();
            break;
        default:
            helpers.throwNotSupportedError('Data type' + type + 'is not supported');
            break;
        }

        return result;
    },

    getTextCellView: function () {
        return factory.__getSimpleView('{{highlightFragment value highlightedFragment}}');
    },

    getReferenceCellView: function () {
        return factory.__getSimpleView('{{#if value}}{{#if value.name}}{{highlightFragment value.name highlightedFragment}}{{/if}}{{/if}}');
    },

    getUserCellView: function () {
        return factory.__getAccountView();
    },

    getEnumCellView: function () {
        return factory.__getEnumView();
    },

    getNumberCellView: function () {
        return factory.__getSimpleView('{{value}}');
    },

    getDurationCellView: function () {
        return factory.__getSimpleView('{{renderShortDuration value}}');
    },

    getBooleanCellView: function () {
        return factory.__getSimpleView(
            "{{#if value}}{{localize 'CORE.GRID.CELLVIEWFACTORY.BOOLEANCELLVIEW.YES'}}{{/if}}" +
            "{{#unless value}}{{localize 'CORE.GRID.CELLVIEWFACTORY.BOOLEANCELLVIEW.NO'}}{{/unless}}");
    },

    getDateTimeCellView: function () {
        return factory.__getSimpleView('{{#if value}}{{renderFullDate value}}{{/if}}');
    },

    getDocumentCellView: function () {
        return factory.__getDocumentView();
    },

    __getSimpleView: function (template, templateHelpers) {
        return Marionette.ItemView.extend({
            template: Handlebars.compile(template),
            modelEvents: {
                'change:highlightedFragment': '__handleHighlightedFragmentChange'
            },
            __handleHighlightedFragmentChange: function () {
                this.render();
            },
            className: 'grid-cell',
            templateHelpers: templateHelpers
        });
    },

    __getAccountView: function () {
        return Marionette.ItemView.extend({
            template: Handlebars.compile('{{text}}'),
            templateHelpers: function () {
                var value = this.model.get('value');
                var text = '';
                if (value && value.length > 0) {
                    text = _.chain(value).
                        map(function (item) {
                            return {
                                id: item.id,
                                text: item.columns[0]
                            };
                        }).
                        sortBy(function (member) {
                            return member.text;
                        }).
                        reduce(function (memo, member) {
                            if (memo) {
                                return memo + ', ' + member.text;
                            } else {
                                return member.text;
                            }
                        }, null).
                        value();
                }
                else if (value && value.name) {
                    text = value.name;
                }

                return {
                    text: text
                };
            },
            modelEvents: {
                'change:highlightedFragment': '__handleHighlightedFragmentChange'
            },
            __handleHighlightedFragmentChange: function () {
                this.render();
            },
            className: 'grid-cell'
        });
    },

    __getDocumentView: function () {
        return Marionette.ItemView.extend({
            template: Handlebars.compile('{{#each documents}}{{text}}{{#unless @last}}, {{/unless}}{{/each}}'),

            templateHelpers: function () {
                var value = this.model.get('value');
                var documents = [];
                if (value && value.length > 0) {
                    documents = _.chain(value).
                        map(function (item) {
                            return {
                                id: item.id,
                                text: item.columns[0],
                                url: item.columns[1]
                            };
                        }).
                        sortBy(function (document) {
                            return document.text;
                        }).
                        value();
                }

                return {
                    documents: documents
                };
            },
            modelEvents: {
                'change:highlightedFragment': '__handleHighlightedFragmentChange'
            },
            __handleHighlightedFragmentChange: function () {
                this.render();
            },
            className: 'grid-cell'
        });
    },

    __getEnumView: function() {
        return Marionette.ItemView.extend({
            template: Handlebars.compile("{{#if value}}{{valueExplained}}{{/if}}"),
            modelEvents: {
                'change:highlightedFragment': '__handleHighlightedFragmentChange'
            },
            __handleHighlightedFragmentChange: function () {
                this.render();
            },
            templateHelpers: function () {
                var value = this.model.get("value");
                return {
                    value: value,
                    valueExplained: value.valueExplained
                };
            },
            className: 'grid-cell'
        });
    }
};
export default factory;
<|endoftext|>


<commit_before>// A demonstration web server that injects possible service-unavailable errors and random latency
// (c)2015 Larry Lang

var servPort = process.env.DS_PORT || 8080; // default TCP port 8080

// for each URL route, import environment variables or set defaults for
// error 503 probability (percentage) and maximum added response latency (milliseconds)
//
// parameters for / [main]
var errorProb_main   = process.env.DS_ERR_MAIN  || 10;
var latencyMax_main  = process.env.DS_LAT_MAIN  || 1000;
// parameters for /other
var errorProb_other  = process.env.DS_ERR_OTHER || 20;
var latencyMax_other = process.env.DS_LAT_OTHER || 2000;
// parameters for /about
var errorProb_about  = process.env.DS_ERR_ABOUT || 50;
var latencyMax_about = process.env.DS_LAT_ABOUT || 0;

// for error 503 case, import environment variable or set default for
// extra maximum added response latency (milliseconds)
var latencyMax_err   = process.env.DS_LAT_ERR   || 3000;

// version assembled from parameters
var metaVersion =
    "m"+errorProb_main+","+latencyMax_main+
    "o"+errorProb_other+","+latencyMax_other+
    "a"+errorProb_about+","+latencyMax_about+
    "e"+latencyMax_err;
console.log("Starting Version "+metaVersion);

var sleep = require('sleep'); //npm install sleep

var express = require('express'); //npm install express
var app = express();
app.set('view engine', 'ejs'); // set the view engine to ejs

app.get('/', function (request, response) {
        console.log("Version",metaVersion,"Route / (main)")
        if(Math.random()*100 <= errorProb_main) {
        // respond with simulated overload
        var snooze = Math.round(Math.random()*latencyMax_main) + Math.round(Math.random()*latencyMax_err); //basic latency plus error latency
        sleep.usleep(snooze*1000);
        console.log("Responding with 503 error after added latency of  "+snooze+" milliseconds");
        sleep.usleep(snooze*1000);
        response.status(503);
        response.render('pages/error', {
                        metaVersion: metaVersion,
                        snooze: snooze
                        });
        } else {
        // respond normally
        var snooze = Math.round(Math.random()*latencyMax_main);
        console.log("Responding with normal page after added latency of "+snooze+" milliseconds");
        sleep.usleep(snooze*1000);
        response.render('pages/normal', {
                        metaVersion: metaVersion,
                        snooze: snooze,
                        greeting: 'Welcome!',
                        tagline: 'You\'ve come to the right place',
                        imageURL: 'http://gencoresystems.com/wp-content/uploads/2014/09/logo.png'
                        });
        }});

app.get('/other', function (request, response) {
        console.log("Version",metaVersion,"Route /other")
        if(Math.random()*100 <= errorProb_other) {
        // respond with simulated overload
        var snooze = Math.round(Math.random()*latencyMax_other) + Math.round(Math.random()*latencyMax_err); //basic latency plus error latency
        sleep.usleep(snooze*1000);
        console.log("Responding with 503 error after added latency of  "+snooze+" milliseconds");
        sleep.usleep(snooze*1000);
        response.status(503);
        response.render('pages/error', {
                        metaVersion: metaVersion,
                        snooze: snooze
                        });
        } else {
        // respond normally
        var snooze = Math.round(Math.random()*latencyMax_other);
        console.log("Responding with normal page after added latency of "+snooze+" milliseconds");
        sleep.usleep(snooze*1000);
        response.render('pages/normal', {
                        metaVersion: metaVersion,
                        snooze: snooze,
                        greeting: 'Surprise!',
                        tagline: 'Something completely different',
                        imageURL: 'http://lorempixel.com/200/200/'
                        });
        }});

app.get('/about', function (request, response) {
        console.log("Version",metaVersion,"Route /about")
        if(Math.random()*100 <= errorProb_about) {
        // respond with simulated overload
        var snooze = Math.round(Math.random()*latencyMax_about) + Math.round(Math.random()*latencyMax_err); //basic latency plus error latency
        sleep.usleep(snooze*1000);
        console.log("Responding with 503 error after added latency of  "+snooze+" milliseconds");
        sleep.usleep(snooze*1000);
        response.status(503);
        response.render('pages/error', {
                        metaVersion: metaVersion,
                        snooze: snooze
                        });
        } else {
        // respond normally
        var snooze = Math.round(Math.random()*latencyMax_about);
        console.log("Responding with normal page after added latency of "+snooze+" milliseconds");
        sleep.usleep(snooze*1000);
        response.render('pages/normal', {
                        metaVersion: metaVersion,
                        snooze: snooze,
                        greeting: 'Cheers!',
                        tagline: 'Everyone likes instant gratification',
                        imageURL: 'http://larrylang.net/images/LarryLangBeer.jpg'
                        });
        }});

var server = app.listen(servPort, function () {
                        var host = server.address().address;
                        var port = server.address().port;
                        console.log('Server listening at http://%s:%s', host, port);
                        });
<commit_msg>Report client information<commit_after>// A demonstration web server that injects possible service-unavailable errors and random latency
// (c)2015 Larry Lang

var servPort = process.env.DS_PORT || 8080; // default TCP port 8080

// for each URL route, import environment variables or set defaults for
// error 503 probability (percentage) and maximum added response latency (milliseconds)
//
// parameters for / [main]
var errorProb_main   = process.env.DS_ERR_MAIN  || 10;
var latencyMax_main  = process.env.DS_LAT_MAIN  || 1000;
// parameters for /other
var errorProb_other  = process.env.DS_ERR_OTHER || 20;
var latencyMax_other = process.env.DS_LAT_OTHER || 2000;
// parameters for /about
var errorProb_about  = process.env.DS_ERR_ABOUT || 50;
var latencyMax_about = process.env.DS_LAT_ABOUT || 0;

// for error 503 case, import environment variable or set default for
// extra maximum added response latency (milliseconds)
var latencyMax_err   = process.env.DS_LAT_ERR   || 3000;

// version assembled from parameters
var metaVersion =
    "m"+errorProb_main+","+latencyMax_main+
    "o"+errorProb_other+","+latencyMax_other+
    "a"+errorProb_about+","+latencyMax_about+
    "e"+latencyMax_err;
console.log("Starting Version "+metaVersion);

var sleep = require('sleep'); //npm install sleep

var express = require('express'); //npm install express
var app = express();
app.set('view engine', 'ejs'); // set the view engine to ejs

app.get('/', function (request, response) {
        console.log("Client",request.connection.remoteAddress, request.headers['user-agent']);
        console.log("Version",metaVersion,"Route / (main)");
        if(Math.random()*100 <= errorProb_main) {
        // respond with simulated overload
        var snooze = Math.round(Math.random()*latencyMax_main) + Math.round(Math.random()*latencyMax_err); //basic latency plus error latency
        sleep.usleep(snooze*1000);
        console.log("Responding with 503 error after added latency of  "+snooze+" milliseconds");
        sleep.usleep(snooze*1000);
        response.status(503);
        response.render('pages/error', {
                        metaVersion: metaVersion,
                        snooze: snooze
                        });
        } else {
        // respond normally
        var snooze = Math.round(Math.random()*latencyMax_main);
        console.log("Responding with normal page after added latency of "+snooze+" milliseconds");
        sleep.usleep(snooze*1000);
        response.render('pages/normal', {
                        metaVersion: metaVersion,
                        snooze: snooze,
                        greeting: 'Welcome!',
                        tagline: 'You\'ve come to the right place',
                        imageURL: 'http://gencoresystems.com/wp-content/uploads/2014/09/logo.png'
                        });
        }});

app.get('/other', function (request, response) {
        console.log("Client",request.connection.remoteAddress, request.headers['user-agent']);
        console.log("Version",metaVersion,"Route /other");
       if(Math.random()*100 <= errorProb_other) {
        // respond with simulated overload
        var snooze = Math.round(Math.random()*latencyMax_other) + Math.round(Math.random()*latencyMax_err); //basic latency plus error latency
        sleep.usleep(snooze*1000);
        console.log("Responding with 503 error after added latency of  "+snooze+" milliseconds");
        sleep.usleep(snooze*1000);
        response.status(503);
        response.render('pages/error', {
                        metaVersion: metaVersion,
                        snooze: snooze
                        });
        } else {
        // respond normally
        var snooze = Math.round(Math.random()*latencyMax_other);
        console.log("Responding with normal page after added latency of "+snooze+" milliseconds");
        sleep.usleep(snooze*1000);
        response.render('pages/normal', {
                        metaVersion: metaVersion,
                        snooze: snooze,
                        greeting: 'Surprise!',
                        tagline: 'Something completely different',
                        imageURL: 'http://lorempixel.com/200/200/'
                        });
        }});

app.get('/about', function (request, response) {
        console.log("Client",request.connection.remoteAddress, request.headers['user-agent']);
        console.log("Version",metaVersion,"Route /about");
        if(Math.random()*100 <= errorProb_about) {
        // respond with simulated overload
        var snooze = Math.round(Math.random()*latencyMax_about) + Math.round(Math.random()*latencyMax_err); //basic latency plus error latency
        sleep.usleep(snooze*1000);
        console.log("Responding with 503 error after added latency of  "+snooze+" milliseconds");
        sleep.usleep(snooze*1000);
        response.status(503);
        response.render('pages/error', {
                        metaVersion: metaVersion,
                        snooze: snooze
                        });
        } else {
        // respond normally
        var snooze = Math.round(Math.random()*latencyMax_about);
        console.log("Responding with normal page after added latency of "+snooze+" milliseconds");
        sleep.usleep(snooze*1000);
        response.render('pages/normal', {
                        metaVersion: metaVersion,
                        snooze: snooze,
                        greeting: 'Cheers!',
                        tagline: 'Everyone likes instant gratification',
                        imageURL: 'http://larrylang.net/images/LarryLangBeer.jpg'
                        });
        }});

var server = app.listen(servPort, function () {
                        var host = server.address().address;
                        var port = server.address().port;
                        console.log('Server listening at http://%s:%s', host, port);
                        });
<|endoftext|>