<commit_before>/// <reference path="../ext/ext-core-debug.js"/>
/// <reference path="Application.js"/>
/// <reference path="View.js"/>

Ext.namespace('Sage.Platform.Mobile');

Sage.Platform.Mobile.List = Ext.extend(Sage.Platform.Mobile.View, {
    viewTemplate: new Simplate([
        '
',
        '
'
    ]),
    searchTemplate: new Simplate([
      '
',
        '<form>',
          '<fieldset>',
            '<input type="text" name="query" class="query" />',
            '{%= $.cancelText %}',
            '{%= $.searchText %}',
            '{%= $.searchText %}',
          '</fieldset>',
        '</form>',
      '
'
    ]),
    loadingTemplate: new Simplate([
        '

{%= loadingText %}

',
        '
{%= moreText %}
'
    ]),
    itemTemplate: new Simplate([
        '
',
        '{% if (this.allowSelection) { %}',
        '<input type="checkbox" m:key="{%= $["$key"] %}" class="list-selector" />',
        '{% } %}',
        '{%! this.contentTemplate %}',
        '
'
    ]),
    contentTemplate: new Simplate([
        '
{%= $["$descriptor"] %}
'
    ]),
    noDataTemplate: new Simplate([
        '
',
        '
{%= noDataText %}
',
        '
'
    ]),
    moreText: 'More',
    titleText: 'List',
    searchText: 'Search',
    cancelText: 'Cancel',
    insertText: 'New',
    noDataText: 'no records',
    loadingText: 'loading...',
    customSearchRE: /^\#\!/,
    constructor: function(o) {
        /// <field name="resourceKind" type="String">The resource kind that is bound to this view.</field>
        /// <field name="pageSize" type="Number">The number of records to return with each request.</field>
        /// <field name="requestedFirstPage" type="Boolean">True if the first page has been request; False otherwise.<field>
        /// <field name="noDataText" type="String">A message to display when there is no data.</field>
        /// <field name="contentTemplate" type="Simplate">A template used to render the initial content of the view.</field>
        /// <field name="itemTemplate" type="Simplate">
        ///     A template used to render each resource feed entry.  This template is rendered and then applied to the DOM
        ///     before the "li.more" element.
        /// </field>
        /// <field name="noDataTemplate" type="Simplate">A template used to render the no data message.</field>
        Sage.Platform.Mobile.List.superclass.constructor.call(this);

        Ext.apply(this, o, {
            id: 'generic_list',
            title: this.titleText,
            pageSize: 20,
            allowSelection: false,
            requestedFirstPage: false,
            contextDialog: 'context_dialog',
            tools: {
                tbar: [{
                    name: 'New',
                    title: this.insertText,
                    fn: this.navigateToInsert,
                    cls: "button",
                    scope: this
                }]
            }
        });
    },
    render: function() {
        Sage.Platform.Mobile.List.superclass.render.call(this);

        this.clear();
    },
    init: function() {
        Sage.Platform.Mobile.List.superclass.init.call(this);

        this.el.on('click', this.onClick, this);
        this.el.on('clicklong', this.onClickLong, this);

        App.on('refresh', this.onRefresh, this);
    },
    getSelected: function() {
        var checked = [];
        this.el.select("input.list-selector:checked").each(function(el) {
            checked.push(el.getAttribute("m:key"));
        }, this);
        return checked;
    },
    onClickLong: function(evt, el, o) {
        var resourceKind = this.resourceKind;
        if (!/^(accounts)|(contacts)|(opportunities)|(leads)|(tickets)$/.test(resourceKind)) {
            this.onClick(evt, el, o);
            return;
        }
        App.getView(this.contextDialog).show(el);
    },
    onClick: function(evt, el, o) {
        var el = Ext.get(el);
        if (el.is('.more') || el.up('.more'))
        {
            evt.stopEvent();

            this.more();
            return;
        }
        else if (el.is('.dismissButton')) {
          evt.stopEvent();
          if (this.searchEl.dom.value != "") {
              this.searchEl.dom.value = "";
          }
          Ext.get(el).hide();
          return;
        }
        else if (el.is('.searchButton')) {
          evt.stopEvent();
          this.search();
          return;
        }

        var link = el;
        if (link.is('a[target="_detail"]') || (link = link.up('a[target="_detail"]')))
        {
            evt.stopEvent();

            var view = link.dom.hash.substring(1);

            var key = link.getAttribute("key", "m");
            var descriptor = link.getAttribute("descriptor", "m");

            this.navigateToDetail(view, key, descriptor);
            return;
        }
    },
    onRefresh: function(o) {
        if (this.resourceKind && o.resourceKind === this.resourceKind)
        {
            this.refreshRequired = true;
        }
    },
    search: function() {
        ///
        ///     Called when a new search is activated.  This method sets up the SData query, clears the content
        ///     of the view, and fires a request for updated data.
        ///
        /// <param name="searchText" type="String">The search query.</param>
        this.clear();
        this.queryText = this.searchEl.dom.value.length > 0 ? this.searchEl.dom.value : false;

        var formatQuery = function (queryText, context) {
            if (context.customSearchRE.test(queryText)) {
                return queryText.replace(context.customSearchRE, "");
            }
            return context.formatSearchQuery(queryText);
        };
        this.query = this.queryText !== false ? formatQuery(this.queryText, this)
                                              : false;
        this.requestData();
    },
    formatSearchQuery: function(query) {
        ///
        ///     Called to transform a textual query into an SData query compatible search expression.
        ///
        /// <returns type="String">An SData query compatible search expression.</returns>
        return false;
    },
    createRequest:function() {
        ///
        ///     Creates SDataResourceCollectionRequest instance and sets a number of known properties.
        ///
        /// <returns type="Sage.SData.Client.SDataResourceCollectionRequest">An SDataResourceCollectionRequest instance.<returns>
        var pageSize = this.pageSize;
        var startIndex = this.feed && this.feed['$startIndex'] > 0 && this.feed['$itemsPerPage'] > 0
            ? this.feed['$startIndex'] + this.feed['$itemsPerPage']
            : 1;

        var request = new Sage.SData.Client.SDataResourceCollectionRequest(this.getService())
            .setCount(pageSize)
            .setStartIndex(startIndex);

        if (this.resourceKind)
            request.setResourceKind(this.resourceKind);

        if (this.resourceProperty)
            request
                .getUri()
                .setPathSegment(Sage.SData.Client.SDataUri.ResourcePropertyIndex, this.resourceProperty);

        var where = [];

        if (this.context)
        {
            var resourceKindExpr = this.expandExpression(this.context.resourceKind);
            if (resourceKindExpr)
                request.setResourceKind(resourceKindExpr);

            var resourcePredicateExpr = this.expandExpression(this.context.resourcePredicate);
            if (resourcePredicateExpr)
                request
                    .getUri()
                    .setCollectionPredicate(resourcePredicateExpr);

            var whereExpr = this.expandExpression(this.context.where);
            if (whereExpr)
                where.push(whereExpr);
        }

        if (this.query)
            where.push(this.query);

        if (where.length > 0)
            request.setQueryArgs({
                'where': where.join(' and ')
            });

        return request;
    },
    navigateToDetail: function(view, key, descriptor) {
        ///
        ///     Navigates to the requested detail view.
        ///
        /// <param name="el" type="Ext.Element">The element that initiated the navigation.</param>
        var v = App.getView(view);
        if (v)
            v.show({
                descriptor: descriptor,
                key: key
            });
    },
    navigateToInsert: function() {
        var view = App.getView(this.editor);
        if (view)
            view.show({insert: true});
    },
    processFeed: function(feed) {
        ///
        ///     Processes the feed result from the SData request and renders out the resource feed entries.
        ///
        /// <param name="feed" type="Object">The feed object.</param>
        if (this.requestedFirstPage == false)
        {
            this.requestedFirstPage = true;
            this.el
                .select('.loading')
                .remove();
            //Insert Search bar first
            Ext.DomHelper.insertFirst(this.el, this.searchTemplate.apply(this));
            //Set Form submit handler. Give the <form> some time to get attached to dom.
            (function(){
              this.el
                  .child('form')
                  .on('submit', function(evt, el, o) {
                      return false;
                  }, this, { preventDefault: true, stopPropagation: true })
                  .dom.onsubmit = false; // fix for iui shenanigans
              this.searchEl = this.el.child('input.query');
              this.searchEl.dom.value = this.queryText === false ? "" : this.queryText;
              this.setSearchLabelVisibility();
              this.el.child('input.query')
                  .on('keypress', function(evt, el, o) {
                      if (evt.getKey() == 13 || evt.getKey() == 10)
                      {
                          evt.stopEvent();

                          /* fix to hide iphone keyboard when go is pressed */
                          if (/(iphone|ipad)/i.test(navigator.userAgent))
                              Ext.get('backButton').focus();
                          this.search();
                      }
                  }, this)
                  .on('keyup', function(evt, el, o) {
                      this.setSearchLabelVisibility();
                  }, this);
            }).defer(200, this);

        }

        this.feed = feed;

        if (this.feed['$totalResults'] === 0)
        {
            Ext.DomHelper.insertBefore(this.moreEl, this.noDataTemplate.apply(this));
        }
        else
        {
            var o = [];

            for (var i = 0; i < feed.$resources.length; i++)
                o.push(this.itemTemplate.apply(feed.$resources[i], this));

            if (o.length > 0)
                Ext.DomHelper.insertBefore(this.moreEl, o.join(''));
        }

        this.moreEl
            .removeClass('more-loading');

        if (this.hasMoreData())
            this.moreEl.show();
        else
            this.moreEl.hide();
    },
    setSearchLabelVisibility: function() {
      if (this.searchEl.dom.value == "") {
          this.el.select('.dismissButton').hide();
          this.el.select('label').show();
      }
      else {
          this.el.select('.dismissButton').show();
          this.el.select('label').hide();
      }
    },
    hasMoreData: function() {
        ///
        ///     Deterimines if there is more data to be shown by inspecting the last feed result.
        ///
        /// <returns type="Boolean">True if the feed has more data; False otherwise.</returns>
        if (this.feed['$startIndex'] > 0 && this.feed['$itemsPerPage'] > 0 && this.feed['$totalResults'] >= 0)
        {
            var start = this.feed['$startIndex'];
            var count = this.feed['$itemsPerPage'];
            var total = this.feed['$totalResults'];

            return (start + count <= total);
        }
        else
        {
            return true; // no way to determine, always assume more data
        }
    },
    requestFailure: function(response, o) {
        ///
        ///     Called when an error occurs while request data from the SData endpoint.
        ///
        /// <param name="response" type="Object">The response object.</param>
        /// <param name="o" type="Object">The options that were passed to Ext when creating the Ajax request.</param>
    },
    requestData: function() {
        ///
        ///     Initiates the SData request.
        ///
        var request = this.createRequest();
        request.read({
            success: function(feed) {
                this.processFeed(feed);
            },
            failure: function(response, o) {
                this.requestFailure(response, o);
            },
            scope: this
        });
    },
    more: function() {
        ///
        ///     Called when the more button is clicked.
        ///
        this.moreEl.addClass('more-loading');
        this.requestData();
    },
    expandExpression: function(expression) {
        ///
        ///     Expands the passed expression if it is a function.
        ///
        /// <param name="expression" type="String">
        ///     1: function - Called on this object and must return a string.
        ///     2: string - Returned directly.
        /// </param>
        if (typeof expression === 'function')
            return expression.call(this);
        else
            return expression;
    },
    refreshRequiredFor: function(options) {
        if (this.options)
        {
            if (options)
            {
                if (this.expandExpression(this.options.where) != this.expandExpression(options.where)) return true;
                if (this.expandExpression(this.options.resourceKind) != this.expandExpression(options.resourceKind)) return true;
                if (this.expandExpression(this.options.resourcePredicate) != this.expandExpression(options.resourcePredicate)) return true;
            }

            return false;
        }
        else
        {
            return true;
        }
    },
    getContext: function() {
        return Ext.apply(Sage.Platform.Mobile.List.superclass.getContext.call(this), {
            resourceKind: this.resourceKind
        });
    },
    beforeTransitionTo: function() {
        Sage.Platform.Mobile.List.superclass.beforeTransitionTo.call(this);

        if (this.refreshRequired) this.clear();
    },
    refresh: function() {
        this.requestData();
    },
    transitionTo: function() {
        Sage.Platform.Mobile.List.superclass.transitionTo.call(this);

    },
    show: function(options) {
        Sage.Platform.Mobile.Detail.superclass.show.apply(this, arguments);

        if (this.searchEl && this.searchEl.dom.value == "") {
            this.el.select('.dismissButton').hide();
            this.el.select('label').show();
        }
        else {
            this.el.select('.dismissButton').show();
            this.el.select('label').hide();
         }
    },
    clear: function() {
        ///
        ///     Clears the view and re-applies the default content template.
        ///
        this.el.update(this.loadingTemplate.apply(this));

        this.moreEl = this.el.down(".more");

        this.requestedFirstPage = false;
        this.feed = false;
        this.query = false;
        this.queryText = false;
    }
});<commit_msg>[#4208405] Refactored List.js search event handler setup.<commit_after>/// <reference path="../ext/ext-core-debug.js"/>
/// <reference path="Application.js"/>
/// <reference path="View.js"/>

Ext.namespace('Sage.Platform.Mobile');

Sage.Platform.Mobile.List = Ext.extend(Sage.Platform.Mobile.View, {
    viewTemplate: new Simplate([
        '
',
        '
'
    ]),
    searchTemplate: new Simplate([
      '
',
        '<form>',
          '<fieldset>',
            '<input type="text" name="query" class="query" />',
            '{%= $.cancelText %}',
            '{%= $.searchText %}',
            '{%= $.searchText %}',
          '</fieldset>',
        '</form>',
      '
'
    ]),
    loadingTemplate: new Simplate([
        '

{%= loadingText %}

',
        '
{%= moreText %}
'
    ]),
    itemTemplate: new Simplate([
        '
',
        '{% if (this.allowSelection) { %}',
        '<input type="checkbox" m:key="{%= $["$key"] %}" class="list-selector" />',
        '{% } %}',
        '{%! this.contentTemplate %}',
        '
'
    ]),
    contentTemplate: new Simplate([
        '
{%= $["$descriptor"] %}
'
    ]),
    noDataTemplate: new Simplate([
        '
',
        '
{%= noDataText %}
',
        '
'
    ]),
    moreText: 'More',
    titleText: 'List',
    searchText: 'Search',
    cancelText: 'Cancel',
    insertText: 'New',
    noDataText: 'no records',
    loadingText: 'loading...',
    customSearchRE: /^\#\!/,
    constructor: function(o) {
        /// <field name="resourceKind" type="String">The resource kind that is bound to this view.</field>
        /// <field name="pageSize" type="Number">The number of records to return with each request.</field>
        /// <field name="requestedFirstPage" type="Boolean">True if the first page has been request; False otherwise.<field>
        /// <field name="noDataText" type="String">A message to display when there is no data.</field>
        /// <field name="contentTemplate" type="Simplate">A template used to render the initial content of the view.</field>
        /// <field name="itemTemplate" type="Simplate">
        ///     A template used to render each resource feed entry.  This template is rendered and then applied to the DOM
        ///     before the "li.more" element.
        /// </field>
        /// <field name="noDataTemplate" type="Simplate">A template used to render the no data message.</field>
        Sage.Platform.Mobile.List.superclass.constructor.call(this);

        Ext.apply(this, o, {
            id: 'generic_list',
            title: this.titleText,
            pageSize: 20,
            allowSelection: false,
            requestedFirstPage: false,
            contextDialog: 'context_dialog',
            tools: {
                tbar: [{
                    name: 'New',
                    title: this.insertText,
                    fn: this.navigateToInsert,
                    cls: "button",
                    scope: this
                }]
            }
        });
    },
    render: function() {
        Sage.Platform.Mobile.List.superclass.render.call(this);

        this.clear();
    },
    init: function() {
        Sage.Platform.Mobile.List.superclass.init.call(this);

        this.el.on('click', this.onClick, this);
        this.el.on('clicklong', this.onClickLong, this);

        App.on('refresh', this.onRefresh, this);
    },
    getSelected: function() {
        var checked = [];
        this.el.select("input.list-selector:checked").each(function(el) {
            checked.push(el.getAttribute("m:key"));
        }, this);
        return checked;
    },
    onClickLong: function(evt, el, o) {
        var resourceKind = this.resourceKind;
        if (!/^(accounts)|(contacts)|(opportunities)|(leads)|(tickets)$/.test(resourceKind)) {
            this.onClick(evt, el, o);
            return;
        }
        App.getView(this.contextDialog).show(el);
    },
    onClick: function(evt, el, o) {
        var el = Ext.get(el);
        if (el.is('.more') || el.up('.more'))
        {
            evt.stopEvent();

            this.more();
            return;
        }
        else if (el.is('.dismissButton')) {
          evt.stopEvent();
          if (this.searchEl.dom.value != "") {
              this.searchEl.dom.value = "";
          }
          Ext.get(el).hide();
          return;
        }
        else if (el.is('.searchButton')) {
          evt.stopEvent();
          this.search();
          return;
        }

        var link = el;
        if (link.is('a[target="_detail"]') || (link = link.up('a[target="_detail"]')))
        {
            evt.stopEvent();

            var view = link.dom.hash.substring(1);

            var key = link.getAttribute("key", "m");
            var descriptor = link.getAttribute("descriptor", "m");

            this.navigateToDetail(view, key, descriptor);
            return;
        }
    },
    onRefresh: function(o) {
        if (this.resourceKind && o.resourceKind === this.resourceKind)
        {
            this.refreshRequired = true;
        }
    },
    search: function() {
        ///
        ///     Called when a new search is activated.  This method sets up the SData query, clears the content
        ///     of the view, and fires a request for updated data.
        ///
        /// <param name="searchText" type="String">The search query.</param>
        this.clear();
        this.queryText = this.searchEl.dom.value.length > 0 ? this.searchEl.dom.value : false;

        var formatQuery = function (queryText, context) {
            if (context.customSearchRE.test(queryText)) {
                return queryText.replace(context.customSearchRE, "");
            }
            return context.formatSearchQuery(queryText);
        };
        this.query = this.queryText !== false ? formatQuery(this.queryText, this)
                                              : false;
        this.requestData();
    },
    formatSearchQuery: function(query) {
        ///
        ///     Called to transform a textual query into an SData query compatible search expression.
        ///
        /// <returns type="String">An SData query compatible search expression.</returns>
        return false;
    },
    createRequest:function() {
        ///
        ///     Creates SDataResourceCollectionRequest instance and sets a number of known properties.
        ///
        /// <returns type="Sage.SData.Client.SDataResourceCollectionRequest">An SDataResourceCollectionRequest instance.<returns>
        var pageSize = this.pageSize;
        var startIndex = this.feed && this.feed['$startIndex'] > 0 && this.feed['$itemsPerPage'] > 0
            ? this.feed['$startIndex'] + this.feed['$itemsPerPage']
            : 1;

        var request = new Sage.SData.Client.SDataResourceCollectionRequest(this.getService())
            .setCount(pageSize)
            .setStartIndex(startIndex);

        if (this.resourceKind)
            request.setResourceKind(this.resourceKind);

        if (this.resourceProperty)
            request
                .getUri()
                .setPathSegment(Sage.SData.Client.SDataUri.ResourcePropertyIndex, this.resourceProperty);

        var where = [];

        if (this.context)
        {
            var resourceKindExpr = this.expandExpression(this.context.resourceKind);
            if (resourceKindExpr)
                request.setResourceKind(resourceKindExpr);

            var resourcePredicateExpr = this.expandExpression(this.context.resourcePredicate);
            if (resourcePredicateExpr)
                request
                    .getUri()
                    .setCollectionPredicate(resourcePredicateExpr);

            var whereExpr = this.expandExpression(this.context.where);
            if (whereExpr)
                where.push(whereExpr);
        }

        if (this.query)
            where.push(this.query);

        if (where.length > 0)
            request.setQueryArgs({
                'where': where.join(' and ')
            });

        return request;
    },
    navigateToDetail: function(view, key, descriptor) {
        ///
        ///     Navigates to the requested detail view.
        ///
        /// <param name="el" type="Ext.Element">The element that initiated the navigation.</param>
        var v = App.getView(view);
        if (v)
            v.show({
                descriptor: descriptor,
                key: key
            });
    },
    navigateToInsert: function() {
        var view = App.getView(this.editor);
        if (view)
            view.show({insert: true});
    },
    processFeed: function(feed) {
        ///
        ///     Processes the feed result from the SData request and renders out the resource feed entries.
        ///
        /// <param name="feed" type="Object">The feed object.</param>
        if (this.requestedFirstPage == false)
        {
            this.requestedFirstPage = true;
            this.el
                .select('.loading')
                .remove();
            //Insert Search bar first
            Ext.DomHelper.insertFirst(this.el, this.searchTemplate.apply(this));
            //Set up handlers. Give the <form> some time to get attached to dom.
            this.setUpSearchBoxHandlers.defer(200, this);
        }

        this.feed = feed;

        if (this.feed['$totalResults'] === 0)
        {
            Ext.DomHelper.insertBefore(this.moreEl, this.noDataTemplate.apply(this));
        }
        else
        {
            var o = [];

            for (var i = 0; i < feed.$resources.length; i++)
                o.push(this.itemTemplate.apply(feed.$resources[i], this));

            if (o.length > 0)
                Ext.DomHelper.insertBefore(this.moreEl, o.join(''));
        }

        this.moreEl
            .removeClass('more-loading');

        if (this.hasMoreData())
            this.moreEl.show();
        else
            this.moreEl.hide();
    },
    setUpSearchBoxHandlers: function() {
      this.el
          .child('form')
          .on('submit', function(evt, el, o) {
              return false;
          }, this, { preventDefault: true, stopPropagation: true })
          .dom.onsubmit = false; // fix for iui shenanigans
      this.searchEl = this.el.child('input.query');
      this.searchEl.dom.value = this.queryText === false ? "" : this.queryText;

      this.setSearchLabelVisibility();

      this.el.child('input.query')
          .on('keypress', function(evt, el, o) {
              if (evt.getKey() == 13 || evt.getKey() == 10)
              {
                  evt.stopEvent();

                  /* fix to hide iphone keyboard when go is pressed */
                  if (/(iphone|ipad)/i.test(navigator.userAgent))
                      Ext.get('backButton').focus();
                  this.search();
              }
          }, this)
          .on('keyup', function(evt, el, o) {
              this.setSearchLabelVisibility();
          }, this);
    },
    setSearchLabelVisibility: function() {
      if (this.searchEl.dom.value == "") {
          this.el.select('.dismissButton').hide();
          this.el.select('label').show();
      }
      else {
          this.el.select('.dismissButton').show();
          this.el.select('label').hide();
      }
    },
    hasMoreData: function() {
        ///
        ///     Deterimines if there is more data to be shown by inspecting the last feed result.
        ///
        /// <returns type="Boolean">True if the feed has more data; False otherwise.</returns>
        if (this.feed['$startIndex'] > 0 && this.feed['$itemsPerPage'] > 0 && this.feed['$totalResults'] >= 0)
        {
            var start = this.feed['$startIndex'];
            var count = this.feed['$itemsPerPage'];
            var total = this.feed['$totalResults'];

            return (start + count <= total);
        }
        else
        {
            return true; // no way to determine, always assume more data
        }
    },
    requestFailure: function(response, o) {
        ///
        ///     Called when an error occurs while request data from the SData endpoint.
        ///
        /// <param name="response" type="Object">The response object.</param>
        /// <param name="o" type="Object">The options that were passed to Ext when creating the Ajax request.</param>
    },
    requestData: function() {
        ///
        ///     Initiates the SData request.
        ///
        var request = this.createRequest();
        request.read({
            success: function(feed) {
                this.processFeed(feed);
            },
            failure: function(response, o) {
                this.requestFailure(response, o);
            },
            scope: this
        });
    },
    more: function() {
        ///
        ///     Called when the more button is clicked.
        ///
        this.moreEl.addClass('more-loading');
        this.requestData();
    },
    expandExpression: function(expression) {
        ///
        ///     Expands the passed expression if it is a function.
        ///
        /// <param name="expression" type="String">
        ///     1: function - Called on this object and must return a string.
        ///     2: string - Returned directly.
        /// </param>
        if (typeof expression === 'function')
            return expression.call(this);
        else
            return expression;
    },
    refreshRequiredFor: function(options) {
        if (this.options)
        {
            if (options)
            {
                if (this.expandExpression(this.options.where) != this.expandExpression(options.where)) return true;
                if (this.expandExpression(this.options.resourceKind) != this.expandExpression(options.resourceKind)) return true;
                if (this.expandExpression(this.options.resourcePredicate) != this.expandExpression(options.resourcePredicate)) return true;
            }

            return false;
        }
        else
        {
            return true;
        }
    },
    getContext: function() {
        return Ext.apply(Sage.Platform.Mobile.List.superclass.getContext.call(this), {
            resourceKind: this.resourceKind
        });
    },
    beforeTransitionTo: function() {
        Sage.Platform.Mobile.List.superclass.beforeTransitionTo.call(this);

        if (this.refreshRequired) this.clear();
    },
    refresh: function() {
        this.requestData();
    },
    transitionTo: function() {
        Sage.Platform.Mobile.List.superclass.transitionTo.call(this);

    },
    show: function(options) {
        Sage.Platform.Mobile.Detail.superclass.show.apply(this, arguments);

        if (this.searchEl && this.searchEl.dom.value == "") {
            this.el.select('.dismissButton').hide();
            this.el.select('label').show();
        }
        else {
            this.el.select('.dismissButton').show();
            this.el.select('label').hide();
         }
    },
    clear: function() {
        ///
        ///     Clears the view and re-applies the default content template.
        ///
        this.el.update(this.loadingTemplate.apply(this));

        this.moreEl = this.el.down(".more");

        this.requestedFirstPage = false;
        this.feed = false;
        this.query = false;
        this.queryText = false;
    }
});<|endoftext|>


<commit_before>import React, { Component } from 'react';
import TextField from 'material-ui/lib/text-field';
import { primaryColor,primaryColorText,primaryColorLight,accentColor,primaryColorDark } from './colors';
import {baseUrl,ERROR_CODE_FROM_EMAIL,ERROR_CODE_TO_EMAIL,ERROR_CODE_INVALID_URL} from './Const';
import RaisedButton from 'material-ui/lib/raised-button';
import CircularProgress from 'material-ui/lib/circular-progress';
import axios from 'axios';
import cookie from 'react-cookie';
import isEmpty from 'lodash/isEmpty';
import Snackbar from 'material-ui/lib/snackbar';
import Dialog from 'material-ui/lib/dialog';
import FlatButton from 'material-ui/lib/flat-button';

//todo 如果是未找到发送的内容 1分钟内再次点击会提示错误 应该修改server1分钟的查询语句
//todo 把preview等未实现的功能 ui处理下 变成不可点击

let from_email = "";
let to_email = "";
let error_from_email = "";
let error_to_email = "";
let send_url_error_text = "";
let snackbar_msg = "";

class Main extends Component {

  constructor(props) {
    super(props);

    this.state = {
      isShowSaveBtn: false,
      showProgressBar: false,
      showSnackbar: false,
      showPreviewDialog: false,
      preview: {
        content: "",
      },
    };

    from_email = cookie.load('from_email');
    to_email = cookie.load('to_email');
  }

  render() {
    return (

        <TextField style={styles.input}
                   underlineFocusStyle={styles.underlineStyle}
                   ref="sendUrl"
                   errorText={send_url_error_text}
                   floatingLabelText="请输入要推送的网址"
        />

          <RaisedButton label="发送" backgroundColor={primaryColor} labelColor={primaryColorText}
                        onClick={this.send.bind(this)}
                        style={styles.button}/>

            {this._showPreviewDialog()}
            <RaisedButton label="预览" backgroundColor={primaryColorLight} labelColor={primaryColorText}
                          onClick={this.preview.bind(this)}
                          style={styles.button}/>
            <RaisedButton label="附件" backgroundColor={primaryColorLight} labelColor={primaryColorText}
                          onClick={this.attach.bind(this)}
                          style={styles.button}>
              <input type="file" style={styles.exampleImageInput}/>
            </RaisedButton>

        {this._progressBar()}

        <TextField style={styles.fromEmail}
                   ref="fromEmail"
                   value={from_email}
                   onChange={this._emailChange.bind(this)}
                   underlineFocusStyle={styles.underlineStyle}
                   floatingLabelText="请输入信任的邮箱"
                   errorText={error_from_email}
        />
        <TextField style={styles.input}
                   ref="toEmail"
                   value={to_email}
                   errorText={error_to_email}
                   onChange={this._emailChange.bind(this)}
                   underlineFocusStyle={styles.underlineStyle}
                   floatingLabelText="请输入Kindle接收邮箱"
        />
        {this._saveButton()}
        <Snackbar
          open={this.state.showSnackbar}
          onRequestClose={this._onRequestClose.bind(this)}
          message={snackbar_msg}
          autoHideDuration={3000}
        />

    );
  }

  send() {
    const sendApi = baseUrl + 'send';

    let sendUrl = this.refs.sendUrl.getValue();

    if (isEmpty(sendUrl)) {
      send_url_error_text = "请输入要推送的网址"
      this.forceUpdate();
      return;
    }

    if (this.state.showProgressBar) {
      snackbar_msg = "发送中...."
      this.forceUpdate();
      return;
    }

    error_from_email = ""
    error_to_email = ""
    send_url_error_text = ""

    this.setState({showProgressBar: true});

    axios.post(sendApi, {
        url: sendUrl,
        from_email: from_email,
        to_email: to_email,
      })
      .then((res) => {
        snackbar_msg = "已发送"
        this.setState({showSnackbar: true});
        this.setState({showProgressBar: false});

        this.forceUpdate();
      })
      .catch((res) => {
        this.setState({showProgressBar: false});
        switch (res.data.code) {
          case ERROR_CODE_FROM_EMAIL:
            error_from_email = res.data.error;
            break;
          case ERROR_CODE_TO_EMAIL:
            error_to_email = res.data.error;
            break;
          case ERROR_CODE_INVALID_URL:
            send_url_error_text = res.data.error;
            break;
          default:
            snackbar_msg = res.data.error
            this.setState({showSnackbar: true});
            break;
        }
        this.forceUpdate();
      });
  }

  preview() {
    const sendApi = baseUrl + 'preview';
    const sendUrl = this.refs.sendUrl.getValue();

    if (isEmpty(sendUrl)) {
      send_url_error_text = "请输入要预览的网址"
      this.forceUpdate();
      return;
    }

    if (this.state.showProgressBar) {
      snackbar_msg = "发送中...."
      this.forceUpdate();
      return;
    }

    this.setState({showProgressBar: true});

    axios.post(sendApi, {
        url: sendUrl,
      })
      .then((res) => {
        this.setState({showPreviewDialog: true});
        this.setState({showProgressBar: false});
        this.setState({
          preview: {
            content: res.data.content,
          }
        });
      })
      .catch((res) => {
        this.setState({showProgressBar: false});
        switch (res.data.code) {
          case ERROR_CODE_INVALID_URL:
            send_url_error_text = res.data.error;
            break;
          default:
            snackbar_msg = res.data.error
            this.setState({showSnackbar: true});
            break;
        }
        this.forceUpdate();
      });
  }

  handleDialogClose() {
    this.setState({showPreviewDialog: false});
  };

  _showPreviewDialog() {
    const actions = [
      <FlatButton
        label="关闭"
        primary={true}
        keyboardFocused={true}
        onTouchTap={this.handleDialogClose.bind(this)}
      />,
    ];

    const customContentStyle = {
      overflow: 'auto',
    };

    return (
      <Dialog
        actions={actions}
        modal={false}
        open={this.state.showPreviewDialog}
        bodyStyle={customContentStyle}
        onRequestClose={this.handleDialogClose.bind(this)}
      >

      </Dialog>
    );
  }

  attach() {
    this.showSnackbar();
    return;
  }

  showSnackbar() {
    snackbar_msg = "开发中的功能,暂不能用...";
    this.setState({showSnackbar: true});
  }

  _onRequestClose() {
    this.setState({showSnackbar: false});
  }

  _progressBar() {
    if (this.state.showProgressBar) {
      return (
        <CircularProgress
          color={primaryColor}
          style={styles.progress}/>
      );
    }
  }

  _emailChange() {
    from_email = this.refs.fromEmail.getValue();
    to_email = this.refs.toEmail.getValue();
    this.setState({isShowSaveBtn: true});
  }

  _saveButton() {
    if (this.state.isShowSaveBtn) {
      return (
        <RaisedButton
          label="保存" backgroundColor={accentColor} labelColor={primaryColorText}
          onClick={this._saveEmail.bind(this)}
          style={styles.saveButton}
        />
      );
    }
  }

  _saveEmail() {
    cookie.save('from_email', this.refs.fromEmail.getValue());
    cookie.save('to_email', this.refs.toEmail.getValue());
    this.setState({isShowSaveBtn: false});
  }

}

var styles = {
  container: {
    display: 'flex',
    justifyContent: 'center',
    flexDirection: 'column',
    paddingTop: '2%',
    alignItems: 'center',
  },

  input: {
    minWidth: '39%',
  },

  fromEmail: {
    minWidth: '39%',
    marginTop: '15vh',
  },

  underlineStyle: {
    borderColor: primaryColor,
  },

  buttonGroup: {
    display: 'flex',
    minWidth: '39%',
    marginTop: '2vh',
  },

  button: {
    flexGrow: 1,
    textAlign: 'center',
    margin: '1vh',
  },

  main_child_button_group: {
    display: 'flex',
    flexGrow: 2,
    textAlign: 'center',
  },

  progress: {
    marginTop: '5vh',
  },

  saveButton: {
    marginTop: '3vh',
    width: '15%',
  },

  labelStyle: {
    fontSize: '18px',
    fontWeight: 'bold'
  },

  iframe: {
    display: 'flex',
    flexDirection: 'column',
    overflowY: 'scroll',
  },

  exampleImageInput: {
    cursor: 'pointer',
    position: 'absolute',
    top: 0,
    bottom: 0,
    right: 0,
    left: 0,
    width: '100%',
    opacity: 0,
  },
};

export default Main;<commit_msg>remove todo<commit_after>import React, { Component } from 'react';
import TextField from 'material-ui/lib/text-field';
import { primaryColor,primaryColorText,primaryColorLight,accentColor,primaryColorDark } from './colors';
import {baseUrl,ERROR_CODE_FROM_EMAIL,ERROR_CODE_TO_EMAIL,ERROR_CODE_INVALID_URL} from './Const';
import RaisedButton from 'material-ui/lib/raised-button';
import CircularProgress from 'material-ui/lib/circular-progress';
import axios from 'axios';
import cookie from 'react-cookie';
import isEmpty from 'lodash/isEmpty';
import Snackbar from 'material-ui/lib/snackbar';
import Dialog from 'material-ui/lib/dialog';
import FlatButton from 'material-ui/lib/flat-button';

let from_email = "";
let to_email = "";
let error_from_email = "";
let error_to_email = "";
let send_url_error_text = "";
let snackbar_msg = "";

class Main extends Component {

  constructor(props) {
    super(props);

    this.state = {
      isShowSaveBtn: false,
      showProgressBar: false,
      showSnackbar: false,
      showPreviewDialog: false,
      preview: {
        content: "",
      },
    };

    from_email = cookie.load('from_email');
    to_email = cookie.load('to_email');
  }

  render() {
    return (

        <TextField style={styles.input}
                   underlineFocusStyle={styles.underlineStyle}
                   ref="sendUrl"
                   errorText={send_url_error_text}
                   floatingLabelText="请输入要推送的网址"
        />

          <RaisedButton label="发送" backgroundColor={primaryColor} labelColor={primaryColorText}
                        onClick={this.send.bind(this)}
                        style={styles.button}/>

            {this._showPreviewDialog()}
            <RaisedButton label="预览" backgroundColor={primaryColorLight} labelColor={primaryColorText}
                          onClick={this.preview.bind(this)}
                          style={styles.button}/>
            <RaisedButton label="附件" backgroundColor={primaryColorLight} labelColor={primaryColorText}
                          onClick={this.attach.bind(this)}
                          style={styles.button}>
              <input type="file" style={styles.exampleImageInput}/>
            </RaisedButton>

        {this._progressBar()}

        <TextField style={styles.fromEmail}
                   ref="fromEmail"
                   value={from_email}
                   onChange={this._emailChange.bind(this)}
                   underlineFocusStyle={styles.underlineStyle}
                   floatingLabelText="请输入信任的邮箱"
                   errorText={error_from_email}
        />
        <TextField style={styles.input}
                   ref="toEmail"
                   value={to_email}
                   errorText={error_to_email}
                   onChange={this._emailChange.bind(this)}
                   underlineFocusStyle={styles.underlineStyle}
                   floatingLabelText="请输入Kindle接收邮箱"
        />
        {this._saveButton()}
        <Snackbar
          open={this.state.showSnackbar}
          onRequestClose={this._onRequestClose.bind(this)}
          message={snackbar_msg}
          autoHideDuration={3000}
        />

    );
  }

  send() {
    const sendApi = baseUrl + 'send';

    let sendUrl = this.refs.sendUrl.getValue();

    if (isEmpty(sendUrl)) {
      send_url_error_text = "请输入要推送的网址"
      this.forceUpdate();
      return;
    }

    if (this.state.showProgressBar) {
      snackbar_msg = "发送中...."
      this.forceUpdate();
      return;
    }

    error_from_email = ""
    error_to_email = ""
    send_url_error_text = ""

    this.setState({showProgressBar: true});

    axios.post(sendApi, {
        url: sendUrl,
        from_email: from_email,
        to_email: to_email,
      })
      .then((res) => {
        snackbar_msg = "已发送"
        this.setState({showSnackbar: true});
        this.setState({showProgressBar: false});

        this.forceUpdate();
      })
      .catch((res) => {
        this.setState({showProgressBar: false});
        switch (res.data.code) {
          case ERROR_CODE_FROM_EMAIL:
            error_from_email = res.data.error;
            break;
          case ERROR_CODE_TO_EMAIL:
            error_to_email = res.data.error;
            break;
          case ERROR_CODE_INVALID_URL:
            send_url_error_text = res.data.error;
            break;
          default:
            snackbar_msg = res.data.error
            this.setState({showSnackbar: true});
            break;
        }
        this.forceUpdate();
      });
  }

  preview() {
    const sendApi = baseUrl + 'preview';
    const sendUrl = this.refs.sendUrl.getValue();

    if (isEmpty(sendUrl)) {
      send_url_error_text = "请输入要预览的网址"
      this.forceUpdate();
      return;
    }

    if (this.state.showProgressBar) {
      snackbar_msg = "发送中...."
      this.forceUpdate();
      return;
    }

    this.setState({showProgressBar: true});

    axios.post(sendApi, {
        url: sendUrl,
      })
      .then((res) => {
        this.setState({showPreviewDialog: true});
        this.setState({showProgressBar: false});
        this.setState({
          preview: {
            content: res.data.content,
          }
        });
      })
      .catch((res) => {
        this.setState({showProgressBar: false});
        switch (res.data.code) {
          case ERROR_CODE_INVALID_URL:
            send_url_error_text = res.data.error;
            break;
          default:
            snackbar_msg = res.data.error
            this.setState({showSnackbar: true});
            break;
        }
        this.forceUpdate();
      });
  }

  handleDialogClose() {
    this.setState({showPreviewDialog: false});
  };

  _showPreviewDialog() {
    const actions = [
      <FlatButton
        label="关闭"
        primary={true}
        keyboardFocused={true}
        onTouchTap={this.handleDialogClose.bind(this)}
      />,
    ];

    const customContentStyle = {
      overflow: 'auto',
    };

    return (
      <Dialog
        actions={actions}
        modal={false}
        open={this.state.showPreviewDialog}
        bodyStyle={customContentStyle}
        onRequestClose={this.handleDialogClose.bind(this)}
      >

      </Dialog>
    );
  }

  attach() {
    this.showSnackbar();
    return;
  }

  showSnackbar() {
    snackbar_msg = "开发中的功能,暂不能用...";
    this.setState({showSnackbar: true});
  }

  _onRequestClose() {
    this.setState({showSnackbar: false});
  }

  _progressBar() {
    if (this.state.showProgressBar) {
      return (
        <CircularProgress
          color={primaryColor}
          style={styles.progress}/>
      );
    }
  }

  _emailChange() {
    from_email = this.refs.fromEmail.getValue();
    to_email = this.refs.toEmail.getValue();
    this.setState({isShowSaveBtn: true});
  }

  _saveButton() {
    if (this.state.isShowSaveBtn) {
      return (
        <RaisedButton
          label="保存" backgroundColor={accentColor} labelColor={primaryColorText}
          onClick={this._saveEmail.bind(this)}
          style={styles.saveButton}
        />
      );
    }
  }

  _saveEmail() {
    cookie.save('from_email', this.refs.fromEmail.getValue());
    cookie.save('to_email', this.refs.toEmail.getValue());
    this.setState({isShowSaveBtn: false});
  }

}

var styles = {
  container: {
    display: 'flex',
    justifyContent: 'center',
    flexDirection: 'column',
    paddingTop: '2%',
    alignItems: 'center',
  },

  input: {
    minWidth: '39%',
  },

  fromEmail: {
    minWidth: '39%',
    marginTop: '15vh',
  },

  underlineStyle: {
    borderColor: primaryColor,
  },

  buttonGroup: {
    display: 'flex',
    minWidth: '39%',
    marginTop: '2vh',
  },

  button: {
    flexGrow: 1,
    textAlign: 'center',
    margin: '1vh',
  },

  main_child_button_group: {
    display: 'flex',
    flexGrow: 2,
    textAlign: 'center',
  },

  progress: {
    marginTop: '5vh',
  },

  saveButton: {
    marginTop: '3vh',
    width: '15%',
  },

  labelStyle: {
    fontSize: '18px',
    fontWeight: 'bold'
  },

  iframe: {
    display: 'flex',
    flexDirection: 'column',
    overflowY: 'scroll',
  },

  exampleImageInput: {
    cursor: 'pointer',
    position: 'absolute',
    top: 0,
    bottom: 0,
    right: 0,
    left: 0,
    width: '100%',
    opacity: 0,
  },
};

export default Main;<|endoftext|>


<commit_before>import React from 'react';

const Main = () => {

	const articles = [
		{
			id: 1,
			title: 'Title 1'
		}, {
			id: 2,
			title: 'Title 2'
		}, {
			id: 3,
			title: 'Title 3'
		}
	];

	return (

			{articles.map(function(article, index) {
				return (

{ article.title }

				);
			})}

	);
}

export default Main;<commit_msg>Add a unique key to each child in an array<commit_after>import React from 'react';

const Main = () => {

	const articles = [
		{
			id: 1,
			title: 'Title 1'
		}, {
			id: 2,
			title: 'Title 2'
		}, {
			id: 3,
			title: 'Title 3'
		}
	];

	return (

			{articles.map(function(article, index) {
				return (

{ article.title }

				);
			})}

	);
}

export default Main;<|endoftext|>


<commit_before>class Peer {

	constructor(options) {
		this.descriptor = new Descriptor(Math.random().toString().replace('.', ''), Peer.mockPosition());
		this.bootstrappingNode = new BootstrappingNode(this, options.websocket);
		this.geobucket = new GeoBucket(Peer.mockPosition(), options.geobucket);
		this.options = options;
		this.listeners = new Map();
	}

  send(message) {
		this.geobucket.geocast(message);
  }

  on(event, callback) {
	  if(this.listeners.has(event)) {
      this.listeners.get(event).add(callback);
    } else {
      var callbacks = new Set();
      callbacks.add(callback);
      this.listeners.set(event, callbacks);
    }
	}

	emit(event, data) {
		if(this.listeners.has(event)) {
			for(var callback of this.listeners.get(event)) {
				callback(data);
			}
		}
	}

	move(position) {
		this.descriptor.position = position;
    if(this.geobucket.move(position)) {
			this.emit("neighbors", this.geobucket.descriptors());
		}
	}

	follows(position) {
		return this.geobucket.follows(position);
	}

	onmessage(message, node) {
		switch(message.type) {
			case DiscoveryRequestMessage.type():
				var descriptors = [];
				for(var n of this.geobucket.retrieve(message.sender.position, this.options.discovery.response_limit)) {
					descriptors.push(n.descriptor);
				}
				node.send(new DiscoveryResponseMessage(this.descriptor, descriptors));
				break;

			case DiscoveryResponseMessage.type():
				for(var index in message.descriptors) {
					var descriptor = message.descriptors[index];
					if(this.descriptor.key !== descriptor.key && this.follows(descriptor.position)) {
						var remoteNode = this.geobucket.get(descriptor);
						if(remoteNode) {
							if(this.geobucket.update(remoteNode)) {
								this.emit("neighbors", this.geobucket.descriptors());
							}
						} else {
							remoteNode = new RemoteNode(this, descriptor, this.options.webrtc, this.bootstrappingNode, node);
							if(this.geobucket.add(remoteNode)) {
								remoteNode.connect();
								this.emit("neighbors", this.geobucket.descriptors());
							}
						}
					}
				}
				break;

			case UpdateMessage.type():
				node.update(message.sender);
				if(this.geobucket.update(node)) {
					this.emit("neighbors", this.geobucket.descriptors());
				}
				break;

			case DataMessage.type():
				this.emit("data", message.data);
				break;

			case LeaveMessage.type():
				var node = this.geobucket.get(message.sender);
				if(node !== null) {
					if(this.geobucket.remove(node)) {
						this.emit("neighbors", this.geobucket.descriptors());
					}
				}
				break;

			case AnswerMessage.type():
				var node = this.geobucket.get(message.sender);
				if(node !== null) {
					node.connection.setRemoteDescription(new RTCSessionDescription(message.answer));
				}
				break;

			case OfferMessage.type():
				if(this.follows(message.sender.position)) {
					var remoteNode = new RemoteNode(this, message.sender, this.options.webrtc, this.bootstrappingNode, node);
					if(!this.geobucket.has(remoteNode)) {
						var self = this;
						remoteNode.connection.setRemoteDescription(new RTCSessionDescription(message.offer));
						remoteNode.connection.createAnswer(function (answer) {
							remoteNode.connection.setLocalDescription(new RTCSessionDescription(answer), function() {
								remoteNode.sendToSignalingNode(new SignalingMessage(self.descriptor, message.sender, new AnswerMessage(self.descriptor, answer)));
							}, function (error) {
								self.emit("error", error);
							});
						}, function (error) {
							self.emit("error", error);
						});
						if(this.geobucket.add(remoteNode)) {
							this.emit("neighbors", this.geobucket.descriptors());
						}
					}
				}
				break;

			case CandidateMessage.type():
				var node = this.geobucket.get(message.sender);
				if(node !== null) {
					node.connection.addIceCandidate(new RTCIceCandidate(message.candidate));
				}
				break;

			case SignalingMessage.type():
				var node = this.geobucket.get(message.recipient);
				if(node != null) {
					node.send(message.payload);
				}
				break;
		}
	}

	connect() {
		this.bootstrappingNode.connect();

		var self = this;
		setInterval(function() {
       self.task();
    }, 60000);
	}

	disconnect() {
		this.geobucket.removeAll();
		if(this.bootstrappingNode.isConnected()) {
			this.bootstrappingNode.disconnect();
		}
	}

	task() {
	  if(this.bootstrappingNode.isConnected()) {

			if(this.geobucket.size() < this.options.geobucket.lower_limit) {
				this.bootstrappingNode.send(new DiscoveryRequestMessage(this.descriptor));
			}

			var nodes = this.geobucket.retrieve(this.descriptor.position, this.options.discovery.request_limit);
			var message = new DiscoveryRequestMessage(this.descriptor);
			for(var node of nodes) {
				if(node.isConnected()) {
					node.send(message);
				}
			}
		}
	}

	static mockPosition() {
		    var latitude = 44.76487;
		    var longitude = 10.30836;
		    var heading = Math.random() * 360.0;
		    var distance = Math.random() * 20000;

		    var destination = Geography.destination(latitude, longitude, heading, distance);
		    return {"coords":{"accuracy":0.0,"altitude":0.0,"altitudeAccuracy":0.0,"heading":destination.heading,"latitude":destination.latitude,"longitude":destination.longitude,"speed":0.0},"timestamp":Date.now()};
	}
}
<commit_msg>Update Peer.js<commit_after>class Peer {

    constructor(options) {
        var position = Geography.mockPosition();
        this.descriptor = new Descriptor(Math.random().toString().replace('.', ''), position);
        this.bootstrappingNode = new BootstrappingNode(this, options.websocket);
        this.geobucket = new GeoBucket(position, options.geobucket);
        this.options = options;
        this.listeners = new Map();
    }

    send(message) {
        this.geobucket.geocast(message);
    }

    on(event, callback) {
        if(this.listeners.has(event)) {
            this.listeners.get(event).add(callback);
        } else {
            var callbacks = new Set();
            callbacks.add(callback);
            this.listeners.set(event, callbacks);
        }
    }

    emit(event, data) {
        if(this.listeners.has(event)) {
            for(var callback of this.listeners.get(event)) {
                callback(data);
            }
        }
    }

    move(position) {
        var p = {coords: {latitude: position.coords.latitude, longitude: position.coords.longitude, accuracy: position.coords.accuracy, altitude: position.coords.altitude, altitudeAccuracy: position.coords.altitudeAccuracy, heading: position.coords.heading, speed: position.coords.speed}, timestamp: position.timestamp};
        this.descriptor.position = p;
        if(this.geobucket.move(p)) {
            this.emit("neighbors", this.geobucket.descriptors());
        }
    }

    follows(position) {
        return this.geobucket.follows(position);
    }

    onmessage(message, node) {
        if(message.sender) {
            console.log('Received ' + message.type + ' from ' + message.sender.key);
        } else {
            console.log('Received ' + message.type + ' from Bootstrapping Node');
        }
        switch(message.type) {
            case DiscoveryRequestMessage.type():
                var descriptors = [];
                for(var n of this.geobucket.retrieve(message.sender.position, this.options.discovery.response_limit)) {
                    descriptors.push(n.descriptor);
                }
                node.send(new DiscoveryResponseMessage(this.descriptor, descriptors));
                break;

            case DiscoveryResponseMessage.type():
                for(var index in message.descriptors) {
                    var descriptor = message.descriptors[index];
                    if(this.descriptor.key !== descriptor.key && this.follows(descriptor.position)) {
                        var remoteNode = this.geobucket.get(descriptor);
                        if(remoteNode) {
                            if(this.geobucket.update(descriptor)) {
                                remoteNode.setSignalingNode(node);
                                this.emit("neighbors", this.geobucket.descriptors());
                            }
                        } else {
                            remoteNode = new RemoteNode(this, descriptor, this.options.webrtc, this.bootstrappingNode, node, true);
                            if(this.geobucket.add(remoteNode)) {
                                remoteNode.connect();
                                this.emit("neighbors", this.geobucket.descriptors());
                            }
                        }
                    }
                }
                break;

            case UpdateMessage.type():
                if(this.geobucket.update(message.sender)) {
                    this.emit("neighbors", this.geobucket.descriptors());
                }
                break;

            case DataMessage.type():
                this.emit("data", message.data);
                break;

            case LeaveMessage.type():
                var node = this.geobucket.get(message.sender);
                if(node !== null) {
                    if(this.geobucket.remove(node)) {
                        this.emit("neighbors", this.geobucket.descriptors());
                    }
                }
                break;

            case AnswerMessage.type():
                var node = this.geobucket.get(message.sender);
                if(node !== null) {
                    node.connection.setRemoteDescription(new RTCSessionDescription(message.answer));
                }
                break;

            case OfferMessage.type():
                if(this.follows(message.sender.position)) {
                    var remoteNode = new RemoteNode(this, message.sender, this.options.webrtc, this.bootstrappingNode, node, false);
                    if(!this.geobucket.has(remoteNode)) {
                        var self = this;
                        remoteNode.connection.setRemoteDescription(new RTCSessionDescription(message.offer));

                        remoteNode.connection.createAnswer().then(function(answer) {
                            return remoteNode.connection.setLocalDescription(answer);
                        }).then(function() {
                            remoteNode.sendToSignalingNode(new SignalingMessage(self.descriptor, message.sender, new AnswerMessage(self.descriptor, remoteNode.connection.localDescription)));
                        }).catch(function(reason) {
                            // An error occurred, so handle the failure to connect
                        });

                        if(this.geobucket.add(remoteNode)) {
                            this.emit("neighbors", this.geobucket.descriptors());
                        }
                    }
                }
                break;

            case CandidateMessage.type():
                var node = this.geobucket.get(message.sender);
                if(node !== null) {
                    node.connection.addIceCandidate(new RTCIceCandidate(message.candidate));
                }
                break;

            case SignalingMessage.type():
                var node = this.geobucket.get(message.recipient);
                if(node != null) {
                    node.send(message.payload);
                }
                break;
        }
    }

    connect() {
        this.bootstrappingNode.connect();

        var self = this;
        setInterval(function() {
            self.task();
        }, this.options.discovery.period);
    }

    disconnect() {
        this.geobucket.removeAll();
        if(this.bootstrappingNode.isConnected()) {
            this.bootstrappingNode.disconnect();
        }
    }

    task() {
        console.log('-------------------- ' + this.descriptor.key + ' --------------------');
        console.log('Neighbors: [' + this.geobucket.descriptors().map(function(descriptor){return descriptor.key}).join(', ') + ']');
        if(this.bootstrappingNode.isConnected()) {
            if(this.geobucket.size() < this.options.geobucket.lower_limit) {
                this.bootstrappingNode.send(new DiscoveryRequestMessage(this.descriptor));
            }

            var nodes = this.geobucket.retrieve(this.descriptor.position, this.options.discovery.request_limit);
            var message = new DiscoveryRequestMessage(this.descriptor);
            for(var node of nodes) {
                if(node.isConnected()) {
                    node.send(message);
                }
            }
        }
    }

}
<|endoftext|>


<commit_before>// Function testing the SDK

// Starting out by requiring all dependancies
var test = require('tape');
var Ibc1 = require('ibm-blockchain-js');

// Then define new instances.
var ibc = new Ibc1();
var chaincode = {};

// Define the cb_ready (call back when ready) function
var cb_ready = function cb_ready(err, cc) {};

// Define options for ibc.load_chancode
// In this case, this is just defining where to get the blockchain code
var options = {
	zip_url: 'https://github.com/ibm-blockchain/marbles-chaincode/archive/master.zip',
	unzip_dir: 'marbles-chaincode-master/part2',
	git_url: 'https://github.com/ibm-blockchain/marbles-chaincode/part2',
	deployed_name: null
};

// Call the actual function of interest
// ibc.load_chaincode inputs options just spcified, outputs callbackready function
ibc.load_chaincode(options, cb_ready);

// Write a test to check if the load function was successful
test('Was the load_chaincode sucessful', function (t) {
	var actual = err;
	var expected = '';
	t.equal(actual, expected, 'should pass if there are no errors');
	t.end();
 });<commit_msg>testing progress<commit_after>// Function testing the SDK

// Starting out by requiring all dependancies
var test = require('tape');
var Ibc1 = require('ibm-blockchain-js');

// Then define new instances.
var ibc = new Ibc1();
var chaincode = {};

// Define options for ibc.load_chancode
// In this case, this is just defining where to get the blockchain code
var options = {
	zip_url: 'https://github.com/ibm-blockchain/marbles-chaincode/archive/master.zip',
	unzip_dir: 'marbles-chaincode-master/part2',
	git_url: 'https://github.com/ibm-blockchain/marbles-chaincode/part2',
	deployed_name: null
};

// Call the actual function of interest
// ibc.load_chaincode inputs options just spcified, outputs callbackready function
ibc.load_chaincode(options, function cb_ready(err, cc) {
	test('Was the load_chaincode sucessful', function (t) {
		t.error(err, 'There were no errors');
		t.end();
	}); //end test
});  //end load chaincode
<|endoftext|>


<commit_before>'use strict';

var Firebase = require("firebase");
var secrets = require("./secrets");

var firebaseRef = new Firebase(secrets.firebaseUrl);
var userListRef = new Firebase(secrets.firebaseUrl + '/userList');
var usersRef = new Firebase(secrets.firebaseUrl + '/users');
var conversationsRef = new Firebase(secrets.firebaseUrl + '/conversations');

var getConversations = function(id, amount) {

  return []
};

var getMessages = function(request, amount) {

  return [];
};

var addUser = function(username) {
  userListRef.push({
    username: username
  });
  var userObject = {};
  userObject[username] = {username : username};
  usersRef.update(userObject);
}

var getUserListSetup = function(user) {
  //use username to remove that person from list
  // Attach an asynchronous callback to read the data at our posts reference
  console.log("setting up socket for: " + user.username);
  userListRef.on("value", function(snapshot) {
    console.log(snapshot.val());
    var userObject = snapshot.val();
    var userList = []
    for (var key in userObject) {
      console.log(userObject[key].username);
      userList.push(userObject[key].username);
    }
    console.log(userList);
    console.log("emitting userlist for: " + user.username);
    user.socket.emit('userList', {userList: userList, emit: 5, user: user.username});
    user.socket.broadcast.emit('userList', {userList: userList, broadcast: 5, user: user.username});
  }, function (errorObject) {
    console.log("The read failed: " + errorObject.code);
  });
}

var storeConversation = function(conversation, messages) {
  var conRef = conversationsRef.push(conversation);
  var newConRef = {};
  newConRef[conversation.origRecipient] = conRef.key();
  usersRef.child(conversation.origSender).update(newConRef);
  conRef.child('messages').push(messages);
};

var storeMessage = function(message, conversationId) {
  //what if conversation not created yet?
  var conRef = conversationsRef.child(conversationId);
  conRef.child('messages').push(message);
};

module.exports = {
  getConversations: getConversations,
  getMessages: getMessages,
  addUser: addUser,
  getUserListSetup: getUserListSetup,
  storeConversation: storeConversation,
  storeMessage: storeMessage
};
<commit_msg>storing conversation key reference in recipient object in db now as well<commit_after>'use strict';

var Firebase = require("firebase");
var secrets = require("./secrets");

var firebaseRef = new Firebase(secrets.firebaseUrl);
var userListRef = new Firebase(secrets.firebaseUrl + '/userList');
var usersRef = new Firebase(secrets.firebaseUrl + '/users');
var conversationsRef = new Firebase(secrets.firebaseUrl + '/conversations');

var getConversations = function(id, amount) {

  return []
};

var getMessages = function(request, amount) {

  return [];
};

var addUser = function(username) {
  userListRef.push({
    username: username
  });
  var userObject = {};
  userObject[username] = {username : username};
  usersRef.update(userObject);
}

var getUserListSetup = function(user) {
  //use username to remove that person from list
  // Attach an asynchronous callback to read the data at our posts reference
  console.log("setting up socket for: " + user.username);
  userListRef.on("value", function(snapshot) {
    console.log(snapshot.val());
    var userObject = snapshot.val();
    var userList = []
    for (var key in userObject) {
      console.log(userObject[key].username);
      userList.push(userObject[key].username);
    }
    console.log(userList);
    console.log("emitting userlist for: " + user.username);
    user.socket.emit('userList', {userList: userList, emit: 5, user: user.username});
    user.socket.broadcast.emit('userList', {userList: userList, broadcast: 5, user: user.username});
  }, function (errorObject) {
    console.log("The read failed: " + errorObject.code);
  });
}

var storeConversation = function(conversation, messages) {
  var conRef = conversationsRef.push(conversation);
  var newConRefUser = {};
  var newConRefRecipient = {};
  newConRefUser[conversation.origRecipient] = conRef.key();
  newConRefRecipient[conversation.origSender] = conRef.key();
  usersRef.child(conversation.origSender).update(newConRefUser);
  usersRef.child(conversation.origRecipient).update(newConRefRecipient);
  conRef.child('messages').push(messages);
};

var storeMessage = function(message, conversationId) {
  //what if conversation not created yet?
  var conRef = conversationsRef.child(conversationId);
  conRef.child('messages').push(message);
};

module.exports = {
  getConversations: getConversations,
  getMessages: getMessages,
  addUser: addUser,
  getUserListSetup: getUserListSetup,
  storeConversation: storeConversation,
  storeMessage: storeMessage
};
<|endoftext|>


<commit_before>const EventEmitter = require('events');

const twgl = require('twgl.js');

const RenderConstants = require('./RenderConstants');

class Skin extends EventEmitter {
    /**
     * Create a Skin, which stores and/or generates textures for use in rendering.
     * @param {int} id - The unique ID for this Skin.
     * @constructor
     */
    constructor (id) {
        super();

        /** @type {int} */
        this._id = id;

        /** @type {Vec3} */
        this._rotationCenter = twgl.v3.create(0, 0);

        /**
         * The uniforms to be used by the vertex and pixel shaders.
         * Some of these are used by other parts of the renderer as well.
         * @type {Object.<string,*>}
         * @private
         */
        this._uniforms = {
            /**
             * The nominal (not necessarily current) size of the current skin.
             * @type {Array<number>}
             */
            u_skinSize: [0, 0],

            /**
             * The actual WebGL texture object for the skin.
             * @type {WebGLTexture}
             */
            u_skin: null
        };

        this.setMaxListeners(RenderConstants.SKIN_SHARE_SOFT_LIMIT);
    }

    /**
     * Dispose of this object. Do not use it after calling this method.
     */
    dispose () {
        this._id = RenderConstants.ID_NONE;
    }

    /**
     * @return {int} the unique ID for this Skin.
     */
    get id () {
        return this._id;
    }

    /**
     * @returns {Vec3} the origin, in object space, about which this Skin should rotate.
     */
    get rotationCenter () {
        return this._rotationCenter;
    }

    /**
     * @abstract
     * @return {Array<number>} the "native" size, in texels, of this skin.
     */
    get size () {
        return [0, 0];
    }

    /**
     * Set the origin, in object space, about which this Skin should rotate.
     * @param {number} x - The x coordinate of the new rotation center.
     * @param {number} y - The y coordinate of the new rotation center.
     * @fires Skin.event:WasAltered
     */
    setRotationCenter (x, y) {
        if (x !== this._rotationCenter[0] || y !== this._rotationCenter[1]) {
            this._rotationCenter[0] = x;
            this._rotationCenter[1] = y;
            this.emit(Skin.Events.WasAltered);
        }
    }

    /**
     * Get the center of the current bounding box
     * @return {Array<number>} the center of the current bounding box
     */
    calculateRotationCenter () {
        return [this.size[0] / 2, this.size[1] / 2];
    }

    /**
     * @abstract
     * @param {Array<number>} scale - The scaling factors to be used.
     * @return {WebGLTexture} The GL texture representation of this skin when drawing at the given size.
     */
    // eslint-disable-next-line no-unused-vars
    getTexture (scale) {
        return null;
    }

    /**
     * Update and returns the uniforms for this skin.
     * @param {Array<number>} scale - The scaling factors to be used.
     * @returns {object.<string, *>} the shader uniforms to be used when rendering with this Skin.
     */
    getUniforms (scale) {
        this._uniforms.u_skin = this.getTexture(scale);
        this._uniforms.u_skinSize = this.size;
        return this._uniforms;
    }
}

/**
 * These are the events which can be emitted by instances of this class.
 * @enum {string}
 */
Skin.Events = {
    /**
     * Emitted when anything about the Skin has been altered, such as the appearance or rotation center.
     * @event Skin.event:WasAltered
     */
    WasAltered: 'WasAltered'
};

module.exports = Skin;
<commit_msg>Scratch 2.0 compat: don't modify rotation center of empty sprites<commit_after>const EventEmitter = require('events');

const twgl = require('twgl.js');

const RenderConstants = require('./RenderConstants');

class Skin extends EventEmitter {
    /**
     * Create a Skin, which stores and/or generates textures for use in rendering.
     * @param {int} id - The unique ID for this Skin.
     * @constructor
     */
    constructor (id) {
        super();

        /** @type {int} */
        this._id = id;

        /** @type {Vec3} */
        this._rotationCenter = twgl.v3.create(0, 0);

        /**
         * The uniforms to be used by the vertex and pixel shaders.
         * Some of these are used by other parts of the renderer as well.
         * @type {Object.<string,*>}
         * @private
         */
        this._uniforms = {
            /**
             * The nominal (not necessarily current) size of the current skin.
             * @type {Array<number>}
             */
            u_skinSize: [0, 0],

            /**
             * The actual WebGL texture object for the skin.
             * @type {WebGLTexture}
             */
            u_skin: null
        };

        this.setMaxListeners(RenderConstants.SKIN_SHARE_SOFT_LIMIT);
    }

    /**
     * Dispose of this object. Do not use it after calling this method.
     */
    dispose () {
        this._id = RenderConstants.ID_NONE;
    }

    /**
     * @return {int} the unique ID for this Skin.
     */
    get id () {
        return this._id;
    }

    /**
     * @returns {Vec3} the origin, in object space, about which this Skin should rotate.
     */
    get rotationCenter () {
        return this._rotationCenter;
    }

    /**
     * @abstract
     * @return {Array<number>} the "native" size, in texels, of this skin.
     */
    get size () {
        return [0, 0];
    }

    /**
     * Set the origin, in object space, about which this Skin should rotate.
     * @param {number} x - The x coordinate of the new rotation center.
     * @param {number} y - The y coordinate of the new rotation center.
     * @fires Skin.event:WasAltered
     */
    setRotationCenter (x, y) {
        const emptySkin = this.size[0] === 0 && this.size[1] === 0;
        const changed = x !== this._rotationCenter[0] || y !== this._rotationCenter[1];
        if (!emptySkin && changed) {
            this._rotationCenter[0] = x;
            this._rotationCenter[1] = y;
            this.emit(Skin.Events.WasAltered);
        }
    }

    /**
     * Get the center of the current bounding box
     * @return {Array<number>} the center of the current bounding box
     */
    calculateRotationCenter () {
        return [this.size[0] / 2, this.size[1] / 2];
    }

    /**
     * @abstract
     * @param {Array<number>} scale - The scaling factors to be used.
     * @return {WebGLTexture} The GL texture representation of this skin when drawing at the given size.
     */
    // eslint-disable-next-line no-unused-vars
    getTexture (scale) {
        return null;
    }

    /**
     * Update and returns the uniforms for this skin.
     * @param {Array<number>} scale - The scaling factors to be used.
     * @returns {object.<string, *>} the shader uniforms to be used when rendering with this Skin.
     */
    getUniforms (scale) {
        this._uniforms.u_skin = this.getTexture(scale);
        this._uniforms.u_skinSize = this.size;
        return this._uniforms;
    }
}

/**
 * These are the events which can be emitted by instances of this class.
 * @enum {string}
 */
Skin.Events = {
    /**
     * Emitted when anything about the Skin has been altered, such as the appearance or rotation center.
     * @event Skin.event:WasAltered
     */
    WasAltered: 'WasAltered'
};

module.exports = Skin;
<|endoftext|>


<commit_before>(function(window) {
	//latter overwrite former
	var Spai = window.Spai || {};

	var isFunction = function(value) {
		return typeof value === "function";
	};
	var allStates = {
		pending: 0,
		fulfilled: 1,
		rejected: 2,
		sealed: 3 //once resolve or reject
	};
	var mutationObserver = window.MutationObserver || window.WebKitMutationObserver;
	//steal form rsvp.js
	var asyncInvoke = (function() {
		if (typeof process !== 'undefined' && toString.call(process) === '[object process]') {
			//node env
			return function(callback) {
				process.nextTick(callback);
			};
		} else if (mutationObserver) {
			//modern browser
			//todo
			return function(callback) {
				setTimeout(callback, 1);
			};
		} else {
			//old browser
			return function(callback) {
				setTimeout(callback, 1);
			};
		}
	})();
	var progress = function(thenable, onFulfilled, onRejected, child) {
		var resolveChild = function(value) {
			child._state = allStates.fulfilled;
			child._resolveValue = value;
		},
			rejectChild = function(reason) {
				child._state = allStates.rejected;
				child._rejectedReason = reason;
			};

		if (thenable._state === allStates.fulfilled) {
			if (isFunction(onFulfilled)) {
				try {
					var returnVal = onFulfilled(thenable._resolveValue);
					resolveChild(returnVal);
				} catch (e) {
					rejectChild(e);
				}
			} else {
				resolveChild(thenable._resolveValue);
			}
		} else if (thenable._state === allStates.rejected) {
			if (isFunction(onRejected)) {
				try {
					var returnVal = onRejected(thenable._rejectedReason);
					resolveChild(returnVal);
				} catch(e) {
					rejectChild(e);
				}
			} else {
				rejectChild(thenable._rejectedReason);
			}
		}
	};
	var resolveAll = function(thenable, value) {
		thenable._state = allStates.fulfilled;
		for (var i = 0, queue = thenable._thenQueue; i < queue.length; i++) {
			var tuple = queue[i];
			var onFulfilled = tuple[allStates.fulfilled],
				onRejected = tuple[allStates.rejected],
				child = tuple[3];

			progress(thenable, onFulfilled, onRejected, child);
			resolveAll(child, value);
		}
	};
	var resolve = function(thenable, value) {
		if (thenable._state !== allStates.pending) {
			return;
		}
		thenable._state = allStates.sealed;
		thenable._resolveValue = value;

		asyncInvoke(function() {
			resolveAll(thenable, value);
		});
	};
	var rejectAll = function(thenable, reason) {
		thenable._state = allStates.rejected;
		for (var i = 0, queue = thenable._thenQueue; i < queue.length; i++) {
			var tuple = queue[i];
			var onFulfilled = tuple[allStates.fulfilled],
				onRejected = tuple[allStates.rejected],
				child = tuple[3];

			progress(thenable, onFulfilled, onRejected, child);
			rejectAll(child, reason);
		}
	};
	var reject = function(thenable, reason) {
		if (thenable._state !== allStates.pending) {
			return;
		}
		thenable._state = allStates.sealed;
		thenable._rejectedReason = reason;

		asyncInvoke(function() {
			rejectAll(thenable, reason);
		});
	};
	var makeThenable = function() {
		var thenable = {
			_thenQueue: [],
			_state: allStates.pending
		};

		thenable.then = function(onFulfilled, onRejected) {
			var child = makeThenable();
			if (thenable._state === allStates.fulfilled || thenable._state === allStates.rejected) {
				asyncInvoke(function() {
					progress(thenable, onFulfilled, onRejected, child);
				});
			} else {
				//tuple[0] is just for pretty and clean
				thenable._thenQueue.push([0, onFulfilled, onRejected, child]);
			}
			return child;
		};

		return thenable;
	};
	Spai.promise = function(initCallback) {
		var promise = makeThenable();
		var resolveWrapper = function(value) {
			resolve(promise, value);
		};
		var rejectWrapper = function(reason) {
			reject(promise, reason);
		};

		if (isFunction(initCallback)) {
			initCallback.call(this, resolveWrapper, rejectWrapper);
		}

		return promise;
	};

	//meets test suite's need
	Spai.defer = function() {
		var deferred = {};
		deferred.promise = Spai.promise(function(resolve, reject) {
			deferred.resolve = resolve;
			deferred.reject = reject;
		});
		return deferred;
	};

	if (typeof module === "object" && module && typeof module.exports === "object") {
		//node env
		module.exports = Spai;
	} else {
		//browser env
		window.Spai = Spai;
	}
})(this);<commit_msg>208 passing, still 20% to write<commit_after>//todo: merge _rejectedReason and _resolvedValue into _detail
(function(window) {
	//latter overwrite former
	var Spai = window.Spai || {};

	var isFunction = function(value) {
		return typeof value === "function";
	};
	var allStates = {
		pending: 0,
		fulfilled: 1,
		rejected: 2,
		sealed: 3 //once resolve or reject
	};
	var mutationObserver = window.MutationObserver || window.WebKitMutationObserver;
	//steal form rsvp.js
	var asyncInvoke = (function() {
		if (typeof process !== 'undefined' && toString.call(process) === '[object process]') {
			//node env
			return function(callback) {
				process.nextTick(callback);
			};
		} else if (mutationObserver) {
			//modern browser
			//todo: this impl is not RIGHT
			return function(callback) {
				setTimeout(callback, 1);
			};
		} else {
			//old browser
			return function(callback) {
				setTimeout(callback, 1);
			};
		}
	})();
	var progress = function(thenable, onFulfilled, onRejected, child) {
		var resolveChild = function(value) {
			child._state = allStates.fulfilled;
			child._resolvedValue = value;
		},
			rejectChild = function(reason) {
				child._state = allStates.rejected;
				child._rejectedReason = reason;
			},
			handleReturn = function(returnVal) {
				//todo: handle every situations
				if (returnVal === child) {
				}
				else{
					resolveChild(returnVal);
				}
			};

		if (thenable._state === allStates.fulfilled) {
			if (isFunction(onFulfilled)) {
				try {
					var returnVal = onFulfilled(thenable._resolvedValue);
					resolveChild(returnVal);
				} catch (e) {
					rejectChild(e);
				}
			} else {
				resolveChild(thenable._resolvedValue);
			}
		} else if (thenable._state === allStates.rejected) {
			if (isFunction(onRejected)) {
				try {
					var returnVal = onRejected(thenable._rejectedReason);
					resolveChild(returnVal);
				} catch (e) {
					rejectChild(e);
				}
			} else {
				rejectChild(thenable._rejectedReason);
			}
		}
	};
	var progressAll = function(thenable, state) {
		if (state) {
			thenable._state = state;
		}
		for (var i = 0, queue = thenable._thenQueue; i < queue.length; i++) {
			var tuple = queue[i];
			var onFulfilled = tuple[allStates.fulfilled],
				onRejected = tuple[allStates.rejected],
				child = tuple[3];

			progress(thenable, onFulfilled, onRejected, child);
			progressAll(child);
		}
	}
	var resolve = function(thenable, value) {
		if (thenable._state !== allStates.pending) {
			return;
		}
		thenable._state = allStates.sealed;
		thenable._resolvedValue = value;

		asyncInvoke(function() {
			progressAll(thenable, allStates.fulfilled);
		});
	};
	var reject = function(thenable, reason) {
		if (thenable._state !== allStates.pending) {
			return;
		}
		thenable._state = allStates.sealed;
		thenable._rejectedReason = reason;

		asyncInvoke(function() {
			progressAll(thenable, allStates.rejected);
		});
	};
	var makeThenable = function() {
		var thenable = {
			_thenQueue: [],
			_state: allStates.pending
		};

		thenable.then = function(onFulfilled, onRejected) {
			var child = makeThenable();
			if (thenable._state === allStates.fulfilled || thenable._state === allStates.rejected) {
				asyncInvoke(function() {
					progress(thenable, onFulfilled, onRejected, child);
				});
			} else {
				//tuple[0] is just for pretty and clean
				thenable._thenQueue.push([0, onFulfilled, onRejected, child]);
			}
			return child;
		};

		return thenable;
	};
	Spai.promise = function(initCallback) {
		var promise = makeThenable();
		var resolveWrapper = function(value) {
			resolve(promise, value);
		};
		var rejectWrapper = function(reason) {
			reject(promise, reason);
		};

		if (isFunction(initCallback)) {
			initCallback.call(this, resolveWrapper, rejectWrapper);
		}

		return promise;
	};

	//meets test suite's need
	Spai.defer = function() {
		var deferred = {};
		deferred.promise = Spai.promise(function(resolve, reject) {
			deferred.resolve = resolve;
			deferred.reject = reject;
		});
		return deferred;
	};

	if (typeof module === "object" && module && typeof module.exports === "object") {
		//node env
		module.exports = Spai;
	} else {
		//browser env
		window.Spai = Spai;
	}
})(this);<|endoftext|>


<commit_before>var logger     = require('../lib/logger');
var sfclient   = require('../lib/sf-client');
var authServer = require('../lib/auth-server');
var index      = require('../commands/index');
var sfClient   = require('../lib/sf-client');
var cliUtil    = require('../lib/cli-util');
var spawn      = require('child_process').spawn;
var user       = require('../lib/user');

var run = module.exports.run = function(opts) {

  var clientOpts = {};

  if(opts.test) {
    clientOpts.environment = 'sandbox';
  } else if(opts.uri) {
    clientOpts.loginUri = opts.uri;
  }

  sfClient.getClient(clientOpts).then(function(client) {

    var authUri = client.getAuthUri({ responseType: 'token' });

    logger.log('login starts...');

    authServer.on('credentials', function(creds) {
      logger.log('received credentials');
      authServer.close();
      logger.log('shutting down server');
      logger.log('saving credentials for ' + opts.org);
      creds.nick = opts.org;
      creds.org = opts.org;
      user.saveCredential(opts.org, creds).then(function(){
        return index.run({ org: opts.org, oauth: creds}, function(err, res) {
          if(err) {
            logger.error('unable to save index');
            logger.done(false);
          } else {
            logger.done();
          }
        });
      });
      //logger.done();
    });

    authServer.listen(3835, function(){
      logger.log('auth server started');
      logger.log('redirect to the following uri');
      logger.log(authUri);
      // probably only works on Mac right now
      spawn('open', [authUri]);
    });
  });

};

var cli = module.exports.cli = function(program) {
  program.command('login <org>')
    .description('login to a Salesforce organization')
    .option('-t, --test', 'login to a test environment')
    .option('-u, --uri <uri>', 'specify a login uri')
    .action(function(org, opts) {
      if(!org) cliUtil.fail('no org name specified');
      opts.org = org;
      run(opts);
    });
};
<commit_msg>adding the environment and login uri to the oauth #4<commit_after>var logger     = require('../lib/logger');
var sfclient   = require('../lib/sf-client');
var authServer = require('../lib/auth-server');
var index      = require('../commands/index');
var sfClient   = require('../lib/sf-client');
var cliUtil    = require('../lib/cli-util');
var spawn      = require('child_process').spawn;
var user       = require('../lib/user');

var run = module.exports.run = function(opts) {

  var clientOpts = {};

  if(opts.test) {
    clientOpts.environment = 'sandbox';
  } else if(opts.uri) {
    clientOpts.loginUri = opts.uri;
  }

  sfClient.getClient(clientOpts).then(function(client) {

    var authUri = client.getAuthUri({ responseType: 'token' });

    logger.log('login starts...');

    authServer.on('credentials', function(creds) {
      logger.log('received credentials');
      authServer.close();
      logger.log('shutting down server');
      logger.log('saving credentials for ' + opts.org);
      creds.nick = opts.org;
      creds.org = opts.org;
      creds.environment = clientOpts.environment;
      creds.loginUri = opts.uri;
      user.saveCredential(opts.org, creds).then(function(){
        return index.run({ org: opts.org, oauth: creds}, function(err, res) {
          if(err) {
            logger.error('unable to save index');
            logger.done(false);
          } else {
            logger.done();
          }
        });
      });
      //logger.done();
    });

    authServer.listen(3835, function(){
      logger.log('auth server started');
      logger.log('redirect to the following uri');
      logger.log(authUri);
      // probably only works on Mac right now
      spawn('open', [authUri]);
    });
  });

};

var cli = module.exports.cli = function(program) {
  program.command('login <org>')
    .description('login to a Salesforce organization')
    .option('-t, --test', 'login to a test environment')
    .option('-u, --uri <uri>', 'specify a login uri')
    .action(function(org, opts) {
      if(!org) cliUtil.fail('no org name specified');
      opts.org = org;
      run(opts);
    });
};
<|endoftext|>


<commit_before>'use strict';

var chalk = require('chalk');
var RSVP = require('rsvp');
var superstatic = require('superstatic').server;

var Command = require('../lib/command');
var FirebaseError = require('../lib/error');
var logger = require('../lib/logger');
var requireConfig = require('../lib/requireConfig');
var utils = require('../lib/utils');

var MAX_PORT_ATTEMPTS = 10;

var _attempts = 0;
var startServer = function(options) {
  var server = superstatic({
    debug: true,
    port: options.port,
    host: options.host,
    config: options.config.get('hosting'),
    stack: 'strict'
  }).listen(function() {
    logger.info();
    logger.info('Server listening at: ' + chalk.underline(chalk.bold('http://' + options.host + ':' + options.port)));
  });

  server.on('error', function(err) {
    if (err.code === 'EADDRINUSE') {
      var message = 'Port ' + options.port + ' is not available.';
      if (_attempts < MAX_PORT_ATTEMPTS) {
        utils.logWarning(message + ' Trying another port...');
        options.port++;
        _attempts++;
        startServer(options);
      } else {
        utils.logWarning(message);
        throw new FirebaseError('Could not find an open port for development server.', {exit: 1});
      }
    }
  });
};

module.exports = new Command('serve')
  .description('start a local server for your static assets')
  .option('-p, --port <port>', 'the port on which to listen (default: 5000)', 5000)
  .option('-o, --host <host>', 'the host on which to listen (default: localhost)', 'localhost')
  .before(requireConfig)
  .action(function(options) {
    logger.info('Starting Firebase development server...');
    startServer(options);

    return new RSVP.Promise(function(resolve) {
      process.on('SIGINT', function() {
        logger.info('Shutting down...');
        resolve();
      });
    });
  });
<commit_msg>Make firebase serve compatible with non-project directories. (#74)<commit_after>'use strict';

var chalk = require('chalk');
var RSVP = require('rsvp');
var superstatic = require('superstatic').server;

var Command = require('../lib/command');
var FirebaseError = require('../lib/error');
var logger = require('../lib/logger');
var utils = require('../lib/utils');

var MAX_PORT_ATTEMPTS = 10;

var _attempts = 0;
var startServer = function(options) {
  var config = options.config ? options.config.get('hosting') : {public: '.'};
  var server = superstatic({
    debug: true,
    port: options.port,
    host: options.host,
    config: config,
    stack: 'strict'
  }).listen(function() {
    if (config.public && config.public !== '.') {
      logger.info(chalk.bold('Public Directory:'), config.public);
    }
    logger.info();
    logger.info('Server listening at: ' + chalk.underline(chalk.bold('http://' + options.host + ':' + options.port)));
  });

  server.on('error', function(err) {
    if (err.code === 'EADDRINUSE') {
      var message = 'Port ' + options.port + ' is not available.';
      if (_attempts < MAX_PORT_ATTEMPTS) {
        utils.logWarning(message + ' Trying another port...');
        options.port++;
        _attempts++;
        startServer(options);
      } else {
        utils.logWarning(message);
        throw new FirebaseError('Could not find an open port for development server.', {exit: 1});
      }
    }
  });
};

module.exports = new Command('serve')
  .description('start a local server for your static assets')
  .option('-p, --port <port>', 'the port on which to listen (default: 5000)', 5000)
  .option('-o, --host <host>', 'the host on which to listen (default: localhost)', 'localhost')
  .action(function(options) {
    logger.info('Starting Firebase development server...');
    logger.info();

    if (options.config) {
      logger.info(chalk.bold('Project Directory:'), options.config.projectDir);
    } else {
      utils.logWarning('No Firebase project directory detected. Serving static content from ' + chalk.bold(options.cwd || process.cwd()));
    }

    startServer(options);

    return new RSVP.Promise(function(resolve) {
      process.on('SIGINT', function() {
        logger.info('Shutting down...');
        resolve();
      });
    });
  });
<|endoftext|>


<commit_before>
// Simple logger
html2canvas.prototype.log = function(a){

    if (this.opts.logging){

        var logger = window.console.log || function(log){
            alert(log);
        };
    /*
        if (typeof(window.console) != "undefined" && console.log){
            console.log(a);
        }else{
            alert(a);
        }*/
    }
}

/**
 * Function to provide bounds for element
 * @return {Bounds} object with position and dimension information
 */
html2canvas.prototype.getBounds = function(el){

    window.scroll(0,0);

    if (el.getBoundingClientRect){
        var bounds = el.getBoundingClientRect();
        bounds.top = bounds.top;
        bounds.left = bounds.left;
        return bounds;
    }else{

        // TODO remove jQuery dependancy
        var p = $(el).offset();

        return {
            left: p.left + parseInt(this.getCSS(el,"border-left-width"),10),
            top: p.top + parseInt(this.getCSS(el,"border-top-width"),10),
            width:$(el).innerWidth(),
            height:$(el).innerHeight()
        }

    }
}

/*
 * Function for looping through array
 */
html2canvas.prototype.each = function(arrayLoop,callbackFunc){
    callbackFunc = callbackFunc || function(){};
    for (var i=0;i<arrayLoop.length;i++){
        callbackFunc(i,arrayLoop[i]);
    }
}

/*
 * Function to get childNodes of an element in the order they should be rendered (based on z-index)
 * reference http://www.w3.org/TR/CSS21/zindex.html
 */

html2canvas.prototype.contentsInZ = function(el){

    // TODO remove jQuery dependency

    var contents = $(el).contents();

    return contents;

}

/*
 * Function for fetching the element attribute
 */
html2canvas.prototype.getAttr = function(el,attribute){
    return el.getAttribute(attribute);
//return $(el).attr(attribute);
}

/*
 * Function to extend object
 */
html2canvas.prototype.extendObj = function(options,defaults){
    for (var key in options){
        defaults[key] = options[key];
    }
    return defaults;
}

html2canvas.prototype.leadingZero = function(num,size){

    var s = "000000000" + num;
    return s.substr(s.length-size);
}

html2canvas.prototype.formatZ = function(zindex,position,parentZ,parentNode){

    if (!parentZ){
        parentZ = "0";
    }

    if (position!="static" && parentZ.charAt(0)=="0"){
        this.needReorder = true;
        parentZ = "1"+parentZ.slice(1);
    }

    if (zindex=="auto"){
        var parentPosition = this.getCSS(parentNode,"position");
        if (parentPosition!="static" && typeof parentPosition != "undefined"){
            zindex = 0;
        }else{
            return parentZ;
        }
    }

    var b = this.leadingZero(this.numDraws,9);

    var s = this.leadingZero(zindex+1,9);

    // var s = "000000000" + num;
    return parentZ+""+""+s+""+b;

}

/*
 * Get element childNodes
 */

html2canvas.prototype.getContents = function(el){
    return (el.nodeName ==  "iframe" ) ?
    el.contentDocument || el.contentWindow.document :
    el.childNodes;
}

/*
 * Function for fetching the css attribute
 * TODO remove jQuery dependancy
 */
html2canvas.prototype.getCSS = function(el,attribute){
    return $(el).css(attribute);
}<commit_msg>added break functionality to each and IE fallback for Array indexOf<commit_after>
// Simple logger
html2canvas.prototype.log = function(a){

    if (this.opts.logging){

        var logger = window.console.log || function(log){
            alert(log);
        };
    /*
        if (typeof(window.console) != "undefined" && console.log){
            console.log(a);
        }else{
            alert(a);
        }*/
    }
}

/**
 * Function to provide bounds for element
 * @return {Bounds} object with position and dimension information
 */
html2canvas.prototype.getBounds = function(el){

    window.scroll(0,0);

    if (el.getBoundingClientRect){
        var bounds = el.getBoundingClientRect();
        bounds.top = bounds.top;
        bounds.left = bounds.left;
        return bounds;
    }else{

        // TODO remove jQuery dependancy
        var p = $(el).offset();

        return {
            left: p.left + parseInt(this.getCSS(el,"border-left-width"),10),
            top: p.top + parseInt(this.getCSS(el,"border-top-width"),10),
            width:$(el).innerWidth(),
            height:$(el).innerHeight()
        }

    }
}

/*
 * Function for looping through array
 */
html2canvas.prototype.each = function(arrayLoop,callbackFunc){
    callbackFunc = callbackFunc || function(){};
    for (var i=0;i<arrayLoop.length;i++){
        if (callbackFunc(i,arrayLoop[i]) === false) return;
    }
}

/*
 * Function to get childNodes of an element in the order they should be rendered (based on z-index)
 * reference http://www.w3.org/TR/CSS21/zindex.html
 */

html2canvas.prototype.contentsInZ = function(el){

    // TODO remove jQuery dependency

    var contents = $(el).contents();

    return contents;

}

/*
 * Function for fetching the element attribute
 */
html2canvas.prototype.getAttr = function(el,attribute){
    return el.getAttribute(attribute);
//return $(el).attr(attribute);
}

/*
 * Function to extend object
 */
html2canvas.prototype.extendObj = function(options,defaults){
    for (var key in options){
        defaults[key] = options[key];
    }
    return defaults;
}

html2canvas.prototype.leadingZero = function(num,size){

    var s = "000000000" + num;
    return s.substr(s.length-size);
}

html2canvas.prototype.formatZ = function(zindex,position,parentZ,parentNode){

    if (!parentZ){
        parentZ = "0";
    }

    if (position!="static" && parentZ.charAt(0)=="0"){
        this.needReorder = true;
        parentZ = "1"+parentZ.slice(1);
    }

    if (zindex=="auto"){
        var parentPosition = this.getCSS(parentNode,"position");
        if (parentPosition!="static" && typeof parentPosition != "undefined"){
            zindex = 0;
        }else{
            return parentZ;
        }
    }

    var b = this.leadingZero(this.numDraws,9);

    var s = this.leadingZero(zindex+1,9);

    // var s = "000000000" + num;
    return parentZ+""+""+s+""+b;

}

/*
 * Get element childNodes
 */

html2canvas.prototype.getContents = function(el){
    return (el.nodeName ==  "iframe" ) ?
    el.contentDocument || el.contentWindow.document :
    el.childNodes;
}

/*
 * Function for fetching the css attribute
 * TODO remove jQuery dependancy
 */
html2canvas.prototype.getCSS = function(el,attribute){
    return $(el).css(attribute);
}

html2canvas.prototype.getIndex = function(array,src){

    if (array.indexOf){
        return array.indexOf(src);
    }else{
        for(var i = 0; i < array.length; i++){
            if(this[i] == src) return i;
        }
        return -1;
    }

}<|endoftext|>