<commit_before>'use strict';

/**
 * Browser SubApp Sidebar View handler.
 *
 * @module juju.browser
 * @submodule views
 *
 */
YUI.add('subapp-browser-mainview', function(Y) {
  var ns = Y.namespace('juju.browser.views'),
      models = Y.namespace('juju.models'),
      views = Y.namespace('juju.views'),
      widgets = Y.namespace('juju.widgets');

  /**
   * Base shared view for MainView sidebar and fullscreen.
   *
   * @class MainView
   * @extends {Y.View}
   *
   */
  ns.MainView = Y.Base.create('browser-view-mainview', Y.View, [], {
    /**
     * Track events we create for proper cleanup.
     * @attribute _events
     * @default []
     * @type {Array}
     *
     */
    _events: [],
    /**
     * When we click the fullscreen toggle UX widget, what url do we route to.
     * We have to dump this into the template because we can't dynamically
     * showView/navigate from here.
     *
     * Views extending this should implement the url path.
     *
     * @attribute _fullscreenTarget
     * @default ''
     * @type {String}
     *
     */
    _fullscreenTarget: '',
    /**
     * Extending classes need to specify the template used to render display.
     *
     * @attribute template
     * @default ''
     * @type {Object}
     *
     */
    template: '',
    /**
     * Is this view currently visible? Tracked for use with the show/hide
     * toggle control in the Search widget.
     *
     * @attribute visible
     * @default true
     * @type {Boolean}
     *
     */
    visible: true,

    /**
     * Bind events watching for search widget changes.
     *
     * @method _bindSearchWidgetEvents
     * @private
     *
     */
    _bindSearchWidgetEvents: function() {
      // Watch the Search widget for changes to the search params.
      this._events.push(
          this.search.on(
              this.search.EVT_UPDATE_SEARCH, this._searchChanged, this)
      );

      this._events.push(
          this.search.on(
              this.search.EVT_TOGGLE_VIEWABLE, this._toggleBrowser, this)
      );
    },

    /**
     * Helper to just render the charm details pane. This is shared in the
     * sidebar/fullscreen views.
     *
     * @method _renderCharmDetails
     * @param {BrowserCharm} charm model instance to render from.
     * @param {Node} container node to look for a details div in to render to.
     *
     */
    _renderCharmDetails: function(charm, container) {
      var detailsNode = container.one('.bws-view-data');
      // Destroy any current details.
      if (this.details) {
        this.details.destroy(true);
      }
      this.details = new ns.BrowserCharmView({
        charm: charm,
        store: this.get('store')
      });
      this.details.render(detailsNode);
    },

    /**
     * Render the view of a single charm details page.
     *
     * @method _renderCharmView
     * @param {Node} container the node to insert our rendered content into.
     *
     */
    _renderCharmView: function(container) {
      var tpl = this.template(),
          tplNode = Y.Node.create(tpl);

      // Create/bind the search before we wait for the charm data to load so
      // that we're prepared for search events in case that request takes a
      // while or even fails.
      this._renderSearchWidget(tplNode);

      // We need to have the template in the DOM for sub views to be able to
      // expect proper structure.
      if (!Y.Lang.isValue(container)) {
        container = this.get('container');
      }
      container.setHTML(tplNode);

      this.get('store').charm(this.get('charmID'), {
        'success': function(data) {
          var charmView = new ns.BrowserCharmView({
            charm: new models.BrowserCharm(data),
            store: this.get('store')
          });
          charmView.render(tplNode.one('.bws-view-data'), this.isFullscreen());
          container.setHTML(tplNode);
        },
        'failure': this.apiFailure
      }, this);
    },

    /**
     * Render out the main search widget and controls shared across various
     * views.
     *
     * @method _renderSearchWidget
     * @param {Node} node the node to render into.
     *
     */
    _renderSearchWidget: function(node) {
      this.search = new widgets.browser.Search({
        fullscreenTarget: this._fullscreenTarget
      });
      this.search.render(node.one('.bws-header'));
    },

    /**
     * When the search term or filter is changed, fetch new data and redraw.
     *
     * @method _searchChanged
     * @param {Event} ev event object from catching changes.
     * @private
     *
     */
    _searchChanged: function(ev) {
      console.log('search changed.');
    },

    /**
     * Toggle the visibility of the browser. Bound to nav controls in the
     * view, however this will be expanded to be controlled from the new
     * constant nav menu outside of the view once it's completed.
     *
     * @method _toggle_sidebar
     * @param {Event} ev event to trigger the toggle.
     *
     */
    _toggleBrowser: function(ev) {
      var sidebar = Y.one('.charmbrowser');

      if (this.visible) {
        sidebar.hide();
        this.visible = false;
      } else {
        sidebar.show();
        this.visible = true;
      }
    },

    /**
     * Shared method to generate a message to the user based on a bad api
     * call.
     *
     * @method apiFailure
     * @param {Object} data the json decoded response text.
     * @param {Object} request the original io_request object for debugging.
     *
     */
    apiFailure: function(data, request) {
      var message;
      if (data && data.type) {
        message = 'Charm API error of type: ' + data.type;
      } else {
        message = 'Charm API server did not respond';
      }
      this.get('db').notifications.add(
          new models.Notification({
            title: 'Failed to load sidebar content.',
            message: message,
            level: 'error'
          })
      );
    },

    /**
     * Destroy this view and clear from the dom world.
     *
     * @method destructor
     *
     */
    destructor: function() {
      Y.Array.each(this._events, function(ev) {
        ev.detach();
      });
      this._cacheCharms.destroy();

      // Clean up any details view we might have hanging around.
      if (this.details) {
        this.details.destroy(true);
      }
    },

    /**
     * General YUI initializer.
     *
     * @method initializer
     * @param {Object} cfg configuration object.
     *
     */
    initializer: function(cfg) {
      this.set('store', new Y.juju.Charmworld0({
        'apiHost': window.juju_config.charmworldURL
      }));

      // Hold onto charm data so we can pass model instances to other views when
      // charms are selected.
      this._cacheCharms = new models.BrowserCharmList();
    },

    /**
     * Check if this view is the fullscreen version to help aid us in
     * template work.
     *
     * @method isFullscreen
     * @return {{Bool}}
     *
     */
    isFullscreen: function() {
      if (this.name.indexOf('fullscreen') === -1) {
        return false;
      } else {
        return true;
      }
    }

  }, {
    ATTRS: {
      /**
       * If this view is called from the point of view of a specific charmId
       * it'll be set here.
       *
       * @attribute charmID
       * @default undefined
       * @type {String}
       *
       */
      charmID: {},

      /**
       * An instance of the Charmworld API object to hit for any data that
       * needs fetching.
       *
       * @attribute store
       * @default undefined
       * @type {Charmworld0}
       *
       */
      store: {},

      /**
       * If this were a route that had a subpath component it's passed into
       * the view to aid in rendering.
       *
       * e.g. /bws/fullscreen/*charmid/hooks to load the hooks tab correctly.
       *
       * @attribute subpath
       * @default undefined
       * @type {String}
       *
       */
      subpath: {}

    }
  });

}, '0.1.0', {
  requires: [
    'browser-charm-slider',
    'browser-charm-token',
    'browser-search-widget',
    'juju-charm-store',
    'juju-models',
    'view'
  ]
});
<commit_msg>Removed slider req in browser views.<commit_after>'use strict';

/**
 * Browser SubApp Sidebar View handler.
 *
 * @module juju.browser
 * @submodule views
 *
 */
YUI.add('subapp-browser-mainview', function(Y) {
  var ns = Y.namespace('juju.browser.views'),
      models = Y.namespace('juju.models'),
      views = Y.namespace('juju.views'),
      widgets = Y.namespace('juju.widgets');

  /**
   * Base shared view for MainView sidebar and fullscreen.
   *
   * @class MainView
   * @extends {Y.View}
   *
   */
  ns.MainView = Y.Base.create('browser-view-mainview', Y.View, [], {
    /**
     * Track events we create for proper cleanup.
     * @attribute _events
     * @default []
     * @type {Array}
     *
     */
    _events: [],
    /**
     * When we click the fullscreen toggle UX widget, what url do we route to.
     * We have to dump this into the template because we can't dynamically
     * showView/navigate from here.
     *
     * Views extending this should implement the url path.
     *
     * @attribute _fullscreenTarget
     * @default ''
     * @type {String}
     *
     */
    _fullscreenTarget: '',
    /**
     * Extending classes need to specify the template used to render display.
     *
     * @attribute template
     * @default ''
     * @type {Object}
     *
     */
    template: '',
    /**
     * Is this view currently visible? Tracked for use with the show/hide
     * toggle control in the Search widget.
     *
     * @attribute visible
     * @default true
     * @type {Boolean}
     *
     */
    visible: true,

    /**
     * Bind events watching for search widget changes.
     *
     * @method _bindSearchWidgetEvents
     * @private
     *
     */
    _bindSearchWidgetEvents: function() {
      // Watch the Search widget for changes to the search params.
      this._events.push(
          this.search.on(
              this.search.EVT_UPDATE_SEARCH, this._searchChanged, this)
      );

      this._events.push(
          this.search.on(
              this.search.EVT_TOGGLE_VIEWABLE, this._toggleBrowser, this)
      );
    },

    /**
     * Helper to just render the charm details pane. This is shared in the
     * sidebar/fullscreen views.
     *
     * @method _renderCharmDetails
     * @param {BrowserCharm} charm model instance to render from.
     * @param {Node} container node to look for a details div in to render to.
     *
     */
    _renderCharmDetails: function(charm, container) {
      var detailsNode = container.one('.bws-view-data');
      // Destroy any current details.
      if (this.details) {
        this.details.destroy(true);
      }
      this.details = new ns.BrowserCharmView({
        charm: charm,
        store: this.get('store')
      });
      this.details.render(detailsNode);
    },

    /**
     * Render the view of a single charm details page.
     *
     * @method _renderCharmView
     * @param {Node} container the node to insert our rendered content into.
     *
     */
    _renderCharmView: function(container) {
      var tpl = this.template(),
          tplNode = Y.Node.create(tpl);

      // Create/bind the search before we wait for the charm data to load so
      // that we're prepared for search events in case that request takes a
      // while or even fails.
      this._renderSearchWidget(tplNode);

      // We need to have the template in the DOM for sub views to be able to
      // expect proper structure.
      if (!Y.Lang.isValue(container)) {
        container = this.get('container');
      }
      container.setHTML(tplNode);

      this.get('store').charm(this.get('charmID'), {
        'success': function(data) {
          var charmView = new ns.BrowserCharmView({
            charm: new models.BrowserCharm(data),
            store: this.get('store')
          });
          charmView.render(tplNode.one('.bws-view-data'), this.isFullscreen());
          container.setHTML(tplNode);
        },
        'failure': this.apiFailure
      }, this);
    },

    /**
     * Render out the main search widget and controls shared across various
     * views.
     *
     * @method _renderSearchWidget
     * @param {Node} node the node to render into.
     *
     */
    _renderSearchWidget: function(node) {
      this.search = new widgets.browser.Search({
        fullscreenTarget: this._fullscreenTarget
      });
      this.search.render(node.one('.bws-header'));
    },

    /**
     * When the search term or filter is changed, fetch new data and redraw.
     *
     * @method _searchChanged
     * @param {Event} ev event object from catching changes.
     * @private
     *
     */
    _searchChanged: function(ev) {
      console.log('search changed.');
    },

    /**
     * Toggle the visibility of the browser. Bound to nav controls in the
     * view, however this will be expanded to be controlled from the new
     * constant nav menu outside of the view once it's completed.
     *
     * @method _toggle_sidebar
     * @param {Event} ev event to trigger the toggle.
     *
     */
    _toggleBrowser: function(ev) {
      var sidebar = Y.one('.charmbrowser');

      if (this.visible) {
        sidebar.hide();
        this.visible = false;
      } else {
        sidebar.show();
        this.visible = true;
      }
    },

    /**
     * Shared method to generate a message to the user based on a bad api
     * call.
     *
     * @method apiFailure
     * @param {Object} data the json decoded response text.
     * @param {Object} request the original io_request object for debugging.
     *
     */
    apiFailure: function(data, request) {
      var message;
      if (data && data.type) {
        message = 'Charm API error of type: ' + data.type;
      } else {
        message = 'Charm API server did not respond';
      }
      this.get('db').notifications.add(
          new models.Notification({
            title: 'Failed to load sidebar content.',
            message: message,
            level: 'error'
          })
      );
    },

    /**
     * Destroy this view and clear from the dom world.
     *
     * @method destructor
     *
     */
    destructor: function() {
      Y.Array.each(this._events, function(ev) {
        ev.detach();
      });
      this._cacheCharms.destroy();

      // Clean up any details view we might have hanging around.
      if (this.details) {
        this.details.destroy(true);
      }
    },

    /**
     * General YUI initializer.
     *
     * @method initializer
     * @param {Object} cfg configuration object.
     *
     */
    initializer: function(cfg) {
      this.set('store', new Y.juju.Charmworld0({
        'apiHost': window.juju_config.charmworldURL
      }));

      // Hold onto charm data so we can pass model instances to other views when
      // charms are selected.
      this._cacheCharms = new models.BrowserCharmList();
    },

    /**
     * Check if this view is the fullscreen version to help aid us in
     * template work.
     *
     * @method isFullscreen
     * @return {{Bool}}
     *
     */
    isFullscreen: function() {
      if (this.name.indexOf('fullscreen') === -1) {
        return false;
      } else {
        return true;
      }
    }

  }, {
    ATTRS: {
      /**
       * If this view is called from the point of view of a specific charmId
       * it'll be set here.
       *
       * @attribute charmID
       * @default undefined
       * @type {String}
       *
       */
      charmID: {},

      /**
       * An instance of the Charmworld API object to hit for any data that
       * needs fetching.
       *
       * @attribute store
       * @default undefined
       * @type {Charmworld0}
       *
       */
      store: {},

      /**
       * If this were a route that had a subpath component it's passed into
       * the view to aid in rendering.
       *
       * e.g. /bws/fullscreen/*charmid/hooks to load the hooks tab correctly.
       *
       * @attribute subpath
       * @default undefined
       * @type {String}
       *
       */
      subpath: {}

    }
  });

}, '0.1.0', {
  requires: [
    'browser-charm-token',
    'browser-search-widget',
    'juju-charm-store',
    'juju-models',
    'view'
  ]
});
<|endoftext|>


<commit_before>/*
**  ComponentJS -- Component System for JavaScript <http://componentjs.com>
**  Copyright (c) 2009-2013 Ralf S. Engelschall <http://engelschall.com>
**
**  This Source Code Form is subject to the terms of the Mozilla Public
**  License, v. 2.0. If a copy of the MPL was not distributed with this
**  file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

/* global Handlebars: true, btoa: true */

app.ui.comp.tracing = cs.clazz({
    mixin: [ cs.marker.controller ],
    protos: {
        create: function () {
            cs(this).property('ComponentJS:state-auto-increase', false)
            cs(this).create('model/view/{toolbar,grid}',
                app.ui.comp.tracing.model,
                app.ui.comp.tracing.view,
                app.ui.widget.toolbar,
                app.ui.widget.grid
            )
        },
        prepare: function () {
            var self = this
            var toolbarItems = [{
                label: 'Record',
                icon:  'microphone',
                type: 'button',
                id: 'recordBtn',
                click: 'event:record',
                state: 'state:record'
            }, {
                label: 'Load',
                icon:  'upload-alt',
                type: 'button',
                id: 'loadBtn',
                click: 'event:load'
            }, {
                label: 'Save',
                icon:  'download-alt',
                id: 'saveBtn',
                type: 'button',
                click: 'event:save'
            }, {
                label: 'Clear',
                icon:  'remove-sign',
                type: 'button',
                id: 'clearBtn',
                click: 'event:clear'
            }, {
                label: 'Check Once',
                icon:  'ok-sign',
                type: 'button',
                id: 'journalBtn',
                click: 'event:check-journal'
            }, {
                label: 'Check Continuously',
                icon: 'repeat',
                type: 'button',
                id: 'continuousBtn',
                click: 'event:continuous',
                state: 'state:continuously'
            }, {
                label: 'Filter:',
                icon:  'filter',
                type: 'text'
            }, {
                type: 'input',
                id: 'filterInp',
                keyup: 'event:filterKeyUp',
                data: 'state:filter'
            }]

            cs(self, 'model/view/toolbar').call('initialize', toolbarItems)

            var linkRenderer = function (op) {
                return new Handlebars.SafeString(
                    '' + op + ''
                )
            }
            var columns = [
                { label: 'Time',        dataIndex: 'time',       width: 50, align: 'center'                         },
                { label: 'Source',      dataIndex: 'source'                                                         },
                { label: 'ST',          dataIndex: 'sourceType', width: 20, align: 'center'                         },
                { label: 'Origin',      dataIndex: 'origin'                                                         },
                { label: 'OT',          dataIndex: 'originType', width: 20, align: 'center'                         },
                { label: 'Operation',   dataIndex: 'operation',  width: 60, align: 'center', renderer: linkRenderer },
                { label: 'Parameters',  dataIndex: 'parameters'                                                     }
            ]

            cs(self, 'model/view/grid').call('initialize', {
                columns: columns,
                selectable: false,
                sorting: { dataIndex: 'time', direction: 'desc' }
            })

            cs(self).subscribe({
                name: 'event:new-trace', spool: 'prepared',
                spreading : true, capturing : false, bubbling : false,
                func: function (ev, trace) {
                    if (trace.hidden || !cs(self, 'model').value('state:record'))
                        return;
                    cs(self, 'model').value('data:traces').push(trace)
                    cs(self, 'model/view/grid').call('insert', trace)
                    if (cs(self, 'model').value('state:continuously'))
                        cs(self).publish('checkTrace', trace)
                }
            })
        },
        show: function () {
            var self = this

            $('#tracing_upload').change(function (evt) {
                var files = evt.target.files;
                for (var i = 0, f; (f = files[i]); i++) {
                    /* global FileReader: true */
                    var reader = new FileReader()
                    reader.onload = (function () {
                        return function (e) {
                            var file = e.target.result.trim()
                            var content = file.split('\n')
                            var traces = cs('/sv').call('parseLogfile', content)
                            cs(self, 'model').value('data:traces', traces)
                            cs(self, 'model/view/grid').call('traces', traces.concat().reverse())
                            if (cs(self, 'model').value('state:continuously'))
                                cs(self).publish('checkJournal', traces)
                        }
                    })(f)

                    reader.readAsText(f)
                    $('#tracing_upload').val('')
                }
            })

            cs(self, 'model').observe({
                name: 'event:record', spool: '..:visible',
                func: function () {
                    cs(self, 'model').value('state:record', !cs(self, 'model').value('state:record'))
                }
            })

            cs(self, 'model').observe({
                name: 'event:continuous', spool: '..:visible',
                func: function () {
                    cs(self, 'model').value('state:continuously', !cs(self, 'model').value('state:continuously'))
                }
            })

            cs(self, 'model').observe({
                name: 'event:load', spool: '..:visible',
                func: function () {
                    $('#tracing_upload').trigger('click')
                }
            })

            cs(self, 'model').observe({
                name: 'event:save', spool: '..:visible',
                func: function () {
                    var traces = cs(self, 'model').value('data:traces')
                    var result = []
                    _.each(traces, function (trace) {
                        result.push(trace.toString())
                    })
                    var str = result.join(' \n')
                    window.location = 'data:application/octet-stream;base64,'+
                        btoa(str)
                }
            })

            cs(self, 'model').observe({
                name: 'event:clear', spool: '..:visible',
                func: function () {
                    cs(self, 'model').value('data:traces', [])
                    cs(self, 'model/view/grid').call('clear')
                }
            })

            cs(self, 'model').observe({
                name: 'data:filter', spool: '..:visible',
                func: function (ev, filter) {
                    cs(self, 'model/view/grid').call('filter', filter)
                }
            })

            cs(self).register({
                name: 'traces', spool: 'visible',
                func: function () {
                    return cs(self, 'model/view/grid').call('traces')
                }
            })

            cs(self, 'model').observe({
                name: 'event:check-journal', spool: '..:visible',
                func: function () {
                    cs(self).publish('checkJournal', cs(self, 'model').value('data:traces'))
                }
            })
        }
    }
})

app.ui.comp.tracing.model = cs.clazz({
    mixin: [ cs.marker.model ],
    protos: {
        create: function () {
            cs(this).property('ComponentJS:state-auto-increase', true)
            cs(this).model({
                'event:record'         : { value: false, valid: 'boolean', autoreset: true },
                'event:load'           : { value: false, valid: 'boolean', autoreset: true },
                'event:save'           : { value: false, valid: 'boolean', autoreset: true },
                'event:clear'          : { value: false, valid: 'boolean', autoreset: true },
                'event:filterKeyUp'    : { value: -1,    valid: 'number',  autoreset: true },
                'data:filter'          : { value: '',    valid: 'string', store: true      },
                'state:filter'         : { value: '',    valid: 'string', store: true      },
                'event:check-journal'  : { value: false, valid: 'boolean', autoreset: true },
                'state:record'         : { value: true,  valid: 'boolean', store: true     },
                'event:continuous'     : { value: false, valid: 'boolean', autoreset: true },
                'state:continuously'   : { value: false, valid: 'boolean', store: true     },
                'data:traces'          : { value: [],    valid: '[object*]'                }
            })
        },
        show: function () {
            var self = this
            cs(self).observe({
                name: 'event:filterKeyUp', spool: 'visible',
                func: function (ev, nVal) {
                    if (nVal === 27 /* ESCAPE */)
                        cs(self).value('state:filter', '')
                    if (nVal === 13 /* RETURN */ || nVal === 27 /* ESCAPE */)
                        cs(self).value('data:filter', cs(self).value('state:filter'))
                }
            })
        }
    }
})

app.ui.comp.tracing.view = cs.clazz({
    mixin: [ cs.marker.view ],
    protos: {
        create: function () {
            cs(this).property('ComponentJS:state-auto-increase', true)
        },
        render: function () {
            var self = this
            var content = $.markup('tracing-content')

            cs(self).socket({
                scope: 'toolbar',
                ctx: $('.toolbar', content)
            })

            cs(self).socket({
                scope: 'grid',
                ctx: $('.grid', content)
            })

            cs(self).plug({
                object: content,
                spool: 'materialized'
            })
        }
    }
})<commit_msg>Moved button to align better to the general layout<commit_after>/*
**  ComponentJS -- Component System for JavaScript <http://componentjs.com>
**  Copyright (c) 2009-2013 Ralf S. Engelschall <http://engelschall.com>
**
**  This Source Code Form is subject to the terms of the Mozilla Public
**  License, v. 2.0. If a copy of the MPL was not distributed with this
**  file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

/* global Handlebars: true, btoa: true */

app.ui.comp.tracing = cs.clazz({
    mixin: [ cs.marker.controller ],
    protos: {
        create: function () {
            cs(this).property('ComponentJS:state-auto-increase', false)
            cs(this).create('model/view/{toolbar,grid}',
                app.ui.comp.tracing.model,
                app.ui.comp.tracing.view,
                app.ui.widget.toolbar,
                app.ui.widget.grid
            )
        },
        prepare: function () {
            var self = this
            var toolbarItems = [{
                label: 'Record',
                icon:  'microphone',
                type: 'button',
                id: 'recordBtn',
                click: 'event:record',
                state: 'state:record'
            }, {
                label: 'Clear',
                icon:  'remove-sign',
                type: 'button',
                id: 'clearBtn',
                click: 'event:clear'
            }, {
                label: 'Load',
                icon:  'upload-alt',
                type: 'button',
                id: 'loadBtn',
                click: 'event:load'
            }, {
                label: 'Save',
                icon:  'download-alt',
                id: 'saveBtn',
                type: 'button',
                click: 'event:save'
            }, {
                label: 'Check Once',
                icon:  'ok-sign',
                type: 'button',
                id: 'journalBtn',
                click: 'event:check-journal'
            }, {
                label: 'Check Continuously',
                icon: 'repeat',
                type: 'button',
                id: 'continuousBtn',
                click: 'event:continuous',
                state: 'state:continuously'
            }, {
                label: 'Filter:',
                icon:  'filter',
                type: 'text'
            }, {
                type: 'input',
                id: 'filterInp',
                keyup: 'event:filterKeyUp',
                data: 'state:filter'
            }]

            cs(self, 'model/view/toolbar').call('initialize', toolbarItems)

            var linkRenderer = function (op) {
                return new Handlebars.SafeString(
                    '' + op + ''
                )
            }
            var columns = [
                { label: 'Time',        dataIndex: 'time',       width: 50, align: 'center'                         },
                { label: 'Source',      dataIndex: 'source'                                                         },
                { label: 'ST',          dataIndex: 'sourceType', width: 20, align: 'center'                         },
                { label: 'Origin',      dataIndex: 'origin'                                                         },
                { label: 'OT',          dataIndex: 'originType', width: 20, align: 'center'                         },
                { label: 'Operation',   dataIndex: 'operation',  width: 60, align: 'center', renderer: linkRenderer },
                { label: 'Parameters',  dataIndex: 'parameters'                                                     }
            ]

            cs(self, 'model/view/grid').call('initialize', {
                columns: columns,
                selectable: false,
                sorting: { dataIndex: 'time', direction: 'desc' }
            })

            cs(self).subscribe({
                name: 'event:new-trace', spool: 'prepared',
                spreading : true, capturing : false, bubbling : false,
                func: function (ev, trace) {
                    if (trace.hidden || !cs(self, 'model').value('state:record'))
                        return;
                    cs(self, 'model').value('data:traces').push(trace)
                    cs(self, 'model/view/grid').call('insert', trace)
                    if (cs(self, 'model').value('state:continuously'))
                        cs(self).publish('checkTrace', trace)
                }
            })
        },
        show: function () {
            var self = this

            $('#tracing_upload').change(function (evt) {
                var files = evt.target.files;
                for (var i = 0, f; (f = files[i]); i++) {
                    /* global FileReader: true */
                    var reader = new FileReader()
                    reader.onload = (function () {
                        return function (e) {
                            var file = e.target.result.trim()
                            var content = file.split('\n')
                            var traces = cs('/sv').call('parseLogfile', content)
                            cs(self, 'model').value('data:traces', traces)
                            cs(self, 'model/view/grid').call('traces', traces.concat().reverse())
                            if (cs(self, 'model').value('state:continuously'))
                                cs(self).publish('checkJournal', traces)
                        }
                    })(f)

                    reader.readAsText(f)
                    $('#tracing_upload').val('')
                }
            })

            cs(self, 'model').observe({
                name: 'event:record', spool: '..:visible',
                func: function () {
                    cs(self, 'model').value('state:record', !cs(self, 'model').value('state:record'))
                }
            })

            cs(self, 'model').observe({
                name: 'event:continuous', spool: '..:visible',
                func: function () {
                    cs(self, 'model').value('state:continuously', !cs(self, 'model').value('state:continuously'))
                }
            })

            cs(self, 'model').observe({
                name: 'event:load', spool: '..:visible',
                func: function () {
                    $('#tracing_upload').trigger('click')
                }
            })

            cs(self, 'model').observe({
                name: 'event:save', spool: '..:visible',
                func: function () {
                    var traces = cs(self, 'model').value('data:traces')
                    var result = []
                    _.each(traces, function (trace) {
                        result.push(trace.toString())
                    })
                    var str = result.join(' \n')
                    window.location = 'data:application/octet-stream;base64,'+
                        btoa(str)
                }
            })

            cs(self, 'model').observe({
                name: 'event:clear', spool: '..:visible',
                func: function () {
                    cs(self, 'model').value('data:traces', [])
                    cs(self, 'model/view/grid').call('clear')
                }
            })

            cs(self, 'model').observe({
                name: 'data:filter', spool: '..:visible',
                func: function (ev, filter) {
                    cs(self, 'model/view/grid').call('filter', filter)
                }
            })

            cs(self).register({
                name: 'traces', spool: 'visible',
                func: function () {
                    return cs(self, 'model/view/grid').call('traces')
                }
            })

            cs(self, 'model').observe({
                name: 'event:check-journal', spool: '..:visible',
                func: function () {
                    cs(self).publish('checkJournal', cs(self, 'model').value('data:traces'))
                }
            })
        }
    }
})

app.ui.comp.tracing.model = cs.clazz({
    mixin: [ cs.marker.model ],
    protos: {
        create: function () {
            cs(this).property('ComponentJS:state-auto-increase', true)
            cs(this).model({
                'event:record'         : { value: false, valid: 'boolean', autoreset: true },
                'event:load'           : { value: false, valid: 'boolean', autoreset: true },
                'event:save'           : { value: false, valid: 'boolean', autoreset: true },
                'event:clear'          : { value: false, valid: 'boolean', autoreset: true },
                'event:filterKeyUp'    : { value: -1,    valid: 'number',  autoreset: true },
                'data:filter'          : { value: '',    valid: 'string', store: true      },
                'state:filter'         : { value: '',    valid: 'string', store: true      },
                'event:check-journal'  : { value: false, valid: 'boolean', autoreset: true },
                'state:record'         : { value: true,  valid: 'boolean', store: true     },
                'event:continuous'     : { value: false, valid: 'boolean', autoreset: true },
                'state:continuously'   : { value: false, valid: 'boolean', store: true     },
                'data:traces'          : { value: [],    valid: '[object*]'                }
            })
        },
        show: function () {
            var self = this
            cs(self).observe({
                name: 'event:filterKeyUp', spool: 'visible',
                func: function (ev, nVal) {
                    if (nVal === 27 /* ESCAPE */)
                        cs(self).value('state:filter', '')
                    if (nVal === 13 /* RETURN */ || nVal === 27 /* ESCAPE */)
                        cs(self).value('data:filter', cs(self).value('state:filter'))
                }
            })
        }
    }
})

app.ui.comp.tracing.view = cs.clazz({
    mixin: [ cs.marker.view ],
    protos: {
        create: function () {
            cs(this).property('ComponentJS:state-auto-increase', true)
        },
        render: function () {
            var self = this
            var content = $.markup('tracing-content')

            cs(self).socket({
                scope: 'toolbar',
                ctx: $('.toolbar', content)
            })

            cs(self).socket({
                scope: 'grid',
                ctx: $('.grid', content)
            })

            cs(self).plug({
                object: content,
                spool: 'materialized'
            })
        }
    }
})<|endoftext|>


<commit_before>"use strict";

var assert = require('assert');

var add = function(x,y) { return x + y; };

var Game = function() {

    this._pins = [];

    this.roll = function(n) {
	this._pins.push(n);
    };

    this._scoreSpare = function(i) {
	return {
	    score: this._scoreFrame(i).score + this._pins[i+2],
	    pinsConsumed: 2
	};
    };

    this._scoreFrame = function(i) {
	return {
	    score: this._pins[i] + this._pins[i+1],
	    pinsConsumed: 2
	};
    };

    this._isSpare = function(i) {
	return this._pins[i] + this._pins[i+1] === 10;
    };

    this._isStrike = function(i) {
	return this._pins[i] === 10;
    };

    this._scoreStrike = function(i) {
	return {
	    score: 10 + this._pins[i+1] + this._pins[i+2],
	    pinsConsumed: 1
	};
    };

    this.score = function() {

	var sum = 0;
	var pinCount = 0;

	for (var i=0;i<10;++i) {
	    var result;
	    if (this._isStrike(i)) {
		result = this._scoreStrike(pinCount);
	    }
	    else if (this._isSpare(pinCount)) {
		result = this._scoreSpare(pinCount);
	    }
	    else {
		result = this._scoreFrame(pinCount);
	    }

	    sum += result.score;
	    pinCount+= result.pinsConsumed;
	}

	return sum;
    };

    return this;
};

describe('bowling game', function() {

    var rollMany = function(game, roll, n) {
	for (var i=0;i<n;++i) {
	    game.roll(roll);
	}
    };

    var rollSpare = function(game) {
	game.roll(5);
	game.roll(5);
    };

    it('rolling 20 gutter balls is a gutter game', function() {
	var game = new Game();
	rollMany(game, 0, 20);

	assert.equal(0, game.score());
    });

    it('hitting 20 single pins scores 20', function() {
	var game = new Game();
	rollMany(game, 1, 20);

	assert.equal(20, game.score());
    });

    it('handles spares', function() {
	var game = new Game();
	rollSpare(game);
	game.roll(3);

	// Rest is a gutter game
	rollMany(game, 0,17);

	assert.equal(16, game.score());
    });

    it('handles strikes', function() {
	var game = new Game();
	game.roll(10);
	game.roll(3);
	game.roll(3);

	rollMany(game, 0, 16);

	assert.equal(10 + 6 + 6, game.score());
    });

    it('handles a perfect game', function() {
	var game = new Game();
	rollMany(game, 10, 12);

	assert.equal(300, game.score());
    });
});
<commit_msg>that shouldn't be there<commit_after><|endoftext|>


<commit_before><commit_msg>Update modcommands.js<commit_after><|endoftext|>


<commit_before><commit_msg>Update compose window title on subject change<commit_after><|endoftext|>


<commit_before>define(function(require, exports, module) {
    main.consumes = ["Tree", "layout", "ui"];
    main.provides = ["Datagrid"];
    return main;

    function main(options, imports, register) {
        var Tree = imports.Tree;
        var layout = imports.layout;
        var ui = imports.ui;

        var TreeModel = require("ace_tree/data_provider");

        /***** Constructors *****/

        function Datagrid(options, forPlugin, baseclass) {
            if (!options) throw new Error("options are required");

            if (!options.baseName)
                options.baseName = "datagrid";

            if (!options.theme)
                options.theme = "blackdg ace-tree-" + options.baseName;

            var model = new TreeModel();
            model.columns = options.columns;
            options.model = model;

            var plugin = new Tree(options, forPlugin, true);
            // var emit = plugin.getEmitter();
            if (baseclass) plugin.baseclass();

            var acetree = plugin.acetree;

            if (!options.rowHeight) {
                layout.on("eachTheme", function(e){
                    var cls = "." + options.theme.replace(/ /g, " .") + " .row";
                    var height = parseInt(ui.getStyleRule(cls, "height"), 10) || 23;
                    // model.rowHeightInner = height - 1;
                    model.rowHeight = height;

                    if (e.changed) plugin.resize(true);
                });
            }

            /**
             */
            /**
             * @constructor
             * Creates a new Datagrid instance.
             * @param {Object} options
             * @param {Array}  options.columns
             * @param {Plugin} plugin           The plugin responsible for creating this datagrid.
             */
            plugin.freezePublicAPI({
                // Properties
                /**
                 *
                 */
                get columns(){ throw new Error("Columns can only be set.") },
                set columns(c){
                    if (!acetree) return;

                    model.columns = c;
                    acetree.renderer.setDataProvider(model);
                }
            });

            if (!baseclass)
                plugin.load(null, options.baseName || "datagrid");

            return plugin;
        }

        register(null, {
            Datagrid: Datagrid
        });
    }
});<commit_msg>fix setting columns in datagrid<commit_after>define(function(require, exports, module) {
    main.consumes = ["Tree", "layout", "ui"];
    main.provides = ["Datagrid"];
    return main;

    function main(options, imports, register) {
        var Tree = imports.Tree;
        var layout = imports.layout;
        var ui = imports.ui;

        var TreeModel = require("ace_tree/data_provider");

        /***** Constructors *****/

        function Datagrid(options, forPlugin, baseclass) {
            if (!options) throw new Error("options are required");

            if (!options.baseName)
                options.baseName = "datagrid";

            if (!options.theme)
                options.theme = "blackdg ace-tree-" + options.baseName;

            var model = new TreeModel();
            model.columns = options.columns;
            options.model = model;

            var plugin = new Tree(options, forPlugin, true);
            // var emit = plugin.getEmitter();
            if (baseclass) plugin.baseclass();

            if (!options.rowHeight) {
                layout.on("eachTheme", function(e){
                    var cls = "." + options.theme.replace(/ /g, " .") + " .row";
                    var height = parseInt(ui.getStyleRule(cls, "height"), 10) || 23;
                    // model.rowHeightInner = height - 1;
                    model.rowHeight = height;

                    if (e.changed) plugin.resize(true);
                });
            }

            /**
             */
            /**
             * @constructor
             * Creates a new Datagrid instance.
             * @param {Object} options
             * @param {Array}  options.columns
             * @param {Plugin} plugin           The plugin responsible for creating this datagrid.
             */
            plugin.freezePublicAPI({
                // Properties
                /**
                 *
                 */
                get columns(){ throw new Error("Columns can only be set.") },
                set columns(c){
                    model.columns = c;
                    if (plugin.acetree)
                        plugin.acetree.renderer.setDataProvider(model);
                }
            });

            if (!baseclass)
                plugin.load(null, options.baseName || "datagrid");

            return plugin;
        }

        register(null, {
            Datagrid: Datagrid
        });
    }
});<|endoftext|>


<commit_before>define(function(require, exports, module) {
    "use strict";

    main.consumes = [
        "connect.render",
        "connect.render.ejs",
        "connect.redirect",
        "connect.static"
    ];
    main.provides = ["preview.handler"];
    return main;

    function main(options, imports, register) {
        var error = require("http-error");
        var https = require("https");
        var http = require("http");
        var mime = require("mime");
        var parseUrl = require("url").parse;
        var debug = require("debug")("preview");

        var staticPrefix = imports["connect.static"].getStaticPrefix();

        function getProjectSession() {
            return function(req, res, next) {
                var session = req.session;

                req.user = req.user || { id: -1 };

                var username = req.params.username;
                var projectname = req.params.projectname;

                var ws = req.ws = username + "/" + projectname;

                if (!session.ws)
                    session.ws = {};

                req.projectSession = session.ws[ws];
                if (!req.projectSession || !req.projectSession.expires || req.projectSession.expires <= Date.now()) {
                    req.projectSession = session.ws[ws] = {
                        expires: Date.now() + 10000
                    };
                }

                next();
            };
        }

        function getRole(db) {
            return function(req, res, next) {
                if (req.projectSession.role) {
                    return next();
                }

                db.Project.findOne({
                    username: req.params.username,
                    name: req.params.projectname
                }, function(err, project) {
                    if (err && err.code == 404)
                        return next(new error.NotFound("Project '" + req.ws + "' doest not exist."));

                    if (err) return next(err);

                    project.getRole(req.user, function(err, role) {
                        if (err) return next(err);

                        if (role == db.Project.ROLE_NONE) {
                            if (project.isPublicPreview())
                                role = db.Project.ROLE_VISITOR;
                            else if (req.user.id == -1)
                                return next(new error.Unauthorized());
                            else
                                return next(new error.Forbidden("You don't have access rights to preview this workspace"));
                        }
                        req.projectSession.role = role;
                        req.projectSession.pid = project.id;

                        var type = project.scm;
                        req.projectSession.type = type;

                        if (type != "docker" || project.state != db.Project.STATE_READY)
                            return next();

                        project.populate("remote", function(err) {
                            if (err) return next(err);

                            var meta = project.remote.metadata;
                            if (meta && meta.host && meta.cid) {
                                db.Container.load(meta.cid, function(err, container) {
                                    if (err) return next(err);

                                    if (container.state == db.Container.STATE_RUNNING)
                                        req.projectSession.proxyUrl = "http://" + meta.host + ":9000/" + meta.cid + "/home/ubuntu/workspace";
                                    else
                                        req.projectSession.expires = Date.now() + 1000;

                                    next();
                                });
                            } else {
                                next();
                            }
                        });
                    });
                });
            };
        }

        function getProxyUrl(getServer) {
            return function(req, res, next) {

                if (req.projectSession.proxyUrl) {
                    req.proxyUrl = req.projectSession.proxyUrl;
                    return next();
                }

                var server = req.projectSession.vfsServer;
                if (!server) {
                    server = getServer();
                    if (!server || !server.url)
                        return next(new error.ServiceUnavailable("No VFS server found"));

                    server = req.projectSession.vfsServer = server.internalUrl || server.url;
                }

                var url = server + "/" + req.projectSession.pid + "/preview";

                req.proxyUrl = url;
                next();
            };
        }

        function proxyCall() {
            return function(req, res, next) {

                var path = req.params.path;
                var url = req.proxyUrl + path;
                if (req.session.token)
                    url += "?access_token=" + encodeURIComponent(req.session.token);

                var parsedUrl = parseUrl(url);
                var httpModule = parsedUrl.protocol == "https:" ? https : http;

                req.headers.accept = "application/json";
                delete req.headers.host;

                var isDir = path[path.length-1] == "/";
                if (isDir || parsedUrl.pathname.match(/\.html?$/i)) {
                    req.headers["accept-encoding"] = "identity";
                    delete req.headers["if-none-match"];
                    delete req.headers["if-range"];
                } else {
                    req.headers["accept-encoding"] = "gzip";
                }

                debug("proxy call %s", url);
                httpModule.get({
                    path: parsedUrl.path,
                    hostname: parsedUrl.hostname,
                    port: parsedUrl.port,
                    headers: req.headers
                }, function(request) {
                    if (request.statusCode >= 400)
                        handleError(request);
                    else if (isDir)
                        serveListing(request);
                    else if (request.headers["content-type"] == "text/html")
                        serveHtml(request, parsedUrl.hostname, req);
                    else
                        serveFile(request);
                }).on("error", function(err) {
                    next(err);
                });

                function handleError(request) {
                    var body = "";

                    debug("handle error %s", request.statusCode);

                    if (request.statusCode == 401)
                        return next(new error.Unauthorized());

                    var stream;
                    if (request.headers["content-encoding"] == "gzip") {
                        stream = require("zlib").createGunzip();
                        request.pipe(stream);
                    } else {
                        stream = request;
                    }

                    stream.on("data", function(data) {
                        body += data;
                    });
                    stream.on("end", function(data) {
                        if (data)
                            body += data;

                        req.headers.accept= "text/html";
                        var statusCode = request.statusCode;

                        if (body.indexOf("EISDIR") !== -1) {
                            res.redirect(req.url + "/");
                        } else if (body.indexOf("ENOENT") !== -1 || statusCode == 404) {
                            next(new error.NotFound("File '" + path + "' could not be found!"));
                        } else {
                            delete req.session.ws[req.ws];

                            var json;
                            try {
                                json = JSON.parse(body);
                            } catch(e) {}

                            if (statusCode == 503) {
                                res.setHeader('Content-Type', 'text/html; charset=utf-8');
                                res.render(__dirname + "/views/progress.html.ejs", {
                                    title: "Connecting to your Workspace",
                                    message: "Please wait a moment while we are connecting to the preview server",
                                    retryIn: 2000,
                                    statusCode: statusCode
                                }, next);
                            }
                            else if (statusCode == 428 && json && json.error) {
                                res.setHeader('Content-Type', 'text/html; charset=utf-8');
                                res.render(__dirname + "/views/progress.html.ejs", {
                                    title: json.error.message,
                                    message: "",
                                    retryIn: 2000,
                                    statusCode: statusCode
                                }, next);
                            }
                            else {
                                next(new error.HttpError(body.split("\n")[0], statusCode));
                            }

                        }
                    });
                }

                function serveListing(request) {
                    debug("serve directory listing %s", request.url);

                    var body = "";
                    request.on("data", function(data) {
                        body += data;
                    });
                    request.on("end", function(data) {
                        if (data)
                            body += data;

                        try {
                            body = JSON.parse(body);
                        } catch (e) {
                            return next(e);
                        }

                        // convert nginx listing
                        if (body[0] && body[0].type) {
                            body = body.map(function(stat) {
                                return {
                                    name: stat.name,
                                    mime: stat.type == "directory" ? "inode/directory" : mime.lookup(stat.name),
                                    size: stat.size || 0,
                                    mtime: stat.mtime
                                };
                            });
                        }
                        var entries = body
                            .filter(function(entry) {
                                return entry.name[0] !== ".";
                            })
                            .sort(function(a, b) {
                                if (a.mime === "inode/directory" && b.mime !== "inode/directory")
                                    return -1;
                                else if (a.mime !== "inode/directory" && b.mime === "inode/directory")
                                    return 1;
                                else if (a.name.toLowerCase() == b.name.toLowerCase())
                                    return 0;
                                else if (a.name.toLowerCase() < b.name.toLowerCase())
                                    return -1;
                                else
                                    return 1;
                            });

                        res.render(__dirname + "/views/listing.html.ejs", {
                            isRoot: path == "/",
                            entries: entries
                        }, next);
                    });
                }

                function serveHtml(request, ideHost, req) {
                    debug("serve HTML %s", request.url);

                    var shouldInject = req.parsedUrl.query["_c9_id"] ? true : false;

                    var inject = shouldInject
                        ? '<script src="' + staticPrefix + '/preview/livecss.js"></script>'
                        : '';
                    var generateInstrumentedHTML = require("../c9.ide.language.html.diff/HTMLSimpleDOM").generateInstrumentedHTML;

                    var buffer = "";
                    request.on("data", function(data) {
                        buffer += data;
                    });
                    request.on("end", function(data) {
                        if (data)
                            buffer += data;

                        if (shouldInject)
                            buffer = generateInstrumentedHTML(buffer) || "";
                        data = new Buffer(buffer);
                        res.writeHead(200, {
                            "content-length": data.length + inject.length,
                            "content-type": request.headers["content-type"],
                            "etag": request.headers.etag,
                            "date": request.headers.data,
                            "access-control-allow-origin": "https://ide." + ideHost
                        });
                        res.write(data);

                        res.end(inject);
                    });
                }

                function serveFile(request) {
                    debug("forward file %s", request.url);
                    res.writeHead(request.statusCode, request.headers);
                    request.pipe(res);
                }

            };
        }

        register(null, {
            "preview.handler": {
                getProjectSession: getProjectSession,
                getRole: getRole,
                getProxyUrl: getProxyUrl,
                proxyCall: proxyCall
            }
        });
    }
});<commit_msg>fix typo<commit_after>define(function(require, exports, module) {
    "use strict";

    main.consumes = [
        "connect.render",
        "connect.render.ejs",
        "connect.redirect",
        "connect.static"
    ];
    main.provides = ["preview.handler"];
    return main;

    function main(options, imports, register) {
        var error = require("http-error");
        var https = require("https");
        var http = require("http");
        var mime = require("mime");
        var parseUrl = require("url").parse;
        var debug = require("debug")("preview");

        var staticPrefix = imports["connect.static"].getStaticPrefix();

        function getProjectSession() {
            return function(req, res, next) {
                var session = req.session;

                req.user = req.user || { id: -1 };

                var username = req.params.username;
                var projectname = req.params.projectname;

                var ws = req.ws = username + "/" + projectname;

                if (!session.ws)
                    session.ws = {};

                req.projectSession = session.ws[ws];
                if (!req.projectSession || !req.projectSession.expires || req.projectSession.expires <= Date.now()) {
                    req.projectSession = session.ws[ws] = {
                        expires: Date.now() + 10000
                    };
                }

                next();
            };
        }

        function getRole(db) {
            return function(req, res, next) {
                if (req.projectSession.role) {
                    return next();
                }

                db.Project.findOne({
                    username: req.params.username,
                    name: req.params.projectname
                }, function(err, project) {
                    if (err && err.code == 404)
                        return next(new error.NotFound("Project '" + req.ws + "' doest not exist."));

                    if (err) return next(err);

                    project.getRole(req.user, function(err, role) {
                        if (err) return next(err);

                        if (role == db.Project.ROLE_NONE) {
                            if (project.isPublicPreview())
                                role = db.Project.ROLE_VISITOR;
                            else if (req.user.id == -1)
                                return next(new error.Unauthorized());
                            else
                                return next(new error.Forbidden("You don't have access rights to preview this workspace"));
                        }
                        req.projectSession.role = role;
                        req.projectSession.pid = project.id;

                        var type = project.scm;
                        req.projectSession.type = type;

                        if (type != "docker" || project.state != db.Project.STATE_READY)
                            return next();

                        project.populate("remote", function(err) {
                            if (err) return next(err);

                            var meta = project.remote.metadata;
                            if (meta && meta.host && meta.cid) {
                                db.Container.load(meta.cid, function(err, container) {
                                    if (err) return next(err);

                                    if (container.state == db.Container.STATE_RUNNING)
                                        req.projectSession.proxyUrl = "http://" + meta.host + ":9000/" + meta.cid + "/home/ubuntu/workspace";
                                    else
                                        req.projectSession.expires = Date.now() + 1000;

                                    next();
                                });
                            } else {
                                next();
                            }
                        });
                    });
                });
            };
        }

        function getProxyUrl(getServer) {
            return function(req, res, next) {

                if (req.projectSession.proxyUrl) {
                    req.proxyUrl = req.projectSession.proxyUrl;
                    return next();
                }

                var server = req.projectSession.vfsServer;
                if (!server) {
                    server = getServer();
                    if (!server || !server.url)
                        return next(new error.ServiceUnavailable("No VFS server found"));

                    server = req.projectSession.vfsServer = server.internalUrl || server.url;
                }

                var url = server + "/" + req.projectSession.pid + "/preview";

                req.proxyUrl = url;
                next();
            };
        }

        function proxyCall() {
            return function(req, res, next) {

                var path = req.params.path;
                var url = req.proxyUrl + path;
                if (req.session.token)
                    url += "?access_token=" + encodeURIComponent(req.session.token);

                var parsedUrl = parseUrl(url);
                var httpModule = parsedUrl.protocol == "https:" ? https : http;

                req.headers.accept = "application/json";
                delete req.headers.host;

                var isDir = path[path.length-1] == "/";
                if (isDir || parsedUrl.pathname.match(/\.html?$/i)) {
                    req.headers["accept-encoding"] = "identity";
                    delete req.headers["if-none-match"];
                    delete req.headers["if-range"];
                } else {
                    req.headers["accept-encoding"] = "gzip";
                }

                debug("proxy call %s", url);
                httpModule.get({
                    path: parsedUrl.path,
                    hostname: parsedUrl.hostname,
                    port: parsedUrl.port,
                    headers: req.headers
                }, function(request) {
                    if (request.statusCode >= 400)
                        handleError(request);
                    else if (isDir)
                        serveListing(request);
                    else if (request.headers["content-type"] == "text/html")
                        serveHtml(request, parsedUrl.hostname, req);
                    else
                        serveFile(request);
                }).on("error", function(err) {
                    next(err);
                });

                function handleError(request) {
                    var body = "";

                    debug("handle error %s", request.statusCode);

                    if (request.statusCode == 401)
                        return next(new error.Unauthorized());

                    var stream;
                    if (request.headers["content-encoding"] == "gzip") {
                        stream = require("zlib").createGunzip();
                        request.pipe(stream);
                    } else {
                        stream = request;
                    }

                    stream.on("data", function(data) {
                        body += data;
                    });
                    stream.on("end", function(data) {
                        if (data)
                            body += data;

                        req.headers.accept= "text/html";
                        var statusCode = request.statusCode;

                        if (body.indexOf("EISDIR") !== -1) {
                            res.redirect(req.url + "/");
                        } else if (body.indexOf("ENOENT") !== -1 || statusCode == 404) {
                            next(new error.NotFound("File '" + path + "' could not be found!"));
                        } else {
                            delete req.session.ws[req.ws];

                            var json;
                            try {
                                json = JSON.parse(body);
                            } catch(e) {}

                            if (statusCode == 503) {
                                res.setHeader('Content-Type', 'text/html; charset=utf-8');
                                res.render(__dirname + "/views/progress.html.ejs", {
                                    title: "Connecting to your Workspace",
                                    message: "Please wait a moment while we are connecting to the preview server",
                                    retryIn: 2000,
                                    statusCode: statusCode
                                }, next);
                            }
                            else if (statusCode == 428 && json && json.error) {
                                res.setHeader('Content-Type', 'text/html; charset=utf-8');
                                res.render(__dirname + "/views/progress.html.ejs", {
                                    title: json.error.message,
                                    message: "",
                                    retryIn: 2000,
                                    statusCode: statusCode
                                }, next);
                            }
                            else {
                                next(new error.HttpError(body.split("\n")[0], statusCode));
                            }

                        }
                    });
                }

                function serveListing(request) {
                    debug("serve directory listing %s", request.url);

                    var body = "";
                    request.on("data", function(data) {
                        body += data;
                    });
                    request.on("end", function(data) {
                        if (data)
                            body += data;

                        try {
                            body = JSON.parse(body);
                        } catch (e) {
                            return next(e);
                        }

                        // convert nginx listing
                        if (body[0] && body[0].type) {
                            body = body.map(function(stat) {
                                return {
                                    name: stat.name,
                                    mime: stat.type == "directory" ? "inode/directory" : mime.lookup(stat.name),
                                    size: stat.size || 0,
                                    mtime: stat.mtime
                                };
                            });
                        }
                        var entries = body
                            .filter(function(entry) {
                                return entry.name[0] !== ".";
                            })
                            .sort(function(a, b) {
                                if (a.mime === "inode/directory" && b.mime !== "inode/directory")
                                    return -1;
                                else if (a.mime !== "inode/directory" && b.mime === "inode/directory")
                                    return 1;
                                else if (a.name.toLowerCase() == b.name.toLowerCase())
                                    return 0;
                                else if (a.name.toLowerCase() < b.name.toLowerCase())
                                    return -1;
                                else
                                    return 1;
                            });

                        res.render(__dirname + "/views/listing.html.ejs", {
                            isRoot: path == "/",
                            entries: entries
                        }, next);
                    });
                }

                function serveHtml(request, ideHost, req) {
                    debug("serve HTML %s", request.url);

                    var shouldInject = req.parsedUrl.query["_c9_id"] ? true : false;

                    var inject = shouldInject
                        ? '<script src="' + staticPrefix + '/preview/livecss.js"></script>'
                        : '';
                    var generateInstrumentedHTML = require("../c9.ide.language.html.diff/HTMLSimpleDOM").generateInstrumentedHTML;

                    var buffer = "";
                    request.on("data", function(data) {
                        buffer += data;
                    });
                    request.on("end", function(data) {
                        if (data)
                            buffer += data;

                        if (shouldInject)
                            buffer = generateInstrumentedHTML(buffer) || "";
                        data = new Buffer(buffer);
                        res.writeHead(200, {
                            "content-length": data.length + inject.length,
                            "content-type": request.headers["content-type"],
                            "etag": request.headers.etag,
                            "date": request.headers.date,
                            "access-control-allow-origin": "https://ide." + ideHost
                        });
                        res.write(data);

                        res.end(inject);
                    });
                }

                function serveFile(request) {
                    debug("forward file %s", request.url);
                    res.writeHead(request.statusCode, request.headers);
                    request.pipe(res);
                }

            };
        }

        register(null, {
            "preview.handler": {
                getProjectSession: getProjectSession,
                getRole: getRole,
                getProxyUrl: getProxyUrl,
                proxyCall: proxyCall
            }
        });
    }
});<|endoftext|>


<commit_before>/** @jsx h */

import { h, render } from 'preact';

export default (Base = HTMLElement) =>
  class extends Base {
    get props() {
      // We override props so that we can satisfy most use
      // cases for children by using a slot.
      return {
        ...super.props,
        ...{ children: <slot /> }
      };
    }
    renderer(root, call) {
      this._preactDom = render(call(), root, this._preactDom || root.childNodes[0]);
    }
  };
<commit_msg>Preserve previous style<commit_after>/** @jsx h */

import { h, render } from 'preact';

export default (Base = HTMLElement) =>
  class extends Base {
    get props() {
      // We override props so that we can satisfy most use
      // cases for children by using a slot.
      return {
        ...super.props,
        ...{ children: <slot /> }
      };
    }
    renderer(root, call) {
      this._preactDom = render(
        call(),
        root,
        this._preactDom || root.childNodes[0]
      );
    }
  };
<|endoftext|>


<commit_before><commit_msg>(standard --fix)<commit_after><|endoftext|>


<commit_before><commit_msg>Made the meme come up if you type in more stuff<commit_after><|endoftext|>


<commit_before>function ProfileWindow(title) {
	var global = require('ui/common/globals');
	var self = Ti.UI.createWindow({
		title:title,
		barColor: global.colors.dark,
		backgroundColor: global.colors.lightest
	});

	var image = Ti.UI.createImageView({

  		height: 100,
  		width: 100,
  		top: 10,
  		left: 10,
  		borderColor: 'black',
		borderWidth: 1,
  		contentMode: 'aspectfill',
  		clipsToBounds: false,
  		image:'/images/user_image.png',
	});
	self.add(image)

	// NEW TEST TEXT FIELD
	var textArea = Ti.UI.createTextArea({

		color: global.colors.dark,
		font: {fontSize:20, fontWeight:'bold'},
		keyboardType: Ti.UI.KEYBOARD_NUMBER_PAD,
		returnKeyType: Ti.UI.RETURNKEY_GO,
		textAlign: 'left',
		value: 'I am a textarea',
		top: 60,
		width: 300, height : 70
});

	return self;
};

module.exports = ProfileWindow;<commit_msg>Added some text to Profile Window<commit_after>function ProfileWindow(title) {
	var global = require('ui/common/globals');
	var self = Ti.UI.createWindow({
		title:title,
		barColor: global.colors.dark,
		backgroundColor: global.colors.lightest
	});

	// User Image field - upper left of view
	// Will be populated with image data from user's account in database.
	var image = Ti.UI.createImageView({

  		height: 100,
  		width: 100,
  		top: 10,
  		left: 10,
  		borderColor: 'black',
		borderWidth: 1,
  		contentMode: 'aspectfill',
  		clipsToBounds: false,
  		image:'/images/user_image.png',
	});
	self.add(image)

	// NEW TEST TEXT FIELD
	var userFullName = Ti.UI.createLabel({
  		color: global.colors.dark,
  		font: { fontSize:28 },
  		//shadowColor: '#aaa',
  		//shadowOffset: {x:5, y:5},
  		text: 'User Name',
  		textAlign: Ti.UI.TEXT_ALIGNMENT_CENTER,
  		top: 10,
  		right: 10,
 		width: 'auto',
 		height: 25,
	});
	self.add(userFullName)

	var aboutMe = Ti.UI.createLabel({
  		color: global.colors.dark,
  		font: { fontSize:18 },
  		//shadowColor: '#aaa',
  		//shadowOffset: {x:5, y:5},
  		text: 'About Me',
  		textAlign: Ti.UI.TEXT_ALIGNMENT_CENTER,
  		top: 40,
  		right: 10,
 		width: 'auto',
 		//height: 'auto',
	});
	self.add(aboutMe)

	var aboutMe_Text = Ti.UI.createLabel({
  		color: global.colors.dark,
  		font: { fontSize:12 },
  		//shadowColor: '#aaa',
  		//shadowOffset: {x:5, y:5},
  		text: 'About me text goes here... ',
  		textAlign: Ti.UI.TEXT_ALIGNMENT_CENTER,
  		top: 60,
  		right: 10,
 		width: 'auto',
 		height: 'auto',
	});
	self.add(aboutMe_Text)

	return self;
};

module.exports = ProfileWindow;<|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: 'foam.ui',
  name:  'TextFieldView',
  label: 'Text Field',

  extendsModel: 'foam.ui.SimpleView',

  requires: ['foam.ui.AutocompleteView'],

  documentation: function() { /*
      The default $$DOC{ref:'foam.ui.View'} for a string. Supports autocomplete
      when an autocompleter is installed in $$DOC{ref:'.autocompleter'}.
  */},

  models: [
    {
      name: 'OnEnterValue',
      properties: [
        {
          name: 'element',
        },
        {
          name: 'listeners',
          factory: function() {
            return [];
          }
        }
      ],
      methods: [
        function get() { return this.element.value; },
        function set(value) {
          if ( this.get() !== value ) this.element.value = value;
        },
        function addListener(listener) {
          if ( ! listener ) return;
          if ( this.listeners.length === 0 )
            this.element.addEventListener('keydown', this.onKeyDown);
          this.listeners.push(listener);
        },
        function removeListener(listener) {
          var index = this.listeners.indexOf(listener);
          if ( index >= 0 ) this.listeners.splice(i, 1);
        },
        function fireListeners(e) {
          for (var i = 0; i < this.listeners.length; i++) {
            this.listeners[i](e);
          }
        }
      ],
      listeners: [
        {
          name: 'onKeyDown',
          code: function(e) {
            if ( e.keyCode === 13 ) {
              this.fireListeners(e);
            }
          }
        }
      ]
    }
  ],

  properties: [
    {
      model_: 'StringProperty',
      name: 'name',
      defaultValue: 'field',
      documentation: function() { /* The name of the field. */}
    },
    {
      model_: 'IntProperty',
      name: 'displayWidth',
      defaultValue: 30,
      documentation: function() { /* The width to fix the HTML text box. */}
    },
    {
      model_: 'IntProperty',
      name: 'displayHeight',
      defaultValue: 1,
      documentation: function() { /* The height to fix the HTML text box. */}
    },
    {
      model_: 'StringProperty',
      name: 'type',
      defaultValue: 'text',
      documentation: function() { /* The type of field to create. */}
    },
    {
      model_: 'StringProperty',
      name: 'placeholder',
      defaultValue: undefined,
      documentation: function() { /* Placeholder to use when empty. */}
    },
    {
      model_: 'BooleanProperty',
      name: 'onKeyMode',
      help: 'If true, value is updated on each keystroke.',
      documentation: function() { /* If true, value is updated on each keystroke. */},
      getter: function() {
        return this.updateMode === this.EACH_KEYSTROKE;
      },
      setter: function(nu) {
        this.updateMode = nu ? this.EACH_KEYSTROKE : this.DONE_EDITING;
      }
    },
    {
      model_: 'foam.core.types.StringEnumProperty',
      name: 'updateMode',
      help: 'Controls when the real .data is updated: on every keystroke, ' +
          'when the user presses enter or blurs the box, or on enter only.',
      defaultValue: 'DONE_EDITING',
      choices: [
        ['DONE_EDITING', 'Done editing'],
        ['EACH_KEYSTROKE', 'Every keystroke'],
        ['ENTER_ONLY', 'Enter only']
      ]
    },
    {
      model_: 'BooleanProperty',
      name: 'escapeHTML',
      defaultValue: true,
      // TODO: make the default 'true' for security reasons
      help: 'If true, HTML content is escaped in display mode.',
      documentation: function() { /* If true, HTML content is escaped in display mode. */}
    },
    {
      model_: 'StringProperty',
      name: 'mode',
      defaultValue: 'read-write',
      view: { factory_: 'foam.ui.ChoiceView', choices: ['read-only', 'read-write', 'final'] },
      documentation: function() { /* Can be 'read-only', 'read-write' or 'final'. */}
    },
    {
      model_: 'BooleanProperty',
      name: 'required',
      documentation: 'If value is required.'
    },
    {
      model_: 'StringProperty',
      name: 'pattern',
      documentation: 'Regex pattern for value.'
    },
    {
      name: 'domValue',
      hidden: true
    },
    {
      name: 'data',
      documentation: function() { /* The object to bind to the user's entered text. */}
    },
    {
      model_: 'StringProperty',
      name: 'readWriteTagName',
      defaultValueFn: function() {
        return this.displayHeight === 1 ? 'input' : 'textarea';
      },
      hidden: true
    },
    {
      model_: 'BooleanProperty',
      name: 'autocomplete',
      defaultValue: true,
      documentation: function() { /* Set to true to enable autocomplete. */}
    },
    {
      name: 'autocompleter',
      documentation: function() { /* The autocompleter model to use. */}
    },
    {
      name: 'autocompleteView',
      documentation: function() { /* The autocomplete view created. */}
    }
  ],

  constants: {
    /** Escape topic published when user presses 'escape' key to abort edits. **/
    // TODO: Model as a 'Topic'
    ESCAPE: ['escape'],

    // These are the constants used by the updateMode. The text of these is
    // duplicated in the choices array of the updateMode property.
    // TODO(braden): That duplication sucks, we need a better way to handle
    // enums.
    DONE_EDITING: 'DONE_EDITING',
    EACH_KEYSTROKE: 'EACH_KEYSTROKE',
    ENTER_ONLY: 'ENTER_ONLY'
  },

  methods: {
    toHTML: function() {
      /* Selects read-only versus read-write DOM output */
      return this.mode === 'read-write' ?
        this.toReadWriteHTML() :
        this.toReadOnlyHTML()  ;
    },

    toReadWriteHTML: function() {
      /* Supplies the correct element for read-write mode */
      var str = '<' + this.readWriteTagName + ' id="' + this.id + '"';
      str += ' type="' + this.type + '" ' + this.cssClassAttr();

      this.on('click', this.onClick, this.id);

      str += this.readWriteTagName === 'input' ?
        ' size="' + this.displayWidth + '"' :
        ' rows="' + this.displayHeight + '" cols="' + this.displayWidth + '"';

      if ( this.required ) str += ' required';
      if ( this.pattern  ) str += ' pattern="' + this.pattern + '"';

      str += this.extraAttributes();

      str += ' name="' + this.name + '">';
      str += '</' + this.readWriteTagName + '>';
      return str;
    },

    extraAttributes: function() { return ''; },

    toReadOnlyHTML: function() {
      /* Supplies the correct element for read-only mode */
      var self = this;
      this.setClass('placeholder', function() { return self.data === ''; }, this.id);
      return '<' + this.tagName + ' id="' + this.id + '"' + this.cssClassAttr() + ' name="' + this.name + '"></' + this.tagName + '>';
    },

    setupAutocomplete: function() {
      /* Initializes autocomplete, if $$DOC{ref:'.autocomplete'} and
        $$DOC{ref:'.autocompleter'} are set. */
      if ( ! this.autocomplete || ! this.autocompleter ) return;

      var view = this.autocompleteView = this.AutocompleteView.create({
        autocompleter: this.autocompleter,
        target: this
      });

      this.bindAutocompleteEvents(view);
    },

    onAutocomplete: function(data) {
      this.data = data;
    },

    bindAutocompleteEvents: function(view) {
      this.$.addEventListener('blur', function() {
        // Notify the autocomplete view of a blur, it can decide what to do from there.
        view.publish('blur');
      });
      this.$.addEventListener('input', (function() {
        view.autocomplete(this.textToValue(this.$.value));
      }).bind(this));
      this.$.addEventListener('focus', (function() {
        view.autocomplete(this.textToValue(this.$.value));
      }).bind(this));
    },

    initHTML: function() {
      if ( ! this.$ ) return;

      this.SUPER();

      if ( this.mode === 'read-write' ) {
        if ( this.placeholder ) this.$.placeholder = this.placeholder;

        if ( this.updateMode === this.EACH_KEYSTROKE ) {
          this.domValue = DomValue.create(this.$, 'input');
        } else if ( this.updateMode === this.DONE_EDITING ) {
          this.domValue = DomValue.create(this.$, 'change');
        } else {
          this.domValue = this.OnEnterValue.create({ element: this.$ });
        }

        // In KeyMode we disable feedback to avoid updating the field
        // while the user is still typing.  Then we update the view
        // once they leave(blur) the field.
        Events.relate(
          this.data$,
          this.domValue,
          this.valueToText.bind(this),
          this.textToValue.bind(this),
          this.updateMode === this.EACH_KEYSTROKE);

        if ( this.updateMode === this.EACH_KEYSTROKE )
          this.$.addEventListener('blur', this.onBlur);

        this.$.addEventListener('keydown', this.onKeyDown);

        this.setupAutocomplete();
      } else {
        this.domValue = DomValue.create(
          this.$,
          'undefined',
          this.escapeHTML ? 'textContent' : 'innerHTML');

        Events.map(
          this.data$,
          this.domValue,
          this.valueToText.bind(this))
      }
    },

    textToValue: function(text) { /* Passthrough */ return text; },

    valueToText: function(value) { /* Filters for read-only mode */
      if ( this.mode === 'read-only' )
        return (value === '') ? this.placeholder : value;
      return value;
    },

    destroy: function( isParentDestroyed ) { /* Unlinks key handler. */
      this.SUPER(isParentDestroyed);
      Events.unlink(this.domValue, this.data$);
    }
  },

  listeners: [
    {
      name: 'onKeyDown',
      code: function(e) {
        if ( e.keyCode == 27 /* ESCAPE KEY */ ) {
          this.domValue.set(this.data);
          this.publish(this.ESCAPE);
        } else {
          this.publish(['keydown'], e);
        }
      }
    },
    {
      name: 'onBlur',
      code: function(e) {
        if ( this.domValue.get() !== this.data )
          this.domValue.set(this.data);
      }
    },
    {
      name: 'onClick',
      code: function(e) { this.$ && this.$.focus(); }
    },
  ]
});

<commit_msg>Fix TextFieldView to display as a textarea when in read-only mode.<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: 'foam.ui',
  name:  'TextFieldView',
  label: 'Text Field',

  extendsModel: 'foam.ui.SimpleView',

  requires: [ 'foam.ui.AutocompleteView' ],

  documentation: function() { /*
      The default $$DOC{ref:'foam.ui.View'} for a string. Supports autocomplete
      when an autocompleter is installed in $$DOC{ref:'.autocompleter'}.
  */},

  models: [
    {
      name: 'OnEnterValue',
      properties: [
        {
          name: 'element',
        },
        {
          name: 'listeners',
          factory: function() {
            return [];
          }
        }
      ],
      methods: [
        function get() { return this.element.value; },
        function set(value) {
          if ( this.get() !== value ) this.element.value = value;
        },
        function addListener(listener) {
          if ( ! listener ) return;
          if ( this.listeners.length === 0 )
            this.element.addEventListener('keydown', this.onKeyDown);
          this.listeners.push(listener);
        },
        function removeListener(listener) {
          var index = this.listeners.indexOf(listener);
          if ( index >= 0 ) this.listeners.splice(i, 1);
        },
        function fireListeners(e) {
          for (var i = 0; i < this.listeners.length; i++) {
            this.listeners[i](e);
          }
        }
      ],
      listeners: [
        {
          name: 'onKeyDown',
          code: function(e) {
            if ( e.keyCode === 13 ) {
              this.fireListeners(e);
            }
          }
        }
      ]
    }
  ],

  properties: [
    {
      model_: 'StringProperty',
      name: 'name',
      defaultValue: 'field',
      documentation: function() { /* The name of the field. */}
    },
    {
      model_: 'IntProperty',
      name: 'displayWidth',
      defaultValue: 30,
      documentation: function() { /* The width to fix the HTML text box. */}
    },
    {
      model_: 'IntProperty',
      name: 'displayHeight',
      defaultValue: 1,
      documentation: function() { /* The height to fix the HTML text box. */}
    },
    {
      model_: 'StringProperty',
      name: 'type',
      defaultValue: 'text',
      documentation: function() { /* The type of field to create. */}
    },
    {
      model_: 'StringProperty',
      name: 'placeholder',
      defaultValue: undefined,
      documentation: function() { /* Placeholder to use when empty. */}
    },
    {
      model_: 'BooleanProperty',
      name: 'onKeyMode',
      help: 'If true, value is updated on each keystroke.',
      documentation: function() { /* If true, value is updated on each keystroke. */},
      getter: function() {
        return this.updateMode === this.EACH_KEYSTROKE;
      },
      setter: function(nu) {
        this.updateMode = nu ? this.EACH_KEYSTROKE : this.DONE_EDITING;
      }
    },
    {
      model_: 'foam.core.types.StringEnumProperty',
      name: 'updateMode',
      help: 'Controls when the real .data is updated: on every keystroke, ' +
          'when the user presses enter or blurs the box, or on enter only.',
      defaultValue: 'DONE_EDITING',
      choices: [
        ['DONE_EDITING', 'Done editing'],
        ['EACH_KEYSTROKE', 'Every keystroke'],
        ['ENTER_ONLY', 'Enter only']
      ]
    },
    {
      model_: 'BooleanProperty',
      name: 'escapeHTML',
      defaultValue: true,
      // TODO: make the default 'true' for security reasons
      help: 'If true, HTML content is escaped in display mode.',
      documentation: function() { /* If true, HTML content is escaped in display mode. */}
    },
    {
      model_: 'StringProperty',
      name: 'mode',
      defaultValue: 'read-write',
      view: { factory_: 'foam.ui.ChoiceView', choices: ['read-only', 'read-write', 'final'] },
      documentation: function() { /* Can be 'read-only', 'read-write' or 'final'. */}
    },
    {
      model_: 'BooleanProperty',
      name: 'required',
      documentation: 'If value is required.'
    },
    {
      model_: 'StringProperty',
      name: 'pattern',
      documentation: 'Regex pattern for value.'
    },
    {
      name: 'domValue',
      hidden: true
    },
    {
      name: 'data',
      documentation: function() { /* The object to bind to the user's entered text. */}
    },
    {
      model_: 'StringProperty',
      name: 'readWriteTagName',
      defaultValueFn: function() {
        return this.displayHeight === 1 ? 'input' : 'textarea';
      },
      hidden: true
    },
    {
      model_: 'BooleanProperty',
      name: 'autocomplete',
      defaultValue: true,
      documentation: function() { /* Set to true to enable autocomplete. */}
    },
    {
      name: 'autocompleter',
      documentation: function() { /* The autocompleter model to use. */}
    },
    {
      name: 'autocompleteView',
      documentation: function() { /* The autocomplete view created. */}
    }
  ],

  constants: {
    /** Escape topic published when user presses 'escape' key to abort edits. **/
    // TODO: Model as a 'Topic'
    ESCAPE: ['escape'],

    // These are the constants used by the updateMode. The text of these is
    // duplicated in the choices array of the updateMode property.
    // TODO(braden): That duplication sucks, we need a better way to handle
    // enums.
    DONE_EDITING: 'DONE_EDITING',
    EACH_KEYSTROKE: 'EACH_KEYSTROKE',
    ENTER_ONLY: 'ENTER_ONLY'
  },

  methods: {
    toHTML: function() {
      /* Selects read-only versus read-write DOM output */
      return this.mode === 'read-write' ?
        this.toReadWriteHTML() :
        this.toReadOnlyHTML()  ;
    },

    toReadWriteHTML: function() {
      /* Supplies the correct element for read-write mode */
      var str = '<' + this.readWriteTagName + ' id="' + this.id + '"';
      str += ' type="' + this.type + '" ' + this.cssClassAttr();

      this.on('click', this.onClick, this.id);

      str += this.readWriteTagName === 'input' ?
        ' size="' + this.displayWidth + '"' :
        ' rows="' + this.displayHeight + '" cols="' + this.displayWidth + '"';

      if ( this.required ) str += ' required';
      if ( this.pattern  ) str += ' pattern="' + this.pattern + '"';

      str += this.extraAttributes();

      str += ' name="' + this.name + '">';
      str += '</' + this.readWriteTagName + '>';
      return str;
    },

    extraAttributes: function() { return ''; },

    toReadOnlyHTML: function() {
      /* Supplies the correct element for read-only mode */
      var self = this;
      this.setClass('placeholder', function() { return self.data === ''; }, this.id);

      return this.displayHeight === 1 ?
        '<' + this.tagName + ' id="' + this.id + '"' + this.cssClassAttr() + ' name="' + this.name + '"></' + this.tagName + '>' :
        '<textarea displayonly id="' + this.id + '"' + this.cssClassAttr() + ' name="' + this.name + '"></textarea>' ;
    },

    setupAutocomplete: function() {
      /* Initializes autocomplete, if $$DOC{ref:'.autocomplete'} and
        $$DOC{ref:'.autocompleter'} are set. */
      if ( ! this.autocomplete || ! this.autocompleter ) return;

      var view = this.autocompleteView = this.AutocompleteView.create({
        autocompleter: this.autocompleter,
        target: this
      });

      this.bindAutocompleteEvents(view);
    },

    onAutocomplete: function(data) {
      this.data = data;
    },

    bindAutocompleteEvents: function(view) {
      this.$.addEventListener('blur', function() {
        // Notify the autocomplete view of a blur, it can decide what to do from there.
        view.publish('blur');
      });
      this.$.addEventListener('input', (function() {
        view.autocomplete(this.textToValue(this.$.value));
      }).bind(this));
      this.$.addEventListener('focus', (function() {
        view.autocomplete(this.textToValue(this.$.value));
      }).bind(this));
    },

    initHTML: function() {
      if ( ! this.$ ) return;

      this.SUPER();

      if ( this.mode === 'read-write' ) {
        if ( this.placeholder ) this.$.placeholder = this.placeholder;

        if ( this.updateMode === this.EACH_KEYSTROKE ) {
          this.domValue = DomValue.create(this.$, 'input');
        } else if ( this.updateMode === this.DONE_EDITING ) {
          this.domValue = DomValue.create(this.$, 'change');
        } else {
          this.domValue = this.OnEnterValue.create({ element: this.$ });
        }

        // In KeyMode we disable feedback to avoid updating the field
        // while the user is still typing.  Then we update the view
        // once they leave(blur) the field.
        Events.relate(
          this.data$,
          this.domValue,
          this.valueToText.bind(this),
          this.textToValue.bind(this),
          this.updateMode === this.EACH_KEYSTROKE);

        if ( this.updateMode === this.EACH_KEYSTROKE )
          this.$.addEventListener('blur', this.onBlur);

        this.$.addEventListener('keydown', this.onKeyDown);

        this.setupAutocomplete();
      } else {
        this.domValue = DomValue.create(
          this.$,
          'undefined',
          this.escapeHTML ? 'textContent' : 'innerHTML');

        Events.map(
          this.data$,
          this.domValue,
          this.valueToText.bind(this))
      }
    },

    textToValue: function(text) { /* Passthrough */ return text; },

    valueToText: function(value) { /* Filters for read-only mode */
      if ( this.mode === 'read-only' )
        return (value === '') ? this.placeholder : value;
      return value;
    },

    destroy: function( isParentDestroyed ) { /* Unlinks key handler. */
      this.SUPER(isParentDestroyed);
      Events.unlink(this.domValue, this.data$);
    }
  },

  listeners: [
    {
      name: 'onKeyDown',
      code: function(e) {
        if ( e.keyCode == 27 /* ESCAPE KEY */ ) {
          this.domValue.set(this.data);
          this.publish(this.ESCAPE);
        } else {
          this.publish(['keydown'], e);
        }
      }
    },
    {
      name: 'onBlur',
      code: function(e) {
        if ( this.domValue.get() !== this.data )
          this.domValue.set(this.data);
      }
    },
    {
      name: 'onClick',
      code: function(e) { this.$ && this.$.focus(); }
    },
  ]
});

<|endoftext|>


<commit_before><commit_msg>No browser GUI for verison 1.2<commit_after><|endoftext|>