<commit_before>/** Initialization code for the project overview page. */
'use strict';

var $ = require('jquery');
require('jquery-tagsinput');
require('bootstrap-editable');
require('js/osfToggleHeight');

var m = require('mithril');
var Fangorn = require('js/fangorn').Fangorn;
var Raven = require('raven-js');
var lodashGet  = require('lodash.get');
require('truncate');

var $osf = require('js/osfHelpers');
var LogFeed = require('js/components/logFeed');
var pointers = require('js/pointers');
var Comment = require('js/comment'); //jshint ignore:line
var NodeControl = require('js/nodeControl');
var CitationList = require('js/citationList');
var CitationWidget = require('js/citationWidget');
var mathrender = require('js/mathrender');
var md = require('js/markdown').full;
var oldMd = require('js/markdown').old;
var AddProject = require('js/addProjectPlugin');
var mHelpers = require('js/mithrilHelpers');
var SocialShare = require('js/components/socialshare');

var ctx = window.contextVars;
var node = window.contextVars.node;
var nodeApiUrl = ctx.node.urls.api;
var nodeCategories = ctx.nodeCategories || [];

// Listen for the nodeLoad event (prevents multiple requests for data)
$('body').on('nodeLoad', function(event, data) {
    if (!data.node.is_retracted) {
        // Initialize controller for "Add Links" modal
        new pointers.PointerManager('#addPointer', window.contextVars.node.title);
    }
    // Initialize CitationWidget if user isn't viewing through an anonymized VOL
    if (!data.node.anonymous && !data.node.is_retracted) {
        var citations = data.node.alternative_citations;
        new CitationList('#citationList', citations, data.user);
        new CitationWidget('#citationStyleInput', '#citationText');
    }
    // Initialize nodeControl
    new NodeControl.NodeControl('#projectScope', data, {categories: nodeCategories});
});

// Initialize comment pane w/ its viewmodel
var $comments = $('.comments');
if ($comments.length) {
    var options = {
        nodeId : window.contextVars.node.id,
        nodeApiUrl: window.contextVars.node.urls.api,
        isRegistration: window.contextVars.node.isRegistration,
        page: 'node',
        rootId: window.contextVars.node.id,
        fileId: null,
        canComment: window.contextVars.currentUser.canComment,
        hasChildren: window.contextVars.node.hasChildren,
        currentUser: window.contextVars.currentUser,
        pageTitle: window.contextVars.node.title,
        inputSelector: '.atwho-input'
    };
    Comment.init('#commentsLink', '.comment-pane', options);
}
var institutionLogos = {
    controller: function(args){
        var self = this;
        self.institutions = args.institutions;
        self.nLogos = self.institutions.length;
        self.side = self.nLogos > 1 ? (self.nLogos === 2 ? '50px' : '35px') : '75px';
        self.width = self.nLogos > 1 ? (self.nLogos === 2 ? '115px' : '86px') : '75px';
        self.makeLogo = function(institution){
            return m('a', {href: '/institutions/' + institution.id},
                m('img', {
                    height: self.side, width: self.side,
                    style: {margin: '3px'},
                    title: institution.name,
                    src: institution.logo_path
                })
            );
        };
    },
    view: function(ctrl, args){
        var tooltips = function(){
            $('[data-toggle="tooltip"]').tooltip();
        };
        var instCircles = $.map(ctrl.institutions, ctrl.makeLogo);
        if (instCircles.length > 4){
            instCircles[3] = m('.fa.fa-plus-square-o', {
                style: {margin: '6px', fontSize: '250%', verticalAlign: 'middle'},
            });
            instCircles.splice(4);
        }

        return m('', {style: {float: 'left', width: ctrl.width, textAlign: 'center', marginRight: '10px'}, config: tooltips}, instCircles);
    }
};

$(document).ready(function () {

    var AddComponentButton = m.component(AddProject, {
        buttonTemplate: m('.btn.btn-sm.btn-default[data-toggle="modal"][data-target="#addSubComponent"]', {onclick: function() {
            $osf.trackClick('project-dashboard', 'add-component', 'open-add-project-modal');
        }}, 'Add Component'),
        modalID: 'addSubComponent',
        title: 'Create new component',
        parentID: window.contextVars.node.id,
        parentTitle: window.contextVars.node.title,
        categoryList: nodeCategories,
        stayCallback: function() {
            // We need to reload because the components list needs to be re-rendered serverside
            window.location.reload();
        },
        trackingCategory: 'project-dashboard',
        trackingAction: 'add-component',
        contributors: window.contextVars.node.contributors,
        currentUserCanEdit: window.contextVars.currentUser.canEdit
    });
    var newComponentElem = document.getElementById('newComponent');

    m.startComputation();

    if (ctx.node.institutions.length && !ctx.node.anonymous && !ctx.node.isRetracted) {
        m.mount(document.getElementById('instLogo'), m.component(institutionLogos, {institutions: window.contextVars.node.institutions}));
    }
    $('#contributorsList').osfToggleHeight();

    if (!ctx.node.isRetracted) {
        // Recent Activity widget
        m.mount(document.getElementById('logFeed'), m.component(LogFeed.LogFeed, {node: node}));

        // Treebeard Files view
        $.ajax({
            url:  nodeApiUrl + 'files/grid/'
        }).done(function (data) {
            var fangornOpts = {
                divID: 'treeGrid',
                filesData: data.data,
                allowMove: !node.isRegistration,
                uploads : true,
                showFilter : true,
                placement: 'dashboard',
                title : undefined,
                filterFullWidth : true, // Make the filter span the entire row for this view
                xhrconfig: $osf.setXHRAuthorization,
                columnTitles : function () {
                    return [
                        {
                            title: 'Name',
                            width : '70%',
                            sort : true,
                            sortType : 'text'
                        },
                        {
                            title: 'Modified',
                            width : '30%',
                            sort : true,
                            sortType : 'text'
                        }
                    ];
                },
                resolveRows : function (item) {
                    var tb = this;
                    item.css = '';
                    if(tb.isMultiselected(item.id)){
                        item.css = 'fangorn-selected';
                    }
                    if(item.data.permissions && !item.data.permissions.view){
                        item.css += ' tb-private-row';
                    }
                    var defaultColumns = [
                                {
                                data: 'name',
                                folderIcons: true,
                                filter: true,
                                custom: Fangorn.DefaultColumns._fangornTitleColumn},
                                {
                                data: 'modified',
                                folderIcons: false,
                                filter: false,
                                custom: Fangorn.DefaultColumns._fangornModifiedColumn
                            }];
                    if (item.parentID) {
                        item.data.permissions = item.data.permissions || item.parent().data.permissions;
                        if (item.data.kind === 'folder') {
                            item.data.accept = item.data.accept || item.parent().data.accept;
                        }
                    }
                    if(item.data.uploadState && (item.data.uploadState() === 'pending' || item.data.uploadState() === 'uploading')){
                        return Fangorn.Utils.uploadRowTemplate.call(tb, item);
                    }

                    var configOption = Fangorn.Utils.resolveconfigOption.call(this, item, 'resolveRows', [item]);
                    return configOption || defaultColumns;
                }
            };
            var filebrowser = new Fangorn(fangornOpts);
            if (newComponentElem) {
                m.mount(newComponentElem, AddComponentButton);
            };
            m.endComputation();
        });
    } else {
        if (newComponentElem) {
            m.mount(newComponentElem, AddComponentButton);
        };
        m.endComputation();
    }

    // Tooltips
    $('[data-toggle="tooltip"]').tooltip({container: 'body'});

    // Tag input
    $('#node-tags').tagsInput({
        width: '100%',
        interactive: window.contextVars.currentUser.canEdit,
        maxChars: 128,
        defaultText: 'add a tag to enhance discoverability',
        onAddTag: function(tag) {
            $('#node-tags_tag').attr('data-default', 'add a tag');
            var url = nodeApiUrl + 'tags/';
            var data = {tag: tag};
            var request = $osf.postJSON(url, data);
            request.fail(function(xhr, textStatus, error) {
                Raven.captureMessage('Failed to add tag', {
                    extra: {
                        tag: tag, url: url, textStatus: textStatus, error: error
                    }
                });
            });
        },
        onRemoveTag: function(tag) {
            var url = nodeApiUrl + 'tags/';
            // Don't try to delete a blank tag (would result in a server error)
            if (!tag) {
                return false;
            }
            var request = $osf.ajaxJSON('DELETE', url, {'data': {'tag': tag}});
            request.fail(function(xhr, textStatus, error) {
                // Suppress "tag not found" errors, as the end result is what the user wanted (tag is gone)- eg could be because two people were working at same time
                if (xhr.status !== 409) {
                    $osf.growl('Error', 'Could not remove tag');
                    Raven.captureMessage('Failed to remove tag', {
                        extra: {
                            tag: tag, url: url, textStatus: textStatus, error: error
                        }
                    });
                }
            });
        }
    });

    // allows inital default message to fit on empty tag
    if(!$('.tag').length){
        $('#node-tags_tag').css('width', '250px');
    }

    $('#addPointer').on('shown.bs.modal', function(){
        if(!$osf.isIE()){
            $('#addPointer input').focus();
        }
    });

    // Limit the maximum length that you can type when adding a tag
    $('#node-tags_tag').attr('maxlength', '128');

    // Wiki widget markdown rendering
    if (ctx.wikiWidget) {
        // Render math in the wiki widget
        var markdownElement = $('#markdownRender');
        mathrender.mathjaxify(markdownElement);

        // Render the raw markdown of the wiki
        var request = $.ajax({
            url: ctx.urls.wikiContent
        });
        request.done(function(resp) {
            var rawText = resp.wiki_content || '*Add important information, links, or images here to describe your project.*';
            var renderedText = ctx.renderedBeforeUpdate ? oldMd.render(rawText) : md.render(rawText);
            var truncatedText = $.truncate(renderedText, {length: 400});
            markdownElement.html(truncatedText);
            mathrender.mathjaxify(markdownElement);
            markdownElement.show();
        });
    }

    // Remove delete UI if not contributor
    if (!window.contextVars.currentUser.canEdit || window.contextVars.node.isRegistration) {
        $('a[title="Removing tag"]').remove();
        $('span.tag span').each(function(idx, elm) {
            $(elm).text($(elm).text().replace(/\s*$/, ''));
        });
    }

    if (window.contextVars.node.isPublic && !window.contextVars.node.isRetracted) {
        m.mount(document.getElementById('shareButtonsPopover'),
                m.component(SocialShare.ShareButtonsPopover,
                    {title: window.contextVars.node.title, url: window.location.href}));
    }
});
<commit_msg>Group mithril mounts in same block, remove obsolete isretract checks<commit_after>/** Initialization code for the project overview page. */
'use strict';

var $ = require('jquery');
require('jquery-tagsinput');
require('bootstrap-editable');
require('js/osfToggleHeight');

var m = require('mithril');
var Fangorn = require('js/fangorn').Fangorn;
var Raven = require('raven-js');
var lodashGet  = require('lodash.get');
require('truncate');

var $osf = require('js/osfHelpers');
var LogFeed = require('js/components/logFeed');
var pointers = require('js/pointers');
var Comment = require('js/comment'); //jshint ignore:line
var NodeControl = require('js/nodeControl');
var CitationList = require('js/citationList');
var CitationWidget = require('js/citationWidget');
var mathrender = require('js/mathrender');
var md = require('js/markdown').full;
var oldMd = require('js/markdown').old;
var AddProject = require('js/addProjectPlugin');
var mHelpers = require('js/mithrilHelpers');
var SocialShare = require('js/components/socialshare');

var ctx = window.contextVars;
var node = window.contextVars.node;
var nodeApiUrl = ctx.node.urls.api;
var nodeCategories = ctx.nodeCategories || [];

// Listen for the nodeLoad event (prevents multiple requests for data)
$('body').on('nodeLoad', function(event, data) {
    if (!data.node.is_retracted) {
        // Initialize controller for "Add Links" modal
        new pointers.PointerManager('#addPointer', window.contextVars.node.title);
    }
    // Initialize CitationWidget if user isn't viewing through an anonymized VOL
    if (!data.node.anonymous && !data.node.is_retracted) {
        var citations = data.node.alternative_citations;
        new CitationList('#citationList', citations, data.user);
        new CitationWidget('#citationStyleInput', '#citationText');
    }
    // Initialize nodeControl
    new NodeControl.NodeControl('#projectScope', data, {categories: nodeCategories});
});

// Initialize comment pane w/ its viewmodel
var $comments = $('.comments');
if ($comments.length) {
    var options = {
        nodeId : window.contextVars.node.id,
        nodeApiUrl: window.contextVars.node.urls.api,
        isRegistration: window.contextVars.node.isRegistration,
        page: 'node',
        rootId: window.contextVars.node.id,
        fileId: null,
        canComment: window.contextVars.currentUser.canComment,
        hasChildren: window.contextVars.node.hasChildren,
        currentUser: window.contextVars.currentUser,
        pageTitle: window.contextVars.node.title,
        inputSelector: '.atwho-input'
    };
    Comment.init('#commentsLink', '.comment-pane', options);
}
var institutionLogos = {
    controller: function(args){
        var self = this;
        self.institutions = args.institutions;
        self.nLogos = self.institutions.length;
        self.side = self.nLogos > 1 ? (self.nLogos === 2 ? '50px' : '35px') : '75px';
        self.width = self.nLogos > 1 ? (self.nLogos === 2 ? '115px' : '86px') : '75px';
        self.makeLogo = function(institution){
            return m('a', {href: '/institutions/' + institution.id},
                m('img', {
                    height: self.side, width: self.side,
                    style: {margin: '3px'},
                    title: institution.name,
                    src: institution.logo_path
                })
            );
        };
    },
    view: function(ctrl, args){
        var tooltips = function(){
            $('[data-toggle="tooltip"]').tooltip();
        };
        var instCircles = $.map(ctrl.institutions, ctrl.makeLogo);
        if (instCircles.length > 4){
            instCircles[3] = m('.fa.fa-plus-square-o', {
                style: {margin: '6px', fontSize: '250%', verticalAlign: 'middle'},
            });
            instCircles.splice(4);
        }

        return m('', {style: {float: 'left', width: ctrl.width, textAlign: 'center', marginRight: '10px'}, config: tooltips}, instCircles);
    }
};

$(document).ready(function () {

    var AddComponentButton = m.component(AddProject, {
        buttonTemplate: m('.btn.btn-sm.btn-default[data-toggle="modal"][data-target="#addSubComponent"]', {onclick: function() {
            $osf.trackClick('project-dashboard', 'add-component', 'open-add-project-modal');
        }}, 'Add Component'),
        modalID: 'addSubComponent',
        title: 'Create new component',
        parentID: window.contextVars.node.id,
        parentTitle: window.contextVars.node.title,
        categoryList: nodeCategories,
        stayCallback: function() {
            // We need to reload because the components list needs to be re-rendered serverside
            window.location.reload();
        },
        trackingCategory: 'project-dashboard',
        trackingAction: 'add-component',
        contributors: window.contextVars.node.contributors,
        currentUserCanEdit: window.contextVars.currentUser.canEdit
    });

    if (!ctx.node.isRetracted) {
        m.startComputation();

        if (ctx.node.institutions.length && !ctx.node.anonymous) {
            m.mount(document.getElementById('instLogo'), m.component(institutionLogos, {institutions: window.contextVars.node.institutions}));
        }
        $('#contributorsList').osfToggleHeight();

        // Recent Activity widget
        m.mount(document.getElementById('logFeed'), m.component(LogFeed.LogFeed, {node: node}));

        // Treebeard Files view
        $.ajax({
            url:  nodeApiUrl + 'files/grid/'
        }).done(function (data) {
            var fangornOpts = {
                divID: 'treeGrid',
                filesData: data.data,
                allowMove: !node.isRegistration,
                uploads : true,
                showFilter : true,
                placement: 'dashboard',
                title : undefined,
                filterFullWidth : true, // Make the filter span the entire row for this view
                xhrconfig: $osf.setXHRAuthorization,
                columnTitles : function () {
                    return [
                        {
                            title: 'Name',
                            width : '70%',
                            sort : true,
                            sortType : 'text'
                        },
                        {
                            title: 'Modified',
                            width : '30%',
                            sort : true,
                            sortType : 'text'
                        }
                    ];
                },
                resolveRows : function (item) {
                    var tb = this;
                    item.css = '';
                    if(tb.isMultiselected(item.id)){
                        item.css = 'fangorn-selected';
                    }
                    if(item.data.permissions && !item.data.permissions.view){
                        item.css += ' tb-private-row';
                    }
                    var defaultColumns = [
                                {
                                data: 'name',
                                folderIcons: true,
                                filter: true,
                                custom: Fangorn.DefaultColumns._fangornTitleColumn},
                                {
                                data: 'modified',
                                folderIcons: false,
                                filter: false,
                                custom: Fangorn.DefaultColumns._fangornModifiedColumn
                            }];
                    if (item.parentID) {
                        item.data.permissions = item.data.permissions || item.parent().data.permissions;
                        if (item.data.kind === 'folder') {
                            item.data.accept = item.data.accept || item.parent().data.accept;
                        }
                    }
                    if(item.data.uploadState && (item.data.uploadState() === 'pending' || item.data.uploadState() === 'uploading')){
                        return Fangorn.Utils.uploadRowTemplate.call(tb, item);
                    }

                    var configOption = Fangorn.Utils.resolveconfigOption.call(this, item, 'resolveRows', [item]);
                    return configOption || defaultColumns;
                }
            };
            var filebrowser = new Fangorn(fangornOpts);
            var newComponentElem = document.getElementById('newComponent');
            if (window.contextVars.node.isPublic) {
                m.mount(document.getElementById('shareButtonsPopover'),
                        m.component(SocialShare.ShareButtonsPopover,
                            {title: window.contextVars.node.title, url: window.location.href}));
            }
            if (newComponentElem) {
                m.mount(newComponentElem, AddComponentButton);
            };
            m.endComputation();
        });

    }

    // Tooltips
    $('[data-toggle="tooltip"]').tooltip({container: 'body'});

    // Tag input
    $('#node-tags').tagsInput({
        width: '100%',
        interactive: window.contextVars.currentUser.canEdit,
        maxChars: 128,
        defaultText: 'add a tag to enhance discoverability',
        onAddTag: function(tag) {
            $('#node-tags_tag').attr('data-default', 'add a tag');
            var url = nodeApiUrl + 'tags/';
            var data = {tag: tag};
            var request = $osf.postJSON(url, data);
            request.fail(function(xhr, textStatus, error) {
                Raven.captureMessage('Failed to add tag', {
                    extra: {
                        tag: tag, url: url, textStatus: textStatus, error: error
                    }
                });
            });
        },
        onRemoveTag: function(tag) {
            var url = nodeApiUrl + 'tags/';
            // Don't try to delete a blank tag (would result in a server error)
            if (!tag) {
                return false;
            }
            var request = $osf.ajaxJSON('DELETE', url, {'data': {'tag': tag}});
            request.fail(function(xhr, textStatus, error) {
                // Suppress "tag not found" errors, as the end result is what the user wanted (tag is gone)- eg could be because two people were working at same time
                if (xhr.status !== 409) {
                    $osf.growl('Error', 'Could not remove tag');
                    Raven.captureMessage('Failed to remove tag', {
                        extra: {
                            tag: tag, url: url, textStatus: textStatus, error: error
                        }
                    });
                }
            });
        }
    });

    // allows inital default message to fit on empty tag
    if(!$('.tag').length){
        $('#node-tags_tag').css('width', '250px');
    }

    $('#addPointer').on('shown.bs.modal', function(){
        if(!$osf.isIE()){
            $('#addPointer input').focus();
        }
    });

    // Limit the maximum length that you can type when adding a tag
    $('#node-tags_tag').attr('maxlength', '128');

    // Wiki widget markdown rendering
    if (ctx.wikiWidget) {
        // Render math in the wiki widget
        var markdownElement = $('#markdownRender');
        mathrender.mathjaxify(markdownElement);

        // Render the raw markdown of the wiki
        var request = $.ajax({
            url: ctx.urls.wikiContent
        });
        request.done(function(resp) {
            var rawText = resp.wiki_content || '*Add important information, links, or images here to describe your project.*';
            var renderedText = ctx.renderedBeforeUpdate ? oldMd.render(rawText) : md.render(rawText);
            var truncatedText = $.truncate(renderedText, {length: 400});
            markdownElement.html(truncatedText);
            mathrender.mathjaxify(markdownElement);
            markdownElement.show();
        });
    }

    // Remove delete UI if not contributor
    if (!window.contextVars.currentUser.canEdit || window.contextVars.node.isRegistration) {
        $('a[title="Removing tag"]').remove();
        $('span.tag span').each(function(idx, elm) {
            $(elm).text($(elm).text().replace(/\s*$/, ''));
        });
    }
});
<|endoftext|>


<commit_before>import { TOGGLE_SIGNUP_FORM, CHANGE_SIGNUP_FIELD, ADD_SIGNUP_ERROR, CLEAR_ERRORS, SIGNUP_SUCCESS, SIGNUP_FAILURE, LOGOUT, CHANGE_LOGIN_FIELD, LOGIN_SUCCESS, LOGIN_FAILURE } from '../constants';

export const initialState = {
  signupForm: {
    activeLink: 'customer',
    firstName: '',
    email: '',
    lastName: '',
    businessName: '',
    password: '',
    passwordConfirmation: '',
  },
  loginForm: {
    email: '',
    password: '',
  },
  formErrors: {
  },
  loggedInUser: {
    firstName: '',
    id: 0,
  },
};

export const auth = (state = initialState, action) => {
  switch (action.type) {
    case TOGGLE_SIGNUP_FORM:
      return ({ ...state, signupForm: { ...state.signupForm, activeLink: action.link } });
    case CHANGE_SIGNUP_FIELD:
      return ({ ...state, signupForm: { ...state.signupForm, [action.field]: action.value } });
    case CHANGE_LOGIN_FIELD:
      return ({ ...state, loginForm: { ...state.loginForm, [action.name]: action.value } });
    case ADD_SIGNUP_ERROR:
      return ({ ...state, formErrors: { ...state.formErrors, [action.error]: action.message } });
    case CLEAR_ERRORS:
      return ({ ...state, formErrors: {} });
    case SIGNUP_SUCCESS:
      return ({
        ...state,
        loggedInUser: { ...state.loggedInUser, ...action.data.user },
      });
    case LOGIN_SUCCESS:
      return ({
        ...state,
        loggedInUser: { ...state.loggedInUser, ...action.data.user },
      });
    case SIGNUP_FAILURE:
      return ({ ...state, formErrors: { ...state.formErrors, userExists: 'A user with that email exists' } });
    case LOGIN_FAILURE:
      return ({ ...state, formErrors: { ...state.formErrors, invalidPass: 'Invalid credentials' } });
    case LOGOUT:
      return ({ ...state, loggedInUser: {} });
    default:
      return state;
  }
};
<commit_msg>Clear signup form after signup<commit_after>import { TOGGLE_SIGNUP_FORM, CHANGE_SIGNUP_FIELD, ADD_SIGNUP_ERROR, CLEAR_ERRORS, SIGNUP_SUCCESS, SIGNUP_FAILURE, LOGOUT, CHANGE_LOGIN_FIELD, LOGIN_SUCCESS, LOGIN_FAILURE } from '../constants';

export const initialState = {
  signupForm: {
    activeLink: 'customer',
    firstName: '',
    email: '',
    lastName: '',
    businessName: '',
    password: '',
    passwordConfirmation: '',
  },
  loginForm: {
    email: '',
    password: '',
  },
  formErrors: {
  },
  loggedInUser: {
    firstName: '',
    id: 0,
  },
};

export const auth = (state = initialState, action) => {
  switch (action.type) {
    case TOGGLE_SIGNUP_FORM:
      return ({ ...state, signupForm: { ...state.signupForm, activeLink: action.link } });
    case CHANGE_SIGNUP_FIELD:
      return ({ ...state, signupForm: { ...state.signupForm, [action.field]: action.value } });
    case CHANGE_LOGIN_FIELD:
      return ({ ...state, loginForm: { ...state.loginForm, [action.name]: action.value } });
    case ADD_SIGNUP_ERROR:
      return ({ ...state, formErrors: { ...state.formErrors, [action.error]: action.message } });
    case CLEAR_ERRORS:
      return ({ ...state, formErrors: {} });
    case SIGNUP_SUCCESS:
      return ({
        ...state,
        loggedInUser: { ...state.loggedInUser, ...action.data.user },
        signupForm: { ...state.signupForm, ...initialState.signupForm },
      });
    case LOGIN_SUCCESS:
      return ({
        ...state,
        loggedInUser: { ...state.loggedInUser, ...action.data.user },
      });
    case SIGNUP_FAILURE:
      return ({ ...state, formErrors: { ...state.formErrors, userExists: 'A user with that email exists' } });
    case LOGIN_FAILURE:
      return ({ ...state, formErrors: { ...state.formErrors, invalidPass: 'Invalid credentials' } });
    case LOGOUT:
      return ({ ...state, loggedInUser: {} });
    default:
      return state;
  }
};
<|endoftext|>


<commit_before>(function (window, document, undefined) {
    var ContactDataServices = window.ContactDataServices = window.ContactDataServices || {};
    window.ContactDataServices = ContactDataServices;

    // Generate the URLs for the various requests
	ContactDataServices.urls = {
		endpoint: "http://int-test-01/capture/v2/address/search",
		construct: {
			address: {
				// Construct the Search URL by appending query, country & token
				search: function(instance){
					var url = ContactDataServices.urls.endpoint;
					url += "?query=" + instance.currentSearchTerm;
					url += "&country=" + instance.currentCountryCode;

					url = ContactDataServices.urls.addToken(url, instance.token);
					return url;
				},
				// Construct the Format URL by appending the token
				format: function(url, instance){
					url = ContactDataServices.urls.addToken(url, instance.token);
					return url;
				}
			}
		},
		// Append the token (this must be specified when initialising)
		addToken: function(url, token){
			return url + "&auth-token=" + token;
		}
	};

	// Integrate with address searching
	ContactDataServices.address = function(options){
		// Build our new instance from user defaults
		var instance = options || {};

		instance.lastSearchTerm = "";
		instance.currentSearchTerm = "";
		instance.lastCountryCode = "";
		instance.currentCountryCode = "";
		instance.currentSearchUrl = "";
		instance.currentFormatUrl = "";
		instance.enabled = true;

		// Initialise this instance
		instance.init = function(){
			if(!instance.token){
				console.log("Please provide a token for ContactDataServices.");
				// Disable searching on this instance
				instance.enabled = false;
			}
			instance.setCountryList();
			if(instance.elements.input){
				instance.input = instance.elements.input;
				instance.input.addEventListener("keyup", instance.search);
			}
		};

		// Main function to search for an address from an input string
		instance.search = function(){
			instance.currentSearchTerm = instance.input.value;
			instance.currentCountryCode = instance.countryList.value;
			// Check is searching is permitted
			if(instance.canSearch()){
				// Abort any outstanding requests
				if(instance.request.currentRequest){
					instance.request.currentRequest.abort();
				}
				// Construct the new Search URL
				var url = ContactDataServices.urls.construct.address.search(instance);

				// Store the last search term
				instance.lastSearchTerm = instance.currentSearchTerm;

				// Hide any previous results
				instance.result.hide();

				// Initiate new Search request
				instance.request.get(url, instance.picklist.show);
			} else {
				// Clear the picklist if the search term is cleared/empty
				instance.picklist.hide();
			}
		};

		instance.setCountryList = function(){
			instance.countryList = instance.elements.countryList;

			// If the user hasn't passed us a country list, then create new list?
			if(!instance.countryList){
				instance.createCountryDropdown();
			}
		};

		// Determine whether searching is currently permitted
		instance.canSearch = function(){
					// If searching on this instance is enabled, and
			return (instance.enabled &&
					// If search term is not empty, and
					instance.currentSearchTerm !== "" &&
					// If search term is not the same as previous search term, and
					instance.lastSearchTerm !== instance.currentSearchTerm &&
					// If the country is not empty
					instance.countryList.value !== "");
		};

		instance.createCountryDropdown = function(){
			// What countries?
			// Where to position it?
		};

		// Get a final (Formatted) address
		instance.format = function(url){
			instance.currentFormatUrl = ContactDataServices.urls.construct.address.format(url, instance);

			// Initiate a new Format request
			instance.request.get(instance.currentFormatUrl, instance.result.show);
		};

		instance.picklist = {
			// Render a picklist of search results
			show: function(items){
				instance.picklist.hide();

				// Get/Create picklist container element
				instance.picklist.container = instance.picklist.container || instance.picklist.createList();

				// Prepend an option for "use address entered"
				instance.picklist.createUseAddressEntered();

				if(items.results.length > 0){
					// Iterate over and show results
					items.results.forEach(function(item, i){
						// Create a new item/row in the picklist
						var listItem = instance.picklist.createListItem(item);
						instance.picklist.container.appendChild(listItem);

						// Listen for selection on this item
						instance.picklist.listen(listItem);
					});
				}
			},
			// Remove the picklist
			hide: function(){
				if(instance.picklist.container){
					instance.picklist.container.innerHTML = "";
				}
			},
			// Create a "use address entered" option
			createUseAddressEntered: function(){
				var item = {
					suggestion: "Use address entered",
					format: ""
				};
				var listItem = instance.picklist.createListItem(item);
				instance.picklist.container.appendChild(listItem);
				listItem.addEventListener("click", instance.picklist.useAddressEntered);
			},
			// Use the address entered as the Formatted address
			useAddressEntered: function(){
				var inputData = {
					address: [
						{
							content: instance.currentSearchTerm
						}
					]
				};
				instance.result.show(inputData);
			},
			// Create the picklist container and inject after the input
			createList: function(){
				var list = document.createElement("div");
				list.classList.add("address-picklist");
				// Insert the picklist after the input
				instance.input.parentNode.insertBefore(list, instance.input.nextSibling);
				return list;
			},
			// Create a new picklist item/row
			createListItem: function(item){
				var row = document.createElement("div");
				row.innerHTML = instance.picklist.addMatchingEmphasis(item);
				// Store the Format URL
				row.setAttribute("format", item.format);
				return row;
			},
			// Add emphasis to the picklist items highlighting the match
			addMatchingEmphasis: function(item){
				var highlights = item.matched || [],
                	label = item.suggestion;

                for (i = 0; i < highlights.length; i++) {
                    var replacement = '' + label.substring(highlights[i][0], highlights[i][1]) + '';
                    label = label.substring(0, highlights[i][0]) + replacement + label.substring(highlights[i][1]);
                }

                return label;
			},
			// Listen to a picklist selection
			listen: function(row){
				row.addEventListener("click", instance.picklist.pick);
			},
			// How to handle a picklist selection
			pick: function(){
				instance.format(this.getAttribute("format"));
			}
		};

		instance.result = {
			// Render a Formatted address
			show: function(data){
				instance.picklist.hide();

				if(data.address.length > 0){
					// Get formatted address container element
					instance.result.formattedAddress = instance.elements.formattedAddress;

					// Create an array to hold the hidden input fields
					var inputArray = [];

					// Loop over each formatted address line
					for(var i = 0; i < data.address.length; i++){
						var line = data.address[i];
						// The line object will only have one property, but we don't know the key
						for(var key in line){
							// Create the address line row and add to the DOM
							var row = instance.result.createAddressLine.row(line[key]);
							instance.result.formattedAddress.appendChild(row);

							// Create a hidden input to store the address line as well
							inputArray.push(instance.result.createAddressLine.input(key, line[key]));
						}
					}

					// Write the list of hidden address line inputs to the DOM in one go
					instance.result.renderInputList(inputArray);
				}
			},
			hide: function(){
				instance.elements.formattedAddress.innerHTML = "";
			},
			createAddressLine: {
				// Create a hidden input to store the address line
				input: function(key, value){
					var input = document.createElement("input");
					input.setAttribute("type", "hidden");
					input.setAttribute("name", key);
					input.setAttribute("value", value);
					return input;
				},
				// Create a DOM element to contain the address line
				row: function(value){
					var row = document.createElement("div");
					row.innerHTML = value;
					return row;
				}
			},
			// Write the list of hidden address line inputs to the DOM
			renderInputList: function(inputArray){
				if(inputArray.length > 0){
					for(var i = 0; i < inputArray.length; i++){
						instance.result.formattedAddress.appendChild(inputArray[i]);
					}
				}
			}
		};

		// Use this to initiate and track XMLHttpRequests
		instance.request = {
			currentRequest: null,
			get: function(url, callback){
				instance.request.currentRequest = new XMLHttpRequest();
				instance.request.currentRequest.open('GET', url, true);
				instance.request.currentRequest.timeout = 5000; // 5 seconds

				instance.request.currentRequest.onload = function() {
				  if (instance.request.currentRequest.status >= 200 && instance.request.currentRequest.status < 400) {
				    // Success!
				    var data = JSON.parse(instance.request.currentRequest.responseText);
				    console.log(data);
				    callback(data);
				  } else {
				    // We reached our target server, but it returned an error

				  }
				};

				instance.request.currentRequest.onerror = function() {
				  // There was a connection error of some sort
				};

				instance.request.currentRequest.ontimeout = function() {
				  // There was a connection timeout
				};

				instance.request.currentRequest.send();
			}
		};

		// Initialise this instance of ContactDataServices
		instance.init();

		// Return the instance object to the invoker
		return instance;
	};

})(window, window.document);<commit_msg>Store the picklist items on the instance object<commit_after>(function (window, document, undefined) {
    var ContactDataServices = window.ContactDataServices = window.ContactDataServices || {};
    window.ContactDataServices = ContactDataServices;

    // Generate the URLs for the various requests
	ContactDataServices.urls = {
		endpoint: "http://int-test-01/capture/v2/address/search",
		construct: {
			address: {
				// Construct the Search URL by appending query, country & token
				search: function(instance){
					var url = ContactDataServices.urls.endpoint;
					url += "?query=" + instance.currentSearchTerm;
					url += "&country=" + instance.currentCountryCode;

					url = ContactDataServices.urls.addToken(url, instance.token);
					return url;
				},
				// Construct the Format URL by appending the token
				format: function(url, instance){
					url = ContactDataServices.urls.addToken(url, instance.token);
					return url;
				}
			}
		},
		// Append the token (this must be specified when initialising)
		addToken: function(url, token){
			return url + "&auth-token=" + token;
		}
	};

	// Integrate with address searching
	ContactDataServices.address = function(options){
		// Build our new instance from user defaults
		var instance = options || {};

		instance.lastSearchTerm = "";
		instance.currentSearchTerm = "";
		instance.lastCountryCode = "";
		instance.currentCountryCode = "";
		instance.currentSearchUrl = "";
		instance.currentFormatUrl = "";
		instance.enabled = true;

		// Initialise this instance
		instance.init = function(){
			if(!instance.token){
				console.log("Please provide a token for ContactDataServices.");
				// Disable searching on this instance
				instance.enabled = false;
			}
			instance.setCountryList();
			if(instance.elements.input){
				instance.input = instance.elements.input;
				instance.input.addEventListener("keyup", instance.search);
			}
		};

		// Main function to search for an address from an input string
		instance.search = function(){
			instance.currentSearchTerm = instance.input.value;
			instance.currentCountryCode = instance.countryList.value;

			// Check is searching is permitted
			if(instance.canSearch()){
				// Abort any outstanding requests
				if(instance.request.currentRequest){
					instance.request.currentRequest.abort();
				}
				// Construct the new Search URL
				var url = ContactDataServices.urls.construct.address.search(instance);

				// Store the last search term
				instance.lastSearchTerm = instance.currentSearchTerm;

				// Hide any previous results
				instance.result.hide();

				// Initiate new Search request
				instance.request.get(url, instance.picklist.show);
			} else {
				// Clear the picklist if the search term is cleared/empty
				instance.picklist.hide();
			}
		};

		instance.setCountryList = function(){
			instance.countryList = instance.elements.countryList;

			// If the user hasn't passed us a country list, then create new list?
			if(!instance.countryList){
				instance.createCountryDropdown();
			}
		};

		// Determine whether searching is currently permitted
		instance.canSearch = function(){
					// If searching on this instance is enabled, and
			return (instance.enabled &&
					// If search term is not empty, and
					instance.currentSearchTerm !== "" &&
					// If search term is not the same as previous search term, and
					instance.lastSearchTerm !== instance.currentSearchTerm &&
					// If the country is not empty
					instance.countryList.value !== "");
		};

		instance.createCountryDropdown = function(){
			// What countries?
			// Where to position it?
		};

		// Get a final (Formatted) address
		instance.format = function(url){
			instance.currentFormatUrl = ContactDataServices.urls.construct.address.format(url, instance);

			// Initiate a new Format request
			instance.request.get(instance.currentFormatUrl, instance.result.show);
		};

		instance.picklist = {
			// Set initial size
			size: 0,
			// Render a picklist of search results
			show: function(items){
				// Hide previous list
				instance.picklist.hide();

				// Store the picklist items
				instance.picklist.items = items.results;

				// Update picklist size
				instance.picklist.size = instance.picklist.items.length;

				// Get/Create picklist container element
				instance.picklist.container = instance.picklist.container || instance.picklist.createList();

				// Prepend an option for "use address entered"
				instance.picklist.createUseAddressEntered();

				if(instance.picklist.items.length > 0){
					// Iterate over and show results
					instance.picklist.items.forEach(function(item){
						// Create a new item/row in the picklist
						var listItem = instance.picklist.createListItem(item);
						instance.picklist.container.appendChild(listItem);

						// Listen for selection on this item
						instance.picklist.listen(listItem);
					});
				}
			},
			// Remove the picklist
			hide: function(){
				if(instance.picklist.container){
					instance.picklist.container.innerHTML = "";
				}
			},
			// Create a "use address entered" option
			createUseAddressEntered: function(){
				var item = {
					suggestion: "Use address entered",
					format: ""
				};
				var listItem = instance.picklist.createListItem(item);
				instance.picklist.container.appendChild(listItem);
				listItem.addEventListener("click", instance.picklist.useAddressEntered);
			},
			// Use the address entered as the Formatted address
			useAddressEntered: function(){
				var inputData = {
					address: [
						{
							content: instance.currentSearchTerm
						}
					]
				};
				instance.result.show(inputData);
			},
			// Create the picklist container and inject after the input
			createList: function(){
				var list = document.createElement("div");
				list.classList.add("address-picklist");
				// Insert the picklist after the input
				instance.input.parentNode.insertBefore(list, instance.input.nextSibling);
				return list;
			},
			// Create a new picklist item/row
			createListItem: function(item){
				var row = document.createElement("div");
				row.innerHTML = instance.picklist.addMatchingEmphasis(item);
				// Store the Format URL
				row.setAttribute("format", item.format);
				return row;
			},
			// Add emphasis to the picklist items highlighting the match
			addMatchingEmphasis: function(item){
				var highlights = item.matched || [],
                	label = item.suggestion;

                for (var i = 0; i < highlights.length; i++) {
                    var replacement = '' + label.substring(highlights[i][0], highlights[i][1]) + '';
                    label = label.substring(0, highlights[i][0]) + replacement + label.substring(highlights[i][1]);
                }

                return label;
			},
			// Listen to a picklist selection
			listen: function(row){
				row.addEventListener("click", instance.picklist.pick.bind(null, row));
			},
			// How to handle a picklist selection
			pick: function(item){
				instance.format(item.getAttribute("format"));
			}
		};

		instance.result = {
			// Render a Formatted address
			show: function(data){
				instance.picklist.hide();

				if(data.address.length > 0){
					// Get formatted address container element
					instance.result.formattedAddress = instance.elements.formattedAddress;

					// Create an array to hold the hidden input fields
					var inputArray = [];

					// Loop over each formatted address line
					for(var i = 0; i < data.address.length; i++){
						var line = data.address[i];
						// The line object will only have one property, but we don't know the key
						for(var key in line){
							if( line.hasOwnProperty( key ) ) {
								// Create the address line row and add to the DOM
								var row = instance.result.createAddressLine.row(line[key]);
								instance.result.formattedAddress.appendChild(row);

								// Create a hidden input to store the address line as well
								inputArray.push(instance.result.createAddressLine.input(key, line[key]));
							}
						}
					}

					// Write the list of hidden address line inputs to the DOM in one go
					instance.result.renderInputList(inputArray);
				}
			},
			hide: function(){
				instance.elements.formattedAddress.innerHTML = "";
			},
			createAddressLine: {
				// Create a hidden input to store the address line
				input: function(key, value){
					var input = document.createElement("input");
					input.setAttribute("type", "hidden");
					input.setAttribute("name", key);
					input.setAttribute("value", value);
					return input;
				},
				// Create a DOM element to contain the address line
				row: function(value){
					var row = document.createElement("div");
					row.innerHTML = value;
					return row;
				}
			},
			// Write the list of hidden address line inputs to the DOM
			renderInputList: function(inputArray){
				if(inputArray.length > 0){
					for(var i = 0; i < inputArray.length; i++){
						instance.result.formattedAddress.appendChild(inputArray[i]);
					}
				}
			}
		};

		// Use this to initiate and track XMLHttpRequests
		instance.request = {
			currentRequest: null,
			get: function(url, callback){
				instance.request.currentRequest = new XMLHttpRequest();
				instance.request.currentRequest.open('GET', url, true);
				instance.request.currentRequest.timeout = 5000; // 5 seconds

				instance.request.currentRequest.onload = function() {
				  if (instance.request.currentRequest.status >= 200 && instance.request.currentRequest.status < 400) {
				    // Success!
				    var data = JSON.parse(instance.request.currentRequest.responseText);
				    console.log(data);
				    callback(data);
				  } else {
				    // We reached our target server, but it returned an error

				  }
				};

				instance.request.currentRequest.onerror = function() {
				  // There was a connection error of some sort
				};

				instance.request.currentRequest.ontimeout = function() {
				  // There was a connection timeout
				};

				instance.request.currentRequest.send();
			}
		};

		// Initialise this instance of ContactDataServices
		instance.init();

		// Return the instance object to the invoker
		return instance;
	};

})(window, window.document);<|endoftext|>


<commit_before>var Request = require("falcor/request/Request");

function BatchedRequest(subscribe) {
    Request.call(this, subscribe);
}

BatchedRequest.create = Request.create;

BatchedRequest.prototype = Object.create(Request.prototype);
BatchedRequest.prototype.constructor = BatchedRequest;

BatchedRequest.prototype.getSourceObservable = function getSourceObservable() {

    if(this.connectedObservable) {
        return this.connectedObservable;
    }

    this.pending = true;
    var index = this.index;
    var connectable = (this
        .map(function(envelope) {
            return {
                index: index,
                jsonGraph: (envelope.jsonGraph ||
                    envelope.jsong  ||
                    envelope.values ||
                    envelope.value)
            };
        })
        .replay(null, 1));

    connectable.connect();

    return (this.connectedObservable = connectable);
};

module.exports = BatchedRequest;<commit_msg>Hides the BatchedRequest's ConnectableObservable inside Observable.using so it's not connected until the request is subscribed to.<commit_after>var Rx = require("falcor-observable");
var Request = require("falcor/request/Request");

function BatchedRequest(subscribe) {
    Request.call(this, subscribe);
}

BatchedRequest.create = Request.create;

BatchedRequest.prototype = Object.create(Request.prototype);
BatchedRequest.prototype.constructor = BatchedRequest;

BatchedRequest.prototype.getSourceObservable = function getSourceObservable() {

    if(this.connectedObservable) {
        return this.connectedObservable;
    }

    this.pending = true;
    var index = this.index;
    var connectable = (this
        .map(function(envelope) {
            return {
                index: index,
                jsonGraph: (envelope.jsonGraph ||
                    envelope.jsong  ||
                    envelope.values ||
                    envelope.value)
            };
        })
        .replay(null, 1));

    return (this.connectedObservable = Rx.Observable.using(
        function() { return connectable.connect(); },
        function() { return connectable; }
    ));
};

module.exports = BatchedRequest;<|endoftext|>


<commit_before>// @flow

import React, { Component } from 'react';
import styled from 'styled-components';
import { Div } from '@cajacko/lib/components/UI';
import CardsListItem from '@cajacko/lib/components/Cards/ListItem';
import Text from '@cajacko/lib/components/Text';

const Container = styled(Div)`
  flex: 1;
  padding-vertical: 50;
  align-items: center;
`;

/**
 * The story loading component
 */
const Loading = ({ text }) => (
  <CardsListItem>
    {({ backgroundColor }) => (
      <Container>
        <Text backgroundColor={backgroundColor} text={text} />
      </Container>
    )}
  </CardsListItem>
);

export default Loading;
<commit_msg>✨ style(loading): Use the new loading spinner<commit_after>// @flow

import React, { Component } from 'react';
import styled from 'styled-components';
import { Div } from '@cajacko/lib/components/UI';
import CardsListItem from '@cajacko/lib/components/Cards/ListItem';
import Spinner from '@cajacko/lib/components/Spinner';

const Container = styled(Div)`
  flex: 1;
  padding-vertical: 50;
  align-items: center;
`;

/**
 * The story loading component
 */
const Loading = ({ text }) => (
  <CardsListItem>
    {({ backgroundColor }) => (
      <Container>
        <Spinner backgroundColor={backgroundColor} text={text} />
      </Container>
    )}
  </CardsListItem>
);

export default Loading;
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import Editor, { createWithText } from 'draft-js-plugin-editor';
import hashtagPlugin from 'draft-js-hashtag-plugin';
import stickerPlugin from 'draft-js-sticker-plugin';
import linkifyPlugin from 'draft-js-linkify-plugin';
import mentionPlugin from 'draft-js-mention-plugin';
import historyPlugin from 'draft-js-history-plugin';
import styles from './styles';
import stickers from './stickers';
import mentions from './mentions';
import StatePreview from '../StatePreview';

// import { EditorState } from 'draft-js-cutting-edge';

const hashtagPluginInstance = hashtagPlugin();
const linkifyPluginInstance = linkifyPlugin();
const mentionPluginInstance = mentionPlugin({
  mentions,
});
const historyPluginInstance = historyPlugin();
const stickerPluginInstance = stickerPlugin({
  stickers,
});
const { StickerSelect } = stickerPluginInstance;
const { UndoButton, RedoButton, History } = historyPluginInstance;

const plugins = [
  hashtagPluginInstance,
  stickerPluginInstance,
  linkifyPluginInstance,
  mentionPluginInstance,
];

export default class UnicornEditor extends Component {

  state = {
    // editorState: EditorState.createEmpty(), // alternative to create an empty state
    editorState: createWithText('Hello World!'),
    showState: false,
  };

  onChange = (editorState) => {
    this.setState({
      editorState,
    });
  };

  focus = () => {
    this.refs.editor.focus();
  };

  toggleShowState = () => {
    this.setState({
      showState: !this.state.showState,
    });
  };

  /* eslint-disable react/jsx-no-bind */
  render() {
    const showStateButtonStyle = {
      ...styles.button,
      background: (this.state.showState ? '#ededed' : '#fff'),
    };

    return (

Example: Unicorn Editor

          <Editor
            editorState={this.state.editorState}
            onChange={this.onChange}
            plugins={plugins}
            spellCheck
            ref="editor"
          />

            <StickerSelect editor={ this } />

          <UndoButton
            editorState={ this.state.editorState }
            onChange={ this.onChange }
          />
          <RedoButton
            editorState={ this.state.editorState }
            onChange={ this.onChange }
          />
          <button
            style={ showStateButtonStyle }
            onClick={ this.toggleShowState }
          >
            Toggle State Preview
          </button>

        <StatePreview
          editorState={ this.state.editorState }
          collapsed={ !this.state.showState }
        />
      <History editorState={ this.state.editorState } />

Features in this Editor via Plugins

Custom stickers

Hashtag support

Automatically turns links into anchor tags

Mentions

Why?

          Just because unicorns are cooler than cats 😜

    );
  }
}
<commit_msg>temporarily remove history<commit_after>import React, { Component } from 'react';
import Editor, { createWithText } from 'draft-js-plugin-editor';
import hashtagPlugin from 'draft-js-hashtag-plugin';
import stickerPlugin from 'draft-js-sticker-plugin';
import linkifyPlugin from 'draft-js-linkify-plugin';
import mentionPlugin from 'draft-js-mention-plugin';
import historyPlugin from 'draft-js-history-plugin';
import styles from './styles';
import stickers from './stickers';
import mentions from './mentions';
import StatePreview from '../StatePreview';

// import { EditorState } from 'draft-js-cutting-edge';

const hashtagPluginInstance = hashtagPlugin();
const linkifyPluginInstance = linkifyPlugin();
const mentionPluginInstance = mentionPlugin({
  mentions,
});
const historyPluginInstance = historyPlugin();
const stickerPluginInstance = stickerPlugin({
  stickers,
});
const { StickerSelect } = stickerPluginInstance;
const { UndoButton, RedoButton, History } = historyPluginInstance;

const plugins = [
  hashtagPluginInstance,
  stickerPluginInstance,
  linkifyPluginInstance,
  mentionPluginInstance,
];

export default class UnicornEditor extends Component {

  state = {
    // editorState: EditorState.createEmpty(), // alternative to create an empty state
    editorState: createWithText('Hello World!'),
    showState: false,
  };

  onChange = (editorState) => {
    this.setState({
      editorState,
    });
  };

  focus = () => {
    this.refs.editor.focus();
  };

  toggleShowState = () => {
    this.setState({
      showState: !this.state.showState,
    });
  };

  /* eslint-disable react/jsx-no-bind */
  render() {
    const showStateButtonStyle = {
      ...styles.button,
      background: (this.state.showState ? '#ededed' : '#fff'),
    };

    return (

Example: Unicorn Editor

          <Editor
            editorState={this.state.editorState}
            onChange={this.onChange}
            plugins={plugins}
            spellCheck
            ref="editor"
          />

            <StickerSelect editor={ this } />

          <UndoButton
            editorState={ this.state.editorState }
            onChange={ this.onChange }
          />
          <RedoButton
            editorState={ this.state.editorState }
            onChange={ this.onChange }
          />
          <button
            style={ showStateButtonStyle }
            onClick={ this.toggleShowState }
          >
            Toggle State Preview
          </button>

        <StatePreview
          editorState={ this.state.editorState }
          collapsed={ !this.state.showState }
        />
        {/* <History editorState={ this.state.editorState } /> */}

Features in this Editor via Plugins

Custom stickers

Hashtag support

Automatically turns links into anchor tags

Mentions

Why?

          Just because unicorns are cooler than cats 😜

    );
  }
}
<|endoftext|>


<commit_before>const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");

let cleanupAry = [];

function restart() (
    Cc['@mozilla.org/toolkit/app-startup;1'].getService(Ci.nsIAppStartup)
        .quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart));

function main(win) {
  let doc = win.document;

  // add hotkey
  let restartKey = doc.createElement("key");
  restartKey.setAttribute("id", "RR:Restart");
  restartKey.setAttribute("key", "R");
  restartKey.setAttribute("modifiers", "accel,alt");
  restartKey.setAttribute("oncommand", "void(0);");
  restartKey.addEventListener("command", restart, true);
  let mainKS = doc.getElementById("mainKeyset");
  mainKS.appendChild(restartKey);

  // add menu bar item
  let restartMI = doc.createElement("menuitem");
  restartMI.setAttribute("id", "menu_FileRestartItem");
  restartMI.setAttribute("label", "Restart");
  restartMI.setAttribute("accesskey", "R");
  restartMI.setAttribute("key", "RR:Restart");
  restartMI.addEventListener("command", restart, true);
  let fileMenu = doc.getElementById("menu_FilePopup");
  fileMenu.insertBefore(restartMI, doc.getElementById("menu_FileQuitItem"));

  // add app (Firefox button) menu item
  let appMenu = doc.getElementById("appmenuPrimaryPane"), restartAMI;
  if (appMenu) {
    restartAMI = restartMI.cloneNode(false);
    restartAMI.setAttribute("id", "appmenu_RestartItem");
    restartAMI.setAttribute("class", "menuitem-iconic menuitem-iconic-tooltip");
    restartAMI.style.listStyleImage =
        "url('http://picol.org/images/icons/files/png/16/refresh_16.png')";
    restartAMI.addEventListener("command", restart, true);
    appMenu.insertBefore(restartAMI, doc.getElementById("appmenu-quit"));
  }

  let idx1 = cleanupAry.push(function() {
    mainKS.removeChild(restartKey);
    fileMenu.removeChild(restartMI);
    appMenu && appMenu.removeChild(restartAMI);
  }) - 1;
  let idx2 = cleanupAry.push(function() (
      win.removeEventListener("unload", winUnloader, false))) - 1;
  function winUnloader() {
    cleanupAry[idx1] = null;
    cleanupAry[idx2] = null;
  }
  win.addEventListener("unload", winUnloader, false);
}

function startup() {
  let browserWins = Services.wm.getEnumerator("navigator:browser");
  while (browserWins.hasMoreElements()) main(browserWins.getNext());

  function winObs(aSubject, aTopic) {
    if ("domwindowopened" != aTopic) return;
    let winLoad = function() {
      aSubject.removeEventListener("load", winLoad, false);
      if ("navigator:browser" ==
          aSubject.document.documentElement.getAttribute("windowtype"))
        main(aSubject);
    }
    aSubject.addEventListener("load", winLoad, false);
  }
  Services.ww.registerNotification(winObs);
  cleanupAry.push(function() Services.ww.unregisterNotification(winObs));
}
function shutdown() {
  for (let [, cleaner] in Iterator(cleanupAry)) cleaner && cleaner();
}
<commit_msg>saw a warning that the install() method was not defined, so I'm adding this and uninstall().<commit_after>const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");

let cleanupAry = [];

function restart() (
    Cc['@mozilla.org/toolkit/app-startup;1'].getService(Ci.nsIAppStartup)
        .quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart));

function main(win) {
  let doc = win.document;

  // add hotkey
  let restartKey = doc.createElement("key");
  restartKey.setAttribute("id", "RR:Restart");
  restartKey.setAttribute("key", "R");
  restartKey.setAttribute("modifiers", "accel,alt");
  restartKey.setAttribute("oncommand", "void(0);");
  restartKey.addEventListener("command", restart, true);
  let mainKS = doc.getElementById("mainKeyset");
  mainKS.appendChild(restartKey);

  // add menu bar item
  let restartMI = doc.createElement("menuitem");
  restartMI.setAttribute("id", "menu_FileRestartItem");
  restartMI.setAttribute("label", "Restart");
  restartMI.setAttribute("accesskey", "R");
  restartMI.setAttribute("key", "RR:Restart");
  restartMI.addEventListener("command", restart, true);
  let fileMenu = doc.getElementById("menu_FilePopup");
  fileMenu.insertBefore(restartMI, doc.getElementById("menu_FileQuitItem"));

  // add app (Firefox button) menu item
  let appMenu = doc.getElementById("appmenuPrimaryPane"), restartAMI;
  if (appMenu) {
    restartAMI = restartMI.cloneNode(false);
    restartAMI.setAttribute("id", "appmenu_RestartItem");
    restartAMI.setAttribute("class", "menuitem-iconic menuitem-iconic-tooltip");
    restartAMI.style.listStyleImage =
        "url('http://picol.org/images/icons/files/png/16/refresh_16.png')";
    restartAMI.addEventListener("command", restart, true);
    appMenu.insertBefore(restartAMI, doc.getElementById("appmenu-quit"));
  }

  let idx1 = cleanupAry.push(function() {
    mainKS.removeChild(restartKey);
    fileMenu.removeChild(restartMI);
    appMenu && appMenu.removeChild(restartAMI);
  }) - 1;
  let idx2 = cleanupAry.push(function() (
      win.removeEventListener("unload", winUnloader, false))) - 1;
  function winUnloader() {
    cleanupAry[idx1] = null;
    cleanupAry[idx2] = null;
  }
  win.addEventListener("unload", winUnloader, false);
}

let install = uninstall = function install(){};
function startup() {
  let browserWins = Services.wm.getEnumerator("navigator:browser");
  while (browserWins.hasMoreElements()) main(browserWins.getNext());

  function winObs(aSubject, aTopic) {
    if ("domwindowopened" != aTopic) return;
    let winLoad = function() {
      aSubject.removeEventListener("load", winLoad, false);
      if ("navigator:browser" ==
          aSubject.document.documentElement.getAttribute("windowtype"))
        main(aSubject);
    }
    aSubject.addEventListener("load", winLoad, false);
  }
  Services.ww.registerNotification(winObs);
  cleanupAry.push(function() Services.ww.unregisterNotification(winObs));
}
function shutdown() {
  for (let [, cleaner] in Iterator(cleanupAry)) cleaner && cleaner();
}
<|endoftext|>


<commit_before><commit_msg>#3960 Help icon in Studio isn't working<commit_after><|endoftext|>


<commit_before>#!/usr/bin/env node

var Type = require("./type");
var path = require("path");
var argv = require("subarg")(process.argv.slice(2));

var options = camelKeys(argv);

try {
  var configFile = path.join(process.cwd(), typeof options.config === "string" ? options.config : ".bitbundler");
  options = Object.assign({}, camelKeys(require(configFile)), options, { config: configFile });
}
catch(ex) {
  if (typeof options.config === "string") {
    console.error(ex);
    process.exit(1);
  }
}

options = Type.coerceValues(options, {
  "config": Type.String,
  "src": Type.Array.withTransform(configureSrc),
  "dest": Type.String,
  "baseUrl": Type.String,
  "stubNotFound": Type.Boolean,
  "sourceMap": Type.Boolean,
  "exportNames": Type.Boolean,
  "watch": Type.Boolean,
  "loader": Type.Array.withTransform(toArray),
  "bundler": Type.Array.withTransform(toArray),
  "multiprocess": Type.Number.withTransform(toNumber)
});

var files = {
  src: options.src,
  dest: options.dest
};

if (argv.print) {
  console.log("files: ", JSON.stringify(files));
  console.log("options: ", JSON.stringify(options));
}
else {
  require("../src/index").bundle(files, options);
}

function configureSrc(src) {
  return options._.length ? options._ : toArray(src);
}

function toArray(value) {
  return value && value._ ? value._ : [].concat(value);
}

function toNumber(value) {
  if (!value || value === "false") {
    return 0;
  }
  else if (value === "true") {
    return 1;
  }
  else {
    return Number(value);
  }
}

function camelKeys(args) {
  var result;

  if (args && args.constructor === Object) {
    result = {};
    Object.keys(args).forEach(arg => result[toCamel(arg)] = camelKeys(args[arg]));
  }
  else if (Array.isArray(args)) {
    result = args.map(camelKeys);
  }

  return result || args;
}

function toCamel(name) {
  return name.replace(/\-(\w)/g, (match, value) => value.toUpperCase());
}
<commit_msg>changed the coercion of multiprocess in the cli to convert to boolean or number<commit_after>#!/usr/bin/env node

var Type = require("./type");
var path = require("path");
var argv = require("subarg")(process.argv.slice(2));

var options = camelKeys(argv);

try {
  var configFile = path.join(process.cwd(), typeof options.config === "string" ? options.config : ".bitbundler");
  options = Object.assign({}, camelKeys(require(configFile)), options, { config: configFile });
}
catch(ex) {
  if (typeof options.config === "string") {
    console.error(ex);
    process.exit(1);
  }
}

options = Type.coerceValues(options, {
  "config": Type.String,
  "src": Type.Array.withTransform(configureSrc),
  "dest": Type.String,
  "baseUrl": Type.String,
  "stubNotFound": Type.Boolean,
  "sourceMap": Type.Boolean,
  "exportNames": Type.Boolean,
  "watch": Type.Boolean,
  "loader": Type.Array.withTransform(toArray),
  "bundler": Type.Array.withTransform(toArray),
  "multiprocess": Type.Number.withTransform(toNumber)
});

var files = {
  src: options.src,
  dest: options.dest
};

if (argv.print) {
  console.log("files: ", JSON.stringify(files));
  console.log("options: ", JSON.stringify(options));
}
else {
  require("../src/index").bundle(files, options);
}

function configureSrc(src) {
  return options._.length ? options._ : toArray(src);
}

function toArray(value) {
  return value && value._ ? value._ : [].concat(value);
}

function toNumber(value) {
  if (!value || value === "false") {
    return false;
  }
  else if (value === "true") {
    return true;
  }
  else {
    return Number(value);
  }
}

function camelKeys(args) {
  var result;

  if (args && args.constructor === Object) {
    result = {};
    Object.keys(args).forEach(arg => result[toCamel(arg)] = camelKeys(args[arg]));
  }
  else if (Array.isArray(args)) {
    result = args.map(camelKeys);
  }

  return result || args;
}

function toCamel(name) {
  return name.replace(/\-(\w)/g, (match, value) => value.toUpperCase());
}
<|endoftext|>


<commit_before>import React from 'react';
import { Modal, Form, Button, Header } from 'semantic-ui-react';

const ReplyFormModal = () => {
  return (
    <Modal trigger={<Button>Contact Them!</Button>}>
      <Modal.Header>Contact Form</Modal.Header>
      <Modal.Content>
        <Modal.Description>
          Send Them A Message!
          <Form>
            <Form.Input
              name="title"
              label="Subject"
              placeholder="Subject"
            />
            <Form.TextArea
              name="body"
              label="Message"
              placeholder="Tell them who you are and why you are contacting them..."
            />
            <Form.Button>Send Message</Form.Button>
          </Form>
        </Modal.Description>
      </Modal.Content>
    </Modal>
  );
};

export default ReplyFormModal;
<commit_msg>Implement ability to submit the reply form<commit_after>import React from 'react';
import { Modal, Form, Button, Header } from 'semantic-ui-react';

const ReplyFormModal = ({ onChange, onSubmit, listingId, userId }) =>
  <Modal trigger={<Button>Contact Them!</Button>}>
    <Modal.Header>Contact Form</Modal.Header>
    <Modal.Content>
      <Modal.Description>
        Send Them A Message!
        <Form onSubmit={e => onSubmit(e, listingId, userId)}>
          <Form.Input
            name="title"
            label="Subject"
            placeholder="Subject"
            onChange={e => onChange(e)}
          />
          <Form.TextArea
            name="body"
            label="Message"
            placeholder="Tell them who you are and why you are contacting them..."
            onChange={e => onChange(e)}
          />
          <Form.Button>Send Message</Form.Button>
        </Form>
      </Modal.Description>
    </Modal.Content>
  </Modal>;

ReplyFormModal.propTypes = {
  listingId: React.PropTypes.number.isRequired,
  userId: React.PropTypes.number.isRequired,
  onChange: React.PropTypes.func.isRequired,
  onSubmit: React.PropTypes.func.isRequired,
};

export default ReplyFormModal;
<|endoftext|>


<commit_before>defineClass("PatchViewController",{
	viewDidLoad: function() {
	var self = this
	this.view.backgroundColor = require("UIColor").alloc().initWithRed_green_blue_alpha(0.5,0.5,0.5,1)
	log('print view clolor')
	}
},{})<commit_msg>new view<commit_after>defineClass("PatchViewController",{
	viewDidLoad: function() {
	var self = this
	// this.view.backgroundColor = require("UIColor").alloc().initWithRed_green_blue_alpha(0.5,0.5,0.5,1)

	var myView = require("UIView").alloc().initWithFrame({
		    x: 20,
    		y: 100,
    		width: 120,
    		height: 20
	})
	self.view.addSubview(myView)
	log('print view clolor')
	}
},{})<|endoftext|>


<commit_before>var mapScale = 2;

var trace;
var reads = [];

function preload() {
  trace = loadStrings('assets/trace_reset.txt');
}

function setup() {
  createCanvas(512, 512);

  printMap();

  processTrace();

  // Uncomment this to save the map to PNG.
  //save('map.png');
}

function processTrace() {
  for (var i=0; i<trace.length; i++) {
    var line = trace[i];
    var tokens = line.split(" ");
    var addressHex = tokens[1];
    var address = parseInt(addressHex, 16);
    if (reads[address] === undefined) {
      reads[address] = 1;
    }
    else {
      reads[address]++;
    }
  }

  for (address in reads) {
    var xy = hilbert.d2xy(8, address);
    noStroke();
    fill(255);
    rect(xy[0] * mapScale, xy[1] * mapScale, mapScale, mapScale);
  }

  trace = null;
}

function printMap() {
  rect(0, 0, 256, 256);

  function addrBlock(color, desc) {
    return function(x, y, size) {
      x *= mapScale;
      y *= mapScale;
      size *= mapScale;
      fill(color);
      rect(x, y, size, size);

      fill(128);
      textAlign(CENTER, CENTER);
      text(desc, x, y, size, size);
    };
  }
  var ramColor = '#004000';
  var romColor = '#000080';
  var ioColor = '#800000';

  hilbertBlock(8, 0x0000, 0x10000, addrBlock(ramColor, "RAM"))

  hilbertBlock(8, 0x0000, 0x0100, addrBlock(ramColor, "System vars"));
  hilbertBlock(8, 0x0100, 0x0100, addrBlock(ramColor, "Stack"));
  hilbertBlock(8, 0x0200, 0x0100, addrBlock(ramColor, "System vars"));
  hilbertBlock(8, 0x0300, 0x0100, addrBlock(ramColor, "System vars"));

  hilbertBlock(8, 0x0400, 0x0400, addrBlock(ramColor, "Video memory"));

  hilbertBlock(8, 0xA000, 0x1000, addrBlock(romColor, "BASIC ROM"))
  hilbertBlock(8, 0xB000, 0x1000, addrBlock(romColor, "BASIC ROM"))

  hilbertBlock(8, 0xD000, 0x0400, addrBlock(ioColor, "VIC-II"))
  hilbertBlock(8, 0xD400, 0x0400, addrBlock(ioColor, "SID"))
  hilbertBlock(8, 0xD800, 0x0400, addrBlock(ioColor, "Color RAM"))
  hilbertBlock(8, 0xDC00, 0x0100, addrBlock(ioColor, "CIA #1"))
  hilbertBlock(8, 0xDD00, 0x0100, addrBlock(ioColor, "CIA #2"))
  hilbertBlock(8, 0xDE00, 0x0100, addrBlock(ioColor, "I/O #1"))
  hilbertBlock(8, 0xDF00, 0x0100, addrBlock(ioColor, "I/O #2"))

  hilbertBlock(8, 0xE000, 0x1000, addrBlock(romColor, "KERNAL ROM"));
  hilbertBlock(8, 0xF000, 0x1000, addrBlock(romColor, "KERNAL ROM"));
}

function hilbertBlock(maxLevel, location, sizeLinear, callback) {
  var level = Math.log2(sizeLinear) / 2;
  var size = 1 << level;

  var xy = hilbert.d2xy(maxLevel - level, location / sizeLinear);
  var x = xy[0] * size;
  var y = xy[1] * size;

  callback(x, y, size);
}

function draw() {
  // if (mouseIsPressed) {
  //   fill(0);
  // } else {
  //   fill(255);
  // }
  // ellipse(mouseX, mouseY, 80, 80);
}<commit_msg>1-fps animation<commit_after>var mapScale = 2;

var trace;
var frames = [];

var microsecsPerFrame = Math.floor(1000000 / 30);

function preload() {
  trace = loadStrings('assets/trace_reset.txt');
}

function setup() {
  createCanvas(512, 512);
  setFrameRate(1);

  printMap();

  processTrace();

  // Uncomment this to save the map to PNG.
  //save('map.png');
}

function processTrace() {
  for (var i=0; i<trace.length; i++) {
    var line = trace[i];
    var tokens = line.split(" ");
    var timestamp = tokens[0];

    var frame = Math.floor(timestamp / microsecsPerFrame);
    var reads;
    if (!frames[frame]) {
      reads = [];
      frames[frame] = {reads: reads};
    }
    else {
      reads = frames[frame].reads;
    }

    var addressHex = tokens[1];
    var address = parseInt(addressHex, 16);
    if (reads[address] === undefined) {
      reads[address] = 1;
    }
    else {
      reads[address]++;
    }
  }

  trace = null;
  console.log(frames.length);
}

function printMap() {
  rect(0, 0, 256, 256);

  function addrBlock(color, desc) {
    return function(x, y, size) {
      x *= mapScale;
      y *= mapScale;
      size *= mapScale;
      fill(color);
      rect(x, y, size, size);

      fill(128);
      textAlign(CENTER, CENTER);
      text(desc, x, y, size, size);
    };
  }
  var ramColor = '#004000';
  var romColor = '#000080';
  var ioColor = '#800000';

  hilbertBlock(8, 0x0000, 0x10000, addrBlock(ramColor, "RAM"))

  hilbertBlock(8, 0x0000, 0x0100, addrBlock(ramColor, "System vars"));
  hilbertBlock(8, 0x0100, 0x0100, addrBlock(ramColor, "Stack"));
  hilbertBlock(8, 0x0200, 0x0100, addrBlock(ramColor, "System vars"));
  hilbertBlock(8, 0x0300, 0x0100, addrBlock(ramColor, "System vars"));

  hilbertBlock(8, 0x0400, 0x0400, addrBlock(ramColor, "Video memory"));

  hilbertBlock(8, 0xA000, 0x1000, addrBlock(romColor, "BASIC ROM"))
  hilbertBlock(8, 0xB000, 0x1000, addrBlock(romColor, "BASIC ROM"))

  hilbertBlock(8, 0xD000, 0x0400, addrBlock(ioColor, "VIC-II"))
  hilbertBlock(8, 0xD400, 0x0400, addrBlock(ioColor, "SID"))
  hilbertBlock(8, 0xD800, 0x0400, addrBlock(ioColor, "Color RAM"))
  hilbertBlock(8, 0xDC00, 0x0100, addrBlock(ioColor, "CIA #1"))
  hilbertBlock(8, 0xDD00, 0x0100, addrBlock(ioColor, "CIA #2"))
  hilbertBlock(8, 0xDE00, 0x0100, addrBlock(ioColor, "I/O #1"))
  hilbertBlock(8, 0xDF00, 0x0100, addrBlock(ioColor, "I/O #2"))

  hilbertBlock(8, 0xE000, 0x1000, addrBlock(romColor, "KERNAL ROM"));
  hilbertBlock(8, 0xF000, 0x1000, addrBlock(romColor, "KERNAL ROM"));
}

function hilbertBlock(maxLevel, location, sizeLinear, callback) {
  var level = Math.log2(sizeLinear) / 2;
  var size = 1 << level;

  var xy = hilbert.d2xy(maxLevel - level, location / sizeLinear);
  var x = xy[0] * size;
  var y = xy[1] * size;

  callback(x, y, size);
}

var frameNum = 0;
function draw() {
  console.log(frameNum);
  frameNum++;

  var frameData = frames[frameNum];
  if (!frameData)
    return;

  var reads = frameData.reads;
  console.log("reads: " + reads.length);

  for (address in reads) {
    var xy = hilbert.d2xy(8, address);
    noStroke();
    fill(255);
    rect(xy[0] * mapScale, xy[1] * mapScale, mapScale, mapScale);
  }

  // if (mouseIsPressed) {
  //   fill(0);
  // } else {
  //   fill(255);
  // }
  // ellipse(mouseX, mouseY, 80, 80);
}<|endoftext|>


<commit_before>define([
	"dojo/_base/declare", // declare
	"dojo/topic",	//subscribe
	"rishson/Base", //createTopicNamespace, _capitaliseTopicName
	"rishson/widget/_Widget", //mixin
	"rishson/control/_Controller", //mixin
	"dojo/_base/lang", //isArray
	"dojo/store/Observable"	//constructor
], function (declare, topic, Base, _Widget, _Controller, lang, Observable) {
	/**
	 * @class
	 * @name rishson.control.ControllerWidget
	 * @description This widget can be extended to provide a module that acts as a 'Controller' (in MVC terms) and a
	 * place to do child widget layout and event handling. Whilst this is a less strict separation of concerns that using
	 * _Controller and _Widget directly (because your controller is DOM-aware), this base class does simplify application
	 * dramatically. Additionally, this is a very 'dojo' way of doing things because a widget is the basic module
	 * pattern of dojo.
	 */
	return declare("rishson.control.ControllerWidget", [Base, _Widget, _Controller], {
		/**
		 * @field
		 * @name rishson.control.ControllerWidget.models
		 * @type {Object}
		 * @description a collection of all the injected models
		 */
		models: null,

		/**
		 * @field
		 * @name rishson.control.ControllerWidget.views
		 * @type {Object}
		 * @description a collection of all the injected views
		 */
		views: null,

		/**
		 * @field
		 * @name rishson.control.ControllerWidget.controllers
		 * @type {Object}
		 * @description a collection of all the injected controllers
		 */
		controllers: null,

		/**
		 * @constructor
		 */
		constructor: function () {
			this.models = {};
			this.views = {};
			this.controllers = {};
		},

		/**
		 * @function
		 * @name rishson.control.ControllerWidget.postCreate
		 * @override rishson.widget._Widget
		 */
		postCreate: function () {
			this.inherited(arguments);
		},

		/**
		 * @function
		 * @name rishson.control.ControllerWidget.addView
		 * @param {Object|rishson.widget._Widget} view a widget
		 */
		addView : function (view, name) {
			this.views[name] = view;
		},

		/**
		 * @function
		 * @name rishson.control.ControllerWidget.addModel
		 * @param {string} name the name of the model to add
		 * @param {dojo.store} model a model (derived from dojo.store) to add to this controller
		 * @param {string} topicName the name of a topic to subscribe to
		 * @description create placeholder for a model and register handler for listener callbacks.
		 */
		addModel : function (name, model, topicName) {
			var myModel = this.models[name] = model;	//lookup shortcut
			myModel.listeners = [];
			myModel.loaded = false;

			//listen for any listeners that want to register when the model is populated
			this.subscribe(topicName + '/register', function (addListener) {
				if (!myModel.loaded) {
					myModel.listeners.push(addListener);	//add listeners because model not yet populated
				} else {
					addListener.call(myModel, myModel);	//just call the listener as the model has data
				}
			});
		},

		/**
		 * @function
		 * @name rishson.control.ControllerWidget.broadcastModel
		 * @param {dojo.store} model a widget which is a store to add to this controller
		 * @description	calls any registered callback function when a model gets populated with data
		 */
		broadcastModel: function (model) {
			var i,
				observeableModel = new Observable(model),	//wrap the store in Observable
				listeners = model.listeners;

			//call all listeners once with the populated observable model
			for (i = 0; i < listeners.length; i += 1) {
				listeners[i].call(observeableModel, observeableModel);
				listeners.splice(i, 1);
			}
		},

		/**
		 * @function
		 * @name rishson.control.ControllerWidget.addController
		 * @param {rishson.control._Controller|rishson.control.ControllerWidget} controller A child controller widget
		 * to add to this controller
		 * @param {string} name the name of the controller
		 */
		addController: function (controller, name) {
			this.controllers[name] = controller;
		}
	});
});<commit_msg>Decorate objects with their type<commit_after>define([
	"dojo/_base/declare", // declare
	"dojo/topic",	//subscribe
	"rishson/Base", //createTopicNamespace, _capitaliseTopicName
	"rishson/widget/_Widget", //mixin
	"rishson/control/_Controller", //mixin
	"dojo/_base/lang", //isArray
	"dojo/store/Observable"	//constructor
], function (declare, topic, Base, _Widget, _Controller, lang, Observable) {
	/**
	 * @class
	 * @name rishson.control.ControllerWidget
	 * @description This widget can be extended to provide a module that acts as a 'Controller' (in MVC terms) and a
	 * place to do child widget layout and event handling. Whilst this is a less strict separation of concerns that using
	 * _Controller and _Widget directly (because your controller is DOM-aware), this base class does simplify application
	 * dramatically. Additionally, this is a very 'dojo' way of doing things because a widget is the basic module
	 * pattern of dojo.
	 */
	return declare("rishson.control.ControllerWidget", [Base, _Widget, _Controller], {
		/**
		 * @field
		 * @name rishson.control.ControllerWidget.models
		 * @type {Object}
		 * @description a collection of all the injected models
		 */
		models: null,

		/**
		 * @field
		 * @name rishson.control.ControllerWidget.types
		 * @type {Object}
		 * @description a collection of the different widget types, used to decorate the objects
		 */
		types: {
			MODEL: "model",
			CONTROLLER: "controller",
			VIEW: "view"
		},

		/**
		 * @field
		 * @name rishson.control.ControllerWidget.views
		 * @type {Object}
		 * @description a collection of all the injected views
		 */
		views: null,

		/**
		 * @field
		 * @name rishson.control.ControllerWidget.controllers
		 * @type {Object}
		 * @description a collection of all the injected controllers
		 */
		controllers: null,

		/**
		 * @constructor
		 */
		constructor: function () {
			this.type = this.types.CONTROLLER;

			this.models = {};
			this.views = {};
			this.controllers = {};
		},

		/**
		 * @function
		 * @name rishson.control.ControllerWidget.postCreate
		 * @override rishson.widget._Widget
		 */
		postCreate: function () {
			this.inherited(arguments);
		},

		/**
		 * @function
		 * @name rishson.control.ControllerWidget.addView
		 * @param {Object|rishson.widget._Widget} view a widget
		 */
		addView : function (view, name) {
			view.type = this.types.VIEW;

			this.views[name] = view;
		},

		/**
		 * @function
		 * @name rishson.control.ControllerWidget.addModel
		 * @param {string} name the name of the model to add
		 * @param {dojo.store} model a model (derived from dojo.store) to add to this controller
		 * @param {string} topicName the name of a topic to subscribe to
		 * @description create placeholder for a model and register handler for listener callbacks.
		 */
		addModel : function (name, model, topicName) {
			model.type = this.types.MODEL;

			var myModel = this.models[name] = model;	//lookup shortcut
			myModel.listeners = [];
			myModel.loaded = false;

			//listen for any listeners that want to register when the model is populated
			this.subscribe(topicName + '/register', lang.hitch(this, function (addListener) {
				if (!myModel.loaded) {
					myModel.listeners.push(addListener);	//add listeners because model not yet populated
				} else {
					addListener.call(myModel, myModel);	//just call the listener as the model has data
				}
			}));
		},

		/**
		 * @function
		 * @name rishson.control.ControllerWidget.broadcastModel
		 * @param {dojo.store} model a widget which is a store to add to this controller
		 * @description	calls any registered callback function when a model gets populated with data
		 */
		broadcastModel: function (model) {
			var i,
				observableModel = new Observable(model),	//wrap the store in Observable
				listeners = model.listeners;

			//call all listeners once with the populated observable model
			for (i = 0; i < listeners.length; i += 1) {
				listeners[i].call(observableModel, observableModel);
				listeners.splice(i, 1);
			}
		},

		/**
		 * @function
		 * @name rishson.control.ControllerWidget.addController
		 * @param {rishson.control._Controller|rishson.control.ControllerWidget} controller A child controller widget
		 * to add to this controller
		 * @param {string} name the name of the controller
		 */
		addController: function (controller, name) {
			this.controllers[name] = controller;
		}
	});
});<|endoftext|>


<commit_before>'use strict';

var groupsApi = require('../Utils/groupsApi.js');
var usersApi = require('../Utils/usersApi.js');

import Firebase from 'firebase'
import React, { Component } from 'react';
import {
  StyleSheet,
  Text,
  AlertIOS,
  ListView,
  View,
  TextInput,
  TouchableHighlight,
  Image,
} from 'react-native';

var styles = StyleSheet.create({
  container: {
    flex: 1
  },
  button: {
    height: 60,
    backgroundColor: '#6600ff',
    flex: 3,
    alignItems: 'center',
    justifyContent: 'center'
  },
  searchInput: {
    height: 60,
    padding: 10,
    fontSize: 18,
    color: '#111',
    flex: 10
  },
  rowContainer: {
    flexDirection: 'row',
    padding: 10,
    marginBottom: 5,
  },
  footerContainer: {
    backgroundColor: '#E3E3E3',
    alignItems: 'center',
    flexDirection: 'row'
  },
  plusButton: {
    flexDirection: 'row',
    backgroundColor: '#6600ff',
    alignItems: 'center',
    justifyContent: 'center',
    width: 40,
    height: 40,
    borderRadius: 20,
    borderColor: 'white',
    borderWidth: 1.5
  },
  buttonText: {
    fontSize: 20,
    color: 'white'
  },
  pluscontainer: {
    flexDirection: 'row',
    paddingTop: 65,
    paddingBottom: 0,
    justifyContent: 'flex-end',
    alignItems: 'flex-end'
  },
  avatar: {
    height: 50,
    width: 50,
    borderRadius: 25,
  },
  messageStyle: {
    marginTop: 15,
    flex: 1,
  },
  errorMessage: {
    fontSize: 14,
    color: '#4d0000',
  }
});

class ChatPage extends Component{
  constructor(props){
    super(props);
    this.chatRef = new Firebase('https://ralli.firebaseio.com/groups/' + this.props.groupData.id + "/chatroom");
    this.ds = new ListView.DataSource({rowHasChanged: (row1, row2) => row1 !== row2})
    this.state = {
      dataSource: this.ds.cloneWithRows([{username: "Ralli Robot", message: "No body has said anything yet, be the first one!", avatarUrl: "http://www.gravatar.com/avatar/04da6ee5653a27ae038bebcdff7ea49c?s=90&r=g"}]),
      items: [],
      message: '',
      error: false,
      userName: ''
    }
  }
  componentWillMount(){
    this.chatRef.on('value', function(snapshot) {
      if(snapshot.val()) {
        var messages = [];
        for(var i in snapshot.val()) {
          messages.push(snapshot.val()[i]);
        }
        this.setState({
          items: messages,
          dataSource: this.ds.cloneWithRows(messages),
          userName: this.props.userData.username
        });
      }else {
        this.setState({
          userName: this.props.userData.username
        });
      }
    }.bind(this));
  }

  saveResponse(promptValue){
    // api call to add user to current chat
    this.setState({ promptValue: promptValue })
    var personEmail = this.state.promptValue;
    usersApi.getUserByEmail(personEmail).then((res) => {
      // user id is Object.keys(res.val())[0]
      // group id is this.
      groupsApi.joinGroup(this.props.groupData.id, Object.keys(res.val())[0], this.props.groupData.name);
    }).catch((err) => {
      this.setState({
        error: "User Not Found"
      })
    })
  }

  handleChange(e){
    this.setState({
      message: e.nativeEvent.text,
      error: false
    })
  }

  handleSubmit(){
    this.chatRef.push({ username: this.state.userName, message: this.state.message || '', avatarUrl: this.props.userData.avatarUrl });
    this.setState({
      message: ''
    })
  }
  renderRow(rowData){
    return (
        <View style={styles.rowContainer}>
          <Image style={styles.avatar} source={{uri: rowData.avatarUrl}} />
          <Text style={styles.messageStyle} > {rowData.username}: {rowData.message}</Text>
        </View>
    )
  }

  footer(){
    return (
      <View style={styles.footerContainer}>
        <TextInput
            style={styles.searchInput}
            value={this.state.message}
            onChange={this.handleChange.bind(this)}
            placeholder="Group Chat" />
        <TouchableHighlight
            style={styles.button}
            onPress={this.handleSubmit.bind(this)}
            underlayColor="#88D4F5">
              <Text style={styles.buttonText}>Submit</Text>
          </TouchableHighlight>
      </View>
    )
  }
  render(){
    var showErr = (
      this.state.error ? <Text style={styles.errorMessage}> {this.state.error} </Text> : <View></View>
    );
    if (this.state.items.length > 0) {
      return (
        <View style={styles.container}>
          <View style={styles.pluscontainer}>
            {showErr}
            <TouchableHighlight
            style={styles.plusButton}
            onPress={() => AlertIOS.prompt('Add a person by Email', null, this.saveResponse.bind(this))}
            underlayColor='black'>
              <Text style={styles.buttonText}> + </Text>
            </TouchableHighlight>
          </View>
          <ListView
            dataSource={this.state.dataSource}
            renderRow={this.renderRow} />
          {this.footer()}
        </View>
      )
    }else {
      return (
        <View style={styles.container}>
          <View style={styles.pluscontainer}>
            <TouchableHighlight
            style={styles.plusButton}
            onPress={() => AlertIOS.prompt('Add a person by Email', null, this.saveResponse.bind(this))}
            underlayColor='black'>
              <Text style={styles.buttonText}> + </Text>
            </TouchableHighlight>
          </View>
          <ListView
            dataSource={this.state.dataSource}
            renderRow={this.renderRow}
          />
          {this.footer()}
        </View>
      )
    }
  }
};

ChatPage.propTypes = {
  groupData: React.PropTypes.object.isRequired,
  userData: React.PropTypes.object.isRequired
};

module.exports = ChatPage;

<commit_msg>fix some styling<commit_after>'use strict';

var groupsApi = require('../Utils/groupsApi.js');
var usersApi = require('../Utils/usersApi.js');

import Firebase from 'firebase'
import React, { Component } from 'react';
import {
  StyleSheet,
  Text,
  AlertIOS,
  ListView,
  View,
  TextInput,
  TouchableHighlight,
  Image,
} from 'react-native';

var styles = StyleSheet.create({
  container: {
    flex: 1
  },
  button: {
    height: 60,
    backgroundColor: '#6600ff',
    flex: 3,
    alignItems: 'center',
    justifyContent: 'center'
  },
  searchInput: {
    height: 60,
    padding: 10,
    fontSize: 18,
    color: '#111',
    flex: 10
  },
  rowContainer: {
    flexDirection: 'row',
    padding: 10,
    marginBottom: 5,
  },
  footerContainer: {
    backgroundColor: '#E3E3E3',
    alignItems: 'center',
    flexDirection: 'row'
  },
  plusButton: {
    flexDirection: 'row',
    backgroundColor: '#6600ff',
    alignItems: 'center',
    justifyContent: 'center',
    width: 40,
    height: 40,
    borderRadius: 20,
    borderColor: 'white',
    borderWidth: 1.5
  },
  buttonText: {
    fontSize: 20,
    color: 'white'
  },
  pluscontainer: {
    flexDirection: 'row',
    paddingTop: 65,
    paddingBottom: 0,
    justifyContent: 'flex-end',
    alignItems: 'flex-end'
  },
  avatar: {
    height: 50,
    width: 50,
    borderRadius: 25,
  },
  messageStyle: {
    marginTop: 15,
    flex: 1,
  },
  errorMessage: {
    fontSize: 14,
    color: '#4d0000',
  }
});

class ChatPage extends Component{
  constructor(props){
    super(props);
    this.chatRef = new Firebase('https://ralli.firebaseio.com/groups/' + this.props.groupData.id + "/chatroom");
    this.ds = new ListView.DataSource({rowHasChanged: (row1, row2) => row1 !== row2})
    this.state = {
      dataSource: this.ds.cloneWithRows([{username: "Ralli Robot", message: "No body has said anything yet, be the first one!", avatarUrl: "http://www.gravatar.com/avatar/04da6ee5653a27ae038bebcdff7ea49c?s=90&r=g"}]),
      items: [],
      message: '',
      error: false,
      userName: ''
    }
  }
  componentWillMount(){
    this.chatRef.on('value', function(snapshot) {
      if(snapshot.val()) {
        var messages = [];
        for(var i in snapshot.val()) {
          messages.push(snapshot.val()[i]);
        }
        messages = messages.reverse();
        this.setState({
          items: messages,
          dataSource: this.ds.cloneWithRows(messages),
          userName: this.props.userData.username
        });
      }else {
        this.setState({
          userName: this.props.userData.username
        });
      }
    }.bind(this));
  }

  saveResponse(promptValue){
    // api call to add user to current chat
    this.setState({ promptValue: promptValue })
    var personEmail = this.state.promptValue;
    usersApi.getUserByEmail(personEmail).then((res) => {
      // user id is Object.keys(res.val())[0]
      // group id is this.
      groupsApi.joinGroup(this.props.groupData.id, Object.keys(res.val())[0], this.props.groupData.name);
    }).catch((err) => {
      this.setState({
        error: "User Not Found"
      })
    })
  }

  handleChange(e){
    this.setState({
      message: e.nativeEvent.text,
      error: false
    })
  }

  handleSubmit(){
    this.chatRef.push({ username: this.state.userName, message: this.state.message || '', avatarUrl: this.props.userData.avatarUrl });
    this.setState({
      message: ''
    })
  }
  renderRow(rowData){
    return (
        <View style={styles.rowContainer}>
          <Image style={styles.avatar} source={{uri: rowData.avatarUrl}} />
          <Text style={styles.messageStyle} > {rowData.username}: {rowData.message}</Text>
        </View>
    )
  }

  footer(){
    return (
      <View style={styles.footerContainer}>
        <TextInput
            style={styles.searchInput}
            value={this.state.message}
            onChange={this.handleChange.bind(this)}
            placeholder="Group Chat" />
        <TouchableHighlight
            style={styles.button}
            onPress={this.handleSubmit.bind(this)}
            underlayColor="#88D4F5">
              <Text style={styles.buttonText}>Submit</Text>
          </TouchableHighlight>
      </View>
    )
  }
  render(){
    var showErr = (
      this.state.error ? <Text style={styles.errorMessage}> {this.state.error} </Text> : <View></View>
    );
    if (this.state.items.length > 0) {
      return (
        <View style={styles.container}>
          <View style={styles.pluscontainer}>
            {showErr}
            <TouchableHighlight
            style={styles.plusButton}
            onPress={() => AlertIOS.prompt('Add a person by Email', null, this.saveResponse.bind(this))}
            underlayColor='black'>
              <Text style={styles.buttonText}> + </Text>
            </TouchableHighlight>
          </View>
          <ListView
            dataSource={this.state.dataSource}
            renderRow={this.renderRow} />
          {this.footer()}
        </View>
      )
    }else {
      return (
        <View style={styles.container}>

          <View style={styles.pluscontainer}>
            <TouchableHighlight
            style={styles.plusButton}
            onPress={() => AlertIOS.prompt('Add a person by Email', null, this.saveResponse.bind(this))}
            underlayColor='black'>
              <Text style={styles.buttonText}> + </Text>
            </TouchableHighlight>
          </View>
          <ListView
            dataSource={this.state.dataSource}
            renderRow={this.renderRow}/>
          {this.footer()}
        </View>
      )
    }
  }
};

ChatPage.propTypes = {
  groupData: React.PropTypes.object.isRequired,
  userData: React.PropTypes.object.isRequired
};

module.exports = ChatPage;

<|endoftext|>