<commit_before>import React from 'react';
import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';
import {
  Accordion,
  Badge,
  Headline
} from '@folio/stripes/components';
import {
  IfPermission,
  withStripes
} from '@folio/stripes/core';

import ProxyEditList from '../../ProxyGroup/ProxyEditList';
import ProxyEditItem from '../../ProxyGroup/ProxyEditItem';

const EditProxy = (props) => {
  const {
    expanded,
    onToggle,
    accordionId,
    sponsors,
    proxies,
    fullName,
    change,
    stripes,
    initialValues,
  } = props;

  const proxySponsor = <FormattedMessage id="ui-users.permissions.proxySponsor" />;

  return (
    <IfPermission perm="ui-users.editproxies">
      <Accordion
        open={expanded}
        id={accordionId}
        onToggle={onToggle}
        label={<Headline size="large" tag="h3">{proxySponsor}</Headline>}
        displayWhenClosed={
          <Badge>{sponsors.length + proxies.length}</Badge>
        }
      >
        <FormattedMessage id="ui-users.permissions.isProxyFor" values={{ name: fullName }}>
          { label => (
            <ProxyEditList itemComponent={ProxyEditItem} label={label} name="sponsors" stripes={stripes} change={change} initialValues={initialValues} />
          )}
        </FormattedMessage>

        <FormattedMessage id="ui-users.permissions.isSponsorOf" values={{ name: fullName }}>
          { label => (
            <ProxyEditList itemComponent={ProxyEditItem} label={label} name="proxies" stripes={stripes} change={change} />
          )}
        </FormattedMessage>

      </Accordion>
    </IfPermission>
  );
};

EditProxy.propTypes = {
  change: PropTypes.func,
  fullName: PropTypes.string,
  expanded: PropTypes.bool,
  onToggle: PropTypes.func,
  accordionId: PropTypes.string.isRequired,
  proxies: PropTypes.arrayOf(PropTypes.object),
  sponsors: PropTypes.arrayOf(PropTypes.sponsors),
  stripes: PropTypes.object,
  initialValues: PropTypes.object,
};

export default withStripes(EditProxy);
<commit_msg>UIU-1313 restore initialValues to proxy AND sponsor<commit_after>import React from 'react';
import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';
import {
  Accordion,
  Badge,
  Headline
} from '@folio/stripes/components';
import {
  IfPermission,
  withStripes
} from '@folio/stripes/core';

import ProxyEditList from '../../ProxyGroup/ProxyEditList';
import ProxyEditItem from '../../ProxyGroup/ProxyEditItem';

const EditProxy = (props) => {
  const {
    expanded,
    onToggle,
    accordionId,
    sponsors,
    proxies,
    fullName,
    change,
    stripes,
    initialValues,
  } = props;

  const proxySponsor = <FormattedMessage id="ui-users.permissions.proxySponsor" />;

  return (
    <IfPermission perm="ui-users.editproxies">
      <Accordion
        open={expanded}
        id={accordionId}
        onToggle={onToggle}
        label={<Headline size="large" tag="h3">{proxySponsor}</Headline>}
        displayWhenClosed={
          <Badge>{sponsors.length + proxies.length}</Badge>
        }
      >
        <FormattedMessage id="ui-users.permissions.isProxyFor" values={{ name: fullName }}>
          { label => (
            <ProxyEditList itemComponent={ProxyEditItem} label={label} name="sponsors" stripes={stripes} change={change} initialValues={initialValues} />
          )}
        </FormattedMessage>

        <FormattedMessage id="ui-users.permissions.isSponsorOf" values={{ name: fullName }}>
          { label => (
            <ProxyEditList itemComponent={ProxyEditItem} label={label} name="proxies" stripes={stripes} change={change} initialValues={initialValues} />
          )}
        </FormattedMessage>

      </Accordion>
    </IfPermission>
  );
};

EditProxy.propTypes = {
  change: PropTypes.func,
  fullName: PropTypes.string,
  expanded: PropTypes.bool,
  onToggle: PropTypes.func,
  accordionId: PropTypes.string.isRequired,
  proxies: PropTypes.arrayOf(PropTypes.object),
  sponsors: PropTypes.arrayOf(PropTypes.sponsors),
  stripes: PropTypes.object,
  initialValues: PropTypes.object,
};

export default withStripes(EditProxy);
<|endoftext|>


<commit_before>'use strict';

var m = require('mithril');
var URI = require('URIjs');
var $ = require('jquery');
var Raven = require('raven-js');

var Fangorn = require('js/fangorn');
var waterbutler = require('js/waterbutler');
var $osf = require('js/osfHelpers');

function changeState(grid, item, version) {
    item.data.version = version;
    grid.updateFolder(null, item);
}

function _downloadEvent(event, item, col) {
    event.stopPropagation();
    window.location = waterbutler.buildTreeBeardDownload(item, {path: item.data.extra.fileId});
}

// Define Fangorn Button Actions
var _dataverseItemButtons = {
    view: function (ctrl, args, children) {
        var buttons = [];
        var tb = args.treebeard;
        var item = args.item;
        function _uploadEvent(event, item, col) {
            event.stopPropagation();
            tb.dropzone.hiddenFileInput.click();
            tb.dropzoneItemCache = item;
        }
        function dataversePublish(event, item, col) {
            var both = !item.data.dataverseIsPublished;
            var url = item.data.urls.publish;
            var host = item.data.host;
            var toPublish = both ? 'Dataverse and dataset' : 'dataset';
            // Set the modal content to reflect the file's external host
            var modalContent = [
                m('p.m-md', both ? 'This dataset cannot be published until the ' + item.data.dataverse + ' Dataverse is published. ' : ''),
                m('p.m-md', 'By publishing this ' + toPublish + ', all content will be made available through ' + host + ' using their internal privacy settings, regardless of your OSF project settings. '),
                m('p.font-thick.m-md', both ? 'Do you want to publish this Dataverse AND this dataset?' : 'Are you sure you want to publish this dataset?')
            ];
            var modalActions =
                [m('button.btn.btn-default', {
                    'onclick': function () {
                        tb.modal.dismiss();
                    }
                }, 'Cancel'),
                m('button.btn.btn-primary', {
                    'onclick': function () {
                        publishDataset();
                    }
                }, 'Publish')];

            tb.modal.update(modalContent, modalActions, m('h3.break-word.modal-title', 'Publish this ' + toPublish + '?'));

            function publishDataset() {
                tb.modal.dismiss();
                item.notify.update('Publishing ' + toPublish, 'info', 1, 1);
                $.osf.putJSON(
                    url,
                    {'publish_both': both}
                ).done(function (data) {
                    item.notify.update();
                    var modalContent = [
                        m('p.m-md', 'Your content has been published.')
                    ];
                    var modalActions = [
                        m('button.btn.btn-primary', {
                            'onclick': function () {
                                tb.modal.dismiss();
                            }
                        }, 'Got it')

                    ];
                    tb.modal.update(modalContent, modalActions, m('h3.break-word.modal-title', 'Successfully published'));
                    item.data.dataverseIsPublished = true;
                    item.data.datasetIsPublished = true;
                    item.data.datasetDraftModified = false;
                    item.data.hasPublishedFiles = item.children.length > 0;
                    item.data.version = item.data.hasPublishedFiles ? 'latest-published' : 'latest';
                    for (var i = 0; i < item.children.length; i++) { // Brute force the child files to be set as "latest-published" without page reload
                        item.children[i].data.extra.datasetVersion = item.data.version;
                    }
                }).fail(function (xhr, status, error) {
                    var statusCode = xhr.responseJSON.code;
                    var message;
                    switch (statusCode) {
                    case 405:
                        message = 'Error: This dataset cannot be published until ' + item.data.dataverse + ' Dataverse is published.';
                        break;
                    case 409:
                        message = 'This dataset version has already been published.';
                        break;
                    default:
                        message = 'Error: Something went wrong when attempting to publish your dataset.';
                        Raven.captureMessage('Could not publish dataset', {
                            extra: {
                                url: url,
                                textStatus: status,
                                error: error
                            }
                        });
                    }

                    var modalContent = [
                        m('p.m-md', message)
                    ];
                    var modalActions = [
                        m('button.btn.btn-primary', {
                            'onclick': function () {
                                tb.modal.dismiss();
                            }
                        }, 'Okay')
                    ];
                    tb.modal.update(modalContent, modalActions);
                });
            }
        }
        if (item.data.addonFullname) {
            var options = [
                m('option', {selected: item.data.version === 'latest', value: 'latest'}, 'Draft')
            ];
            if (item.data.datasetIsPublished) {
                options.push(m('option', {selected: item.data.version === 'latest-published', value: 'latest-published'}, 'Published'));
            }
            buttons.push(
                m.component(Fangorn.Components.dropdown, {
                    'label': 'Version: ',
                    onchange: function (e) {
                        changeState(tb, item, e.target.value);
                    },
                    icon: 'fa fa-external-link',
                    className: 'text-info'
                }, options)
            );
        }
        if (tb.options.placement !== 'fileview') {
            if (item.kind === 'folder' && item.data.addonFullname && item.data.version === 'latest' && item.data.permissions.edit) {
                buttons.push(
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            _uploadEvent.call(tb, event, item);
                        },
                        icon: 'fa fa-upload',
                        className: 'text-success'
                    }, 'Upload')
                );
                // Only allow the Publish button to appear if the draft dataset is modified
                if(item.data.datasetDraftModified) {
                    buttons.push(
                        m.component(Fangorn.Components.button, {
                            onclick: function (event) {
                                dataversePublish.call(tb, event, item);
                            },
                            icon: 'fa fa-globe',
                            className: 'text-primary'
                        }, 'Publish')
                    );
                }
            } else if (item.kind === 'folder' && !item.data.addonFullname) {
                buttons.push(
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            _uploadEvent.call(tb, event, item);
                        },
                        icon: 'fa fa-upload',
                        className: 'text-success'
                    }, 'Upload')
                );
            } else if (item.kind === 'file') {
                buttons.push(
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            _downloadEvent.call(tb, event, item);
                        },
                        icon: 'fa fa-download',
                        className: 'text-primary'
                    }, 'Download')
                );
                if (item.parent().data.version === 'latest' && item.data.permissions.edit) {
                    buttons.push(
                        m.component(Fangorn.Components.button, {
                            onclick: function (event) {
                                Fangorn.ButtonEvents._removeEvent.call(tb, event, [item]);
                            },
                            icon: 'fa fa-trash',
                            className: 'text-danger'
                        }, 'Delete')
                    );
                }
                if (item.data.permissions && item.data.permissions.view) {
                    buttons.push(
                        m.component(Fangorn.Components.button, {
                            onclick: function(event) {
                                gotoFile(item);
                            },
                            icon: 'fa fa-external-link',
                            className : 'text-info'
                        }, 'View'));

                }
            }
        }
        return m('span', buttons);
    }
};

function gotoFile (item) {
    var redir = new URI(item.data.nodeUrl);
    window.location = redir
        .segment('files')
        .segment(item.data.provider)
        .segment(item.data.extra.fileId)
        .query({version: item.data.extra.datasetVersion})
        .toString();
}

function _fangornDataverseTitle(item, col) {
    var tb = this;
    if (item.data.isAddonRoot && item.connected === false) { // as opposed to undefined, avoids unnecessary setting of this value
        return Fangorn.Utils.connectCheckTemplate.call(this, item);
    }
    var version = item.data.version === 'latest-published' ? 'Published' : 'Draft';
    if (item.data.addonFullname) {
        var contents = [m('dataverse-name', item.data.name + ' (' + version + ')')];
        if (item.data.hasPublishedFiles) {
            if (item.data.permissions.edit) {
                // Default to version in url parameters for file view page
                var urlParams = $osf.urlParams();
                if (urlParams.version && urlParams.version !== item.data.version) {
                    item.data.version = urlParams.version;
                }
            } else {
                contents.push(
                    m('span.text-muted', '[Published]')
                );
            }
        } else {
            contents.push(
                m('span.text.text-muted', '[Draft]')
            );
        }
        return m('span', contents);
    } else {
        return m('span', [
            m('dataverse-name.fg-file-links', {
                onclick: function () {
                    gotoFile(item);
                }
            }, item.data.name
                )
        ]);
    }
}

function _fangornColumns(item) {
    var tb = this;
    var columns = [];
    columns.push({
        data : 'name',
        folderIcons : true,
        filter : true,
        custom: _fangornDataverseTitle
    });
    if (tb.options.placement === 'project-files') {
        columns.push(
        {
            data  : 'size',
            sortInclude : false,
            filter : false,
            custom : function() {return m('');}
        });
        columns.push(
        {
            data  : 'downloads',
            sortInclude : false,
            filter : false,
            custom : function() {return m('');}
        });
        columns.push({
            data: 'version',
            filter: false,
            sortInclude : false,
            custom: function() {return m('');}
        });
    }
    if(tb.options.placement !== 'fileview') {
        columns.push({
            data : 'modified',
            filter: false,
            custom : function() {return m('');}
        });
    }
    return columns;
}

function _fangornFolderIcons(item) {
    if (item.data.iconUrl) {
        return m('img', {
            src: item.data.iconUrl,
            style: {
                width: '16px',
                height: 'auto'
            }
        }, ' ');
    }
    return undefined;
}

function _fangornLazyLoad(item) {
    return waterbutler.buildTreeBeardMetadata(item, {version: item.data.version});
}

function _canDrop(item) {
    return item.data.provider &&
        item.kind === 'folder' &&
        item.data.permissions.edit &&
        item.data.version === 'latest';
}

Fangorn.config.dataverse = {
    folderIcon: _fangornFolderIcons,
    resolveRows: _fangornColumns,
    lazyload:_fangornLazyLoad,
    canDrop: _canDrop,
    itemButtons: _dataverseItemButtons
};
<commit_msg>don't pass path twice in dataverse's fangorn<commit_after>'use strict';

var m = require('mithril');
var URI = require('URIjs');
var $ = require('jquery');
var Raven = require('raven-js');

var Fangorn = require('js/fangorn');
var waterbutler = require('js/waterbutler');
var $osf = require('js/osfHelpers');

function changeState(grid, item, version) {
    item.data.version = version;
    grid.updateFolder(null, item);
}

function _downloadEvent(event, item, col) {
    event.stopPropagation();
    window.location = waterbutler.buildTreeBeardDownload(item);
}

// Define Fangorn Button Actions
var _dataverseItemButtons = {
    view: function (ctrl, args, children) {
        var buttons = [];
        var tb = args.treebeard;
        var item = args.item;
        function _uploadEvent(event, item, col) {
            event.stopPropagation();
            tb.dropzone.hiddenFileInput.click();
            tb.dropzoneItemCache = item;
        }
        function dataversePublish(event, item, col) {
            var both = !item.data.dataverseIsPublished;
            var url = item.data.urls.publish;
            var host = item.data.host;
            var toPublish = both ? 'Dataverse and dataset' : 'dataset';
            // Set the modal content to reflect the file's external host
            var modalContent = [
                m('p.m-md', both ? 'This dataset cannot be published until the ' + item.data.dataverse + ' Dataverse is published. ' : ''),
                m('p.m-md', 'By publishing this ' + toPublish + ', all content will be made available through ' + host + ' using their internal privacy settings, regardless of your OSF project settings. '),
                m('p.font-thick.m-md', both ? 'Do you want to publish this Dataverse AND this dataset?' : 'Are you sure you want to publish this dataset?')
            ];
            var modalActions =
                [m('button.btn.btn-default', {
                    'onclick': function () {
                        tb.modal.dismiss();
                    }
                }, 'Cancel'),
                m('button.btn.btn-primary', {
                    'onclick': function () {
                        publishDataset();
                    }
                }, 'Publish')];

            tb.modal.update(modalContent, modalActions, m('h3.break-word.modal-title', 'Publish this ' + toPublish + '?'));

            function publishDataset() {
                tb.modal.dismiss();
                item.notify.update('Publishing ' + toPublish, 'info', 1, 1);
                $.osf.putJSON(
                    url,
                    {'publish_both': both}
                ).done(function (data) {
                    item.notify.update();
                    var modalContent = [
                        m('p.m-md', 'Your content has been published.')
                    ];
                    var modalActions = [
                        m('button.btn.btn-primary', {
                            'onclick': function () {
                                tb.modal.dismiss();
                            }
                        }, 'Got it')

                    ];
                    tb.modal.update(modalContent, modalActions, m('h3.break-word.modal-title', 'Successfully published'));
                    item.data.dataverseIsPublished = true;
                    item.data.datasetIsPublished = true;
                    item.data.datasetDraftModified = false;
                    item.data.hasPublishedFiles = item.children.length > 0;
                    item.data.version = item.data.hasPublishedFiles ? 'latest-published' : 'latest';
                    for (var i = 0; i < item.children.length; i++) { // Brute force the child files to be set as "latest-published" without page reload
                        item.children[i].data.extra.datasetVersion = item.data.version;
                    }
                }).fail(function (xhr, status, error) {
                    var statusCode = xhr.responseJSON.code;
                    var message;
                    switch (statusCode) {
                    case 405:
                        message = 'Error: This dataset cannot be published until ' + item.data.dataverse + ' Dataverse is published.';
                        break;
                    case 409:
                        message = 'This dataset version has already been published.';
                        break;
                    default:
                        message = 'Error: Something went wrong when attempting to publish your dataset.';
                        Raven.captureMessage('Could not publish dataset', {
                            extra: {
                                url: url,
                                textStatus: status,
                                error: error
                            }
                        });
                    }

                    var modalContent = [
                        m('p.m-md', message)
                    ];
                    var modalActions = [
                        m('button.btn.btn-primary', {
                            'onclick': function () {
                                tb.modal.dismiss();
                            }
                        }, 'Okay')
                    ];
                    tb.modal.update(modalContent, modalActions);
                });
            }
        }
        if (item.data.addonFullname) {
            var options = [
                m('option', {selected: item.data.version === 'latest', value: 'latest'}, 'Draft')
            ];
            if (item.data.datasetIsPublished) {
                options.push(m('option', {selected: item.data.version === 'latest-published', value: 'latest-published'}, 'Published'));
            }
            buttons.push(
                m.component(Fangorn.Components.dropdown, {
                    'label': 'Version: ',
                    onchange: function (e) {
                        changeState(tb, item, e.target.value);
                    },
                    icon: 'fa fa-external-link',
                    className: 'text-info'
                }, options)
            );
        }
        if (tb.options.placement !== 'fileview') {
            if (item.kind === 'folder' && item.data.addonFullname && item.data.version === 'latest' && item.data.permissions.edit) {
                buttons.push(
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            _uploadEvent.call(tb, event, item);
                        },
                        icon: 'fa fa-upload',
                        className: 'text-success'
                    }, 'Upload')
                );
                // Only allow the Publish button to appear if the draft dataset is modified
                if(item.data.datasetDraftModified) {
                    buttons.push(
                        m.component(Fangorn.Components.button, {
                            onclick: function (event) {
                                dataversePublish.call(tb, event, item);
                            },
                            icon: 'fa fa-globe',
                            className: 'text-primary'
                        }, 'Publish')
                    );
                }
            } else if (item.kind === 'folder' && !item.data.addonFullname) {
                buttons.push(
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            _uploadEvent.call(tb, event, item);
                        },
                        icon: 'fa fa-upload',
                        className: 'text-success'
                    }, 'Upload')
                );
            } else if (item.kind === 'file') {
                buttons.push(
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            _downloadEvent.call(tb, event, item);
                        },
                        icon: 'fa fa-download',
                        className: 'text-primary'
                    }, 'Download')
                );
                if (item.parent().data.version === 'latest' && item.data.permissions.edit) {
                    buttons.push(
                        m.component(Fangorn.Components.button, {
                            onclick: function (event) {
                                Fangorn.ButtonEvents._removeEvent.call(tb, event, [item]);
                            },
                            icon: 'fa fa-trash',
                            className: 'text-danger'
                        }, 'Delete')
                    );
                }
                if (item.data.permissions && item.data.permissions.view) {
                    buttons.push(
                        m.component(Fangorn.Components.button, {
                            onclick: function(event) {
                                gotoFile(item);
                            },
                            icon: 'fa fa-external-link',
                            className : 'text-info'
                        }, 'View'));

                }
            }
        }
        return m('span', buttons);
    }
};

function gotoFile (item) {
    var redir = new URI(item.data.nodeUrl);
    window.location = redir
        .segment('files')
        .segment(item.data.provider)
        .segment(item.data.extra.fileId)
        .query({version: item.data.extra.datasetVersion})
        .toString();
}

function _fangornDataverseTitle(item, col) {
    var tb = this;
    if (item.data.isAddonRoot && item.connected === false) { // as opposed to undefined, avoids unnecessary setting of this value
        return Fangorn.Utils.connectCheckTemplate.call(this, item);
    }
    var version = item.data.version === 'latest-published' ? 'Published' : 'Draft';
    if (item.data.addonFullname) {
        var contents = [m('dataverse-name', item.data.name + ' (' + version + ')')];
        if (item.data.hasPublishedFiles) {
            if (item.data.permissions.edit) {
                // Default to version in url parameters for file view page
                var urlParams = $osf.urlParams();
                if (urlParams.version && urlParams.version !== item.data.version) {
                    item.data.version = urlParams.version;
                }
            } else {
                contents.push(
                    m('span.text-muted', '[Published]')
                );
            }
        } else {
            contents.push(
                m('span.text.text-muted', '[Draft]')
            );
        }
        return m('span', contents);
    } else {
        return m('span', [
            m('dataverse-name.fg-file-links', {
                onclick: function () {
                    gotoFile(item);
                }
            }, item.data.name
                )
        ]);
    }
}

function _fangornColumns(item) {
    var tb = this;
    var columns = [];
    columns.push({
        data : 'name',
        folderIcons : true,
        filter : true,
        custom: _fangornDataverseTitle
    });
    if (tb.options.placement === 'project-files') {
        columns.push(
        {
            data  : 'size',
            sortInclude : false,
            filter : false,
            custom : function() {return m('');}
        });
        columns.push(
        {
            data  : 'downloads',
            sortInclude : false,
            filter : false,
            custom : function() {return m('');}
        });
        columns.push({
            data: 'version',
            filter: false,
            sortInclude : false,
            custom: function() {return m('');}
        });
    }
    if(tb.options.placement !== 'fileview') {
        columns.push({
            data : 'modified',
            filter: false,
            custom : function() {return m('');}
        });
    }
    return columns;
}

function _fangornFolderIcons(item) {
    if (item.data.iconUrl) {
        return m('img', {
            src: item.data.iconUrl,
            style: {
                width: '16px',
                height: 'auto'
            }
        }, ' ');
    }
    return undefined;
}

function _fangornLazyLoad(item) {
    return waterbutler.buildTreeBeardMetadata(item, {version: item.data.version});
}

function _canDrop(item) {
    return item.data.provider &&
        item.kind === 'folder' &&
        item.data.permissions.edit &&
        item.data.version === 'latest';
}

Fangorn.config.dataverse = {
    folderIcon: _fangornFolderIcons,
    resolveRows: _fangornColumns,
    lazyload:_fangornLazyLoad,
    canDrop: _canDrop,
    itemButtons: _dataverseItemButtons
};
<|endoftext|>


<commit_before>'use strict';

var bcrypt = require('bcrypt-nodejs'),
    Server = require('socket.io'),
    express = require('express'),
    http = require('http'),
    path = require('path'),
    mongo = require('mongojs');

var util = require('util');
var session = require('./session');
var _ = require('lodash');
var cors = require('cors');

var _validTokens = {};

function throwErr (func) {
    return function(err, data) {
        if (err) {
            throw err;
        } else {
            func.call(null, data);
        }
    };
}

function validateScene (sceneData) {
    // convert _id so mongo recognizes it
    if (sceneData.hasOwnProperty('_id')) {
        sceneData._id = mongo.ObjectId(sceneData._id);
    }

    // add _ids to all mediaObjects
    if (sceneData.hasOwnProperty('scene')) {
        if (! Array.isArray(sceneData.scene) ) {
            throw new Error('"scene" property must be an array of objects');
        } else {
            sceneData.scene.forEach(function(mediaObject) {
                if (! mediaObject.hasOwnProperty('_id')) {
                    mediaObject._id = mongo.ObjectId();
                }
            });
        }
    }

    return sceneData;
}

function validateSceneGraph (sceneGraphData) {
    //convert _id so mongo recognizes it
    if (sceneGraphData.hasOwnProperty('_id')) {
        sceneGraphData._id = mongo.ObjectId(sceneGraphData._id);
    }

    //TODO most likely more detailed validation can occur

    return sceneGraphData;
}

function addApiCalls (hub, io, socket) {
    function idSearch (id) {
        return {_id: mongo.ObjectId(id)};
    }

    function _findScene (sceneId, cb) {
        return hub.db.mediaScenes.findOne(idSearch(sceneId), cb);
    }

    function _findSceneGraph(sceneGraphId, cb) {
        return hub.db.mediaSceneGraphs.findOne(idSearch(sceneGraphId), cb);
    }

    socket.on('listScenes', function(callback) {
<<<<<<< HEAD
        console.log("listScenes groupID: " + socket.groupID);
=======
        //console.log("listScenes");
        //console.log("groupID: " + socket.groupID);
>>>>>>> 8991a4e77945f1cf83f6a7e6043731c4202475c9
        //AJF: if the groupID is 0 (admin) then list all scenes
        if(socket.groupID == 0)
            hub.db.mediaScenes.find({'$query': {}, '$orderby': {name: 1}}, {name: 1}, callback);
        else
        {
            hub.db.mediaScenes.find({'$query': {_groupID: socket.groupID}, '$orderby': {name: 1}}, {name: 1}, callback);
        }
<<<<<<< HEAD
=======
    });

    socket.on('listSceneGraphs', function(callback) {
        hub.db.mediaSceneGraphs.find({'$query': {}}, callback);
>>>>>>> 8991a4e77945f1cf83f6a7e6043731c4202475c9
    });

    socket.on('saveScene', function(sceneData, callback) {
        try {

            console.log("saveScene");

            var data = validateScene(sceneData);

            //AJF: save the groupID acquired from the socket
            data._groupID = socket.groupID;

            hub.db.mediaScenes.save(data, function(err, scene) {
                io.to(scene._id.toString()).emit('sceneUpdate', scene);

                if (callback) {
                    callback(err, scene);
                }
            });
        } catch(err) {
            if (callback) {
                callback(err.message);
            }
        }
    });

    socket.on('saveSceneGraph', function(sceneGraphData, callback) {
        try {
            var data = validateSceneGraph(sceneGraphData);

            hub.db.mediaSceneGraphs.save(data, function(err, sceneGraph) {
                io.to(sceneGraph._id.toString()).emit('sceneGraphUpdate', sceneGraph);

                if (callback) {
                    callback(err, sceneGraph);
                }
            });

        } catch(err) {
            if(callback) {
                callback(err.message);
            }
        }
    });

    socket.on('loadScene', _findScene);

    socket.on('loadSceneGraph', _findSceneGraph);

    //TODO loadSceneGraphByName

    socket.on('loadSceneByName', function(name, callback) {
        hub.db.mediaScenes.findOne({name: name}, callback);
    });

    socket.on('deleteScene', function(sceneId, callback) {
        hub.db.mediaScenes.remove(idSearch(sceneId), function(err) {
            if (callback) {
                callback(err);
            }
        });
    });

    socket.on('subScene', function(sceneId, callback) {
        _findScene(sceneId, function(err, scene) {
            socket.join(sceneId);

            if (callback) {
                callback(err, scene);
            }
        });
    });

    socket.on('unsubScene', function(sceneId, callback) {
        _findScene(sceneId, function(err, scene) {
            socket.leave(sceneId);
            if (callback) {
                callback(err);
            }
        });
    });

    socket.on('sendCommand', function(roomId, commandName, commandValue) {
        io.to(roomId).emit('command', {name: commandName, value: commandValue});
    });

    socket.on('register', function(roomId) {
        socket.join(roomId);
    });
}

var Hub = function(config) {
    this.config = config;
    this.db = mongo.connect(config.mongo, ['mediaScenes', 'sessions', 'mediaSceneGraphs']);
    session.setClient(this.db);
};

Hub.prototype.listen = function(callback) {
    var self = this,
        app = express(),
        server = http.Server(app),
        io = new Server(server);

    this.server = server;

    app.use(cors());

    // require a valid session token for all api calls
    var validateSession =  function(req, res, next) {
        session.find(req.query.token, function(err, data) {
            if (err || ! data) {
                res.sendStatus(401);
            } else {
                next();
            }
        });
    };

    // allow cross origin requests
    io.set('origins', '*:*');
    io.sockets.on('connection', function (socket) {
        var disconnectTimer = setTimeout(function() {
            socket.disconnect();
        }, 10000);

        socket.on('auth', function (creds, callback) {

            function succeed (record) {
<<<<<<< HEAD
                //AJF: set the groupID on the socket to be used in further local calls
=======
                //AJF: set the groupID on the socket to be used in further calls
>>>>>>> 8991a4e77945f1cf83f6a7e6043731c4202475c9
                socket.groupID = record._groupID;
                addApiCalls(self, io, socket);
                clearTimeout(disconnectTimer);
                callback(null, record._id.toString(), socket.id, record._groupID.toString());//AJF: try to return the groupID...
            }

            function fail (msg) {
                callback(msg);
                socket.disconnect();
            }

            if (creds.hasOwnProperty('password') && creds.password && creds.password !== '') {
                //AJF: Compares the passwords and determines what group the user logging into belongs to
                if ( bcrypt.compareSync(creds.password, self.config.secret) ) {
                    session.create(0, throwErr(succeed));
                } else if (bcrypt.compareSync(creds.password, self.config.secret_1)) {
                    session.create(1, throwErr(succeed));
                } else if (bcrypt.compareSync(creds.password, self.config.secret_2)) {
                    session.create(2, throwErr(succeed));
                } else if (bcrypt.compareSync(creds.password, self.config.secret_101)) {
                    session.create(101, throwErr(succeed));
                } else if (bcrypt.compareSync(creds.password, self.config.secret_102)) {
                    session.create(102, throwErr(succeed));
                } else if (bcrypt.compareSync(creds.password, self.config.secret_103)) {
                    session.create(103, throwErr(succeed));
                } else if (bcrypt.compareSync(creds.password, self.config.secret_104)) {
                    session.create(104, throwErr(succeed));
                } else if (bcrypt.compareSync(creds.password, self.config.secret_105)) {
                    session.create(105, throwErr(succeed));
                } else if (bcrypt.compareSync(creds.password, self.config.secret_106)) {
                    session.create(106, throwErr(succeed));
                } else {
                    fail('Invalid Password');
                }

            } else if (creds.hasOwnProperty('token') && creds.token && creds.token !== '') {
                console.log("Finding session via token");
                session.find(creds.token, function(err, data) {
                        if (data) {
                            succeed(data);
                        } else {
                            fail('Invalid Token');
                        }
                    });
            } else {
                fail('Password must be provided');
            }
        });

    });

    server.listen(self.config.port, callback);
};

Hub.prototype.close = function(cb) {
    this.server.close(cb);
};

module.exports = {
    createHub: function(mongoUrl) {
        return new Hub(mongoUrl);
    }
};
<commit_msg>Removed <<<<<< HEAD etc<commit_after>'use strict';

var bcrypt = require('bcrypt-nodejs'),
    Server = require('socket.io'),
    express = require('express'),
    http = require('http'),
    path = require('path'),
    mongo = require('mongojs');

var util = require('util');
var session = require('./session');
var _ = require('lodash');
var cors = require('cors');

var _validTokens = {};

function throwErr (func) {
    return function(err, data) {
        if (err) {
            throw err;
        } else {
            func.call(null, data);
        }
    };
}

function validateScene (sceneData) {
    // convert _id so mongo recognizes it
    if (sceneData.hasOwnProperty('_id')) {
        sceneData._id = mongo.ObjectId(sceneData._id);
    }

    // add _ids to all mediaObjects
    if (sceneData.hasOwnProperty('scene')) {
        if (! Array.isArray(sceneData.scene) ) {
            throw new Error('"scene" property must be an array of objects');
        } else {
            sceneData.scene.forEach(function(mediaObject) {
                if (! mediaObject.hasOwnProperty('_id')) {
                    mediaObject._id = mongo.ObjectId();
                }
            });
        }
    }

    return sceneData;
}

function validateSceneGraph (sceneGraphData) {
    //convert _id so mongo recognizes it
    if (sceneGraphData.hasOwnProperty('_id')) {
        sceneGraphData._id = mongo.ObjectId(sceneGraphData._id);
    }

    //TODO most likely more detailed validation can occur

    return sceneGraphData;
}

function addApiCalls (hub, io, socket) {
    function idSearch (id) {
        return {_id: mongo.ObjectId(id)};
    }

    function _findScene (sceneId, cb) {
        return hub.db.mediaScenes.findOne(idSearch(sceneId), cb);
    }

    function _findSceneGraph(sceneGraphId, cb) {
        return hub.db.mediaSceneGraphs.findOne(idSearch(sceneGraphId), cb);
    }

    socket.on('listScenes', function(callback) {

        console.log("listScenes groupID: " + socket.groupID);

        //console.log("listScenes");
        //console.log("groupID: " + socket.groupID);

        //AJF: if the groupID is 0 (admin) then list all scenes
        if(socket.groupID == 0)
            hub.db.mediaScenes.find({'$query': {}, '$orderby': {name: 1}}, {name: 1}, callback);
        else
        {
            hub.db.mediaScenes.find({'$query': {_groupID: socket.groupID}, '$orderby': {name: 1}}, {name: 1}, callback);
        }

    });

    socket.on('listSceneGraphs', function(callback) {
        hub.db.mediaSceneGraphs.find({'$query': {}}, callback);

    });

    socket.on('saveScene', function(sceneData, callback) {
        try {

            console.log("saveScene");

            var data = validateScene(sceneData);

            //AJF: save the groupID acquired from the socket
            data._groupID = socket.groupID;

            hub.db.mediaScenes.save(data, function(err, scene) {
                io.to(scene._id.toString()).emit('sceneUpdate', scene);

                if (callback) {
                    callback(err, scene);
                }
            });
        } catch(err) {
            if (callback) {
                callback(err.message);
            }
        }
    });

    socket.on('saveSceneGraph', function(sceneGraphData, callback) {
        try {
            var data = validateSceneGraph(sceneGraphData);

            hub.db.mediaSceneGraphs.save(data, function(err, sceneGraph) {
                io.to(sceneGraph._id.toString()).emit('sceneGraphUpdate', sceneGraph);

                if (callback) {
                    callback(err, sceneGraph);
                }
            });

        } catch(err) {
            if(callback) {
                callback(err.message);
            }
        }
    });

    socket.on('loadScene', _findScene);

    socket.on('loadSceneGraph', _findSceneGraph);

    //TODO loadSceneGraphByName

    socket.on('loadSceneByName', function(name, callback) {
        hub.db.mediaScenes.findOne({name: name}, callback);
    });

    socket.on('deleteScene', function(sceneId, callback) {
        hub.db.mediaScenes.remove(idSearch(sceneId), function(err) {
            if (callback) {
                callback(err);
            }
        });
    });

    socket.on('subScene', function(sceneId, callback) {
        _findScene(sceneId, function(err, scene) {
            socket.join(sceneId);

            if (callback) {
                callback(err, scene);
            }
        });
    });

    socket.on('unsubScene', function(sceneId, callback) {
        _findScene(sceneId, function(err, scene) {
            socket.leave(sceneId);
            if (callback) {
                callback(err);
            }
        });
    });

    socket.on('sendCommand', function(roomId, commandName, commandValue) {
        io.to(roomId).emit('command', {name: commandName, value: commandValue});
    });

    socket.on('register', function(roomId) {
        socket.join(roomId);
    });
}

var Hub = function(config) {
    this.config = config;
    this.db = mongo.connect(config.mongo, ['mediaScenes', 'sessions', 'mediaSceneGraphs']);
    session.setClient(this.db);
};

Hub.prototype.listen = function(callback) {
    var self = this,
        app = express(),
        server = http.Server(app),
        io = new Server(server);

    this.server = server;

    app.use(cors());

    // require a valid session token for all api calls
    var validateSession =  function(req, res, next) {
        session.find(req.query.token, function(err, data) {
            if (err || ! data) {
                res.sendStatus(401);
            } else {
                next();
            }
        });
    };

    // allow cross origin requests
    io.set('origins', '*:*');
    io.sockets.on('connection', function (socket) {
        var disconnectTimer = setTimeout(function() {
            socket.disconnect();
        }, 10000);

        socket.on('auth', function (creds, callback) {

            function succeed (record) {

                //AJF: set the groupID on the socket to be used in further local calls

                //AJF: set the groupID on the socket to be used in further calls

                socket.groupID = record._groupID;
                addApiCalls(self, io, socket);
                clearTimeout(disconnectTimer);
                callback(null, record._id.toString(), socket.id, record._groupID.toString());//AJF: try to return the groupID...
            }

            function fail (msg) {
                callback(msg);
                socket.disconnect();
            }

            if (creds.hasOwnProperty('password') && creds.password && creds.password !== '') {
                //AJF: Compares the passwords and determines what group the user logging into belongs to
                if ( bcrypt.compareSync(creds.password, self.config.secret) ) {
                    session.create(0, throwErr(succeed));
                } else if (bcrypt.compareSync(creds.password, self.config.secret_1)) {
                    session.create(1, throwErr(succeed));
                } else if (bcrypt.compareSync(creds.password, self.config.secret_2)) {
                    session.create(2, throwErr(succeed));
                } else if (bcrypt.compareSync(creds.password, self.config.secret_101)) {
                    session.create(101, throwErr(succeed));
                } else if (bcrypt.compareSync(creds.password, self.config.secret_102)) {
                    session.create(102, throwErr(succeed));
                } else if (bcrypt.compareSync(creds.password, self.config.secret_103)) {
                    session.create(103, throwErr(succeed));
                } else if (bcrypt.compareSync(creds.password, self.config.secret_104)) {
                    session.create(104, throwErr(succeed));
                } else if (bcrypt.compareSync(creds.password, self.config.secret_105)) {
                    session.create(105, throwErr(succeed));
                } else if (bcrypt.compareSync(creds.password, self.config.secret_106)) {
                    session.create(106, throwErr(succeed));
                } else {
                    fail('Invalid Password');
                }

            } else if (creds.hasOwnProperty('token') && creds.token && creds.token !== '') {
                console.log("Finding session via token");
                session.find(creds.token, function(err, data) {
                        if (data) {
                            succeed(data);
                        } else {
                            fail('Invalid Token');
                        }
                    });
            } else {
                fail('Password must be provided');
            }
        });

    });

    server.listen(self.config.port, callback);
};

Hub.prototype.close = function(cb) {
    this.server.close(cb);
};

module.exports = {
    createHub: function(mongoUrl) {
        return new Hub(mongoUrl);
    }
};
<|endoftext|>


<commit_before>/**
 * XIN SPA Framework
 *
 * MIT LICENSE
 *
 * Copyright (c) 2014 PT Sagara Xinix Solusitama
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * @author      Ganesha <reekoheek@gmail.com>
 * @copyright   2014 PT Sagara Xinix Solusitama
 * @link        http://xinix.co.id/products/xin
 * @license     https://raw.github.com/reekoheek/xin/master/LICENSE
 * @package     xin
 *
 */

;(function(xin) {
    "use strict";

    var IoC = function() {
        this.initialize.apply(this, arguments);
    };

    _.extend(IoC.prototype, {
        context: {},
        fallbackContext: null,

        initialize: function(options) {
            options = options || {};
            this.fallbackContext = options.fallbackContext || window;

            this.aliases = {
                'app': 'xin.App',
                'view': 'xin.ui.Outlet',
                'layout': 'xin.ui.Layout',
                'header': 'xin.ui.Header',
                'pane': 'xin.ui.Pane',
                'list': 'xin.ui.List',
                'list-item': 'xin.ui.List.Item',
                'drawer': 'xin.ui.Drawer',
                'navbar': 'xin.ui.Navbar'
            };
        },

        clear: function() {
            this.context = {};
            this.context.prototype = this.fallbackContext;
        },

        get: function(key) {
            if (!key) {
                var target = document.getElementsByClassName('xin-show'),
                    id     = target.item().dataset.id;
                if(!id){
                    console.error('IoC cannot get from key:', key);
                    return;
                }
                key = id;
            }

            var keys = key.split('.'),
                from = this.context,
                found;

            from = this.context;
            found = _.every(keys, function(k) {
                from = from[k] || undefined;
                return (from !== undefined);
            });

            if (found) return from;

            from = this.fallbackContext;
            found = _.every(keys, function(k) {
                from = from[k] || undefined;
                return (from !== undefined);
            });

            return (found) ? from : undefined;

        },

        set: function(key, value) {
            if (!key) {
                console.error('cannot set key:', key, 'value:', value);
                return;
            }

            var keys = key.split('.'),
                to = this.context,
                index = 0;

            var found = _.each(keys, function(k) {
                index++;
                if (index == keys.length) {
                    to[k] = value;
                } else {
                    if (!to[k]) {
                        to[k] = {};
                    }
                    to = to[k];
                }
            });
        },

        createObject: function(Constructor, options) {
            var deferred = xin.Deferred(),
                object,
                id = (options) ? options.id : null,
                args = [],
                f;

            for(var i in arguments) {
                args.push(arguments[i]);
            }

            if (Constructor.prototype instanceof Backbone.Collection || Constructor.prototype instanceof Backbone.Model) {
                args[0] = null;
            } else {
                args = args.slice(1);
            }

            // TODO if there is any other technique that works cross browser
            // for construct new object based on dynamic length arguments
            switch (args.length) {
                case 0:
                    object = new Constructor();
                    break;
                case 1:
                    object = new Constructor(args[0]);
                    break;
                case 2:
                    object = new Constructor(args[0], args[1]);
                    break;
                case 3:
                    object = new Constructor(args[0], args[1], args[2]);
                    break;
                case 4:
                    object = new Constructor(args[0], args[1], args[2], args[3]);
                    break;
                case 5:
                    object = new Constructor(args[0], args[1], args[2], args[3], args[4]);
                    break;
            }

            object.app = this.app;

            if (id) {
                this.set(id, object);
            }

            if (options.show) {
                f = this.get(options.show);
                if (f && typeof object.on == 'function') {
                    object.on('show', f);
                }
            }

            if (options.init) {
                f = this.get(options.init);
                f.apply(object);
            }

            // Backbone.trigger('xin-init', object);

            deferred.resolve(object);
            return deferred.promise();
        },

        resolveAlias: function(key) {
            var found;
            do {
                found = false;
                if (this.aliases[key]) {
                    key = this.aliases[key];
                    found = true;
                }
            } while(found);
            return key;
        },

        resolve: function(key) {
            var index,
                resolver,
                type = '',
                deferred = xin.Deferred(),
                args = [],
                value;

            key = this.resolveAlias(key);

            if (!key) return deferred.reject(new Error('Key not found or cannot resolve!')).promise();

            for(var i in arguments) {
                args.push(arguments[i]);
            }

            if (typeof key == 'function') {

                // if type of key is function, we assume it as a Class constructor
                // so we will create object based on that Class constructor
                this.createObject.apply(this, args).then(deferred.resolve, deferred.reject);

            } else if (typeof key == 'string') {

                // if type of key is string, we parse it, is there any type of
                // resolver ?
                index = key.indexOf(':');
                if (index >= 0) {
                    type = key.substr(0, index);
                    key = key.substr(index + 1);
                }

                if (type) {
                    // if there is any type of resolver, try to resolve
                    try {
                        resolver = IoC.getResolver(type);
                        args[0] = key;
                        resolver.apply(this, args).then(deferred.resolve, deferred.reject);
                    } catch(e) {
                        deferred.resolve(key);
                    }
                } else {
                    // no type of resolver, then resolve the key as it is
                    value = this.get(key);
                    if (!value) {
                        throw new Error('Key: ' + key + ' not found!');
                    }

                    value.xrole = key;
                    if (typeof value == 'function') {
                        var first = _.last(key.split('.'))[0];
                        if (first.toUpperCase() == first) {
                            args[0] = value;
                            this.createObject(args[0], args[1]).then(deferred.resolve, deferred.reject);
                        } else {
                            deferred.resolve(value.apply(undefined, args.slice(1)));
                        }
                    } else {
                        deferred.resolve(value);
                    }
                }

            } else {
                // return the object as is
                deferred.resolve(key);
            }

            return deferred.promise();
        }
    });

    _.extend(IoC, {
        resolvers: {

            xhr: function(key) {
                // key is in URL form
                return xin.$.get(key).then(null, function(xhr, state, message) {
                    return xin.Deferred().reject(new Error(message)).promise();
                }).promise();
            },

            template: function(key) {
                if (key.indexOf('/') < 0) {
                    var $template = xin.$('script#' + key);
                    if ($template.length > 0) {
                        return xin.Deferred().resolve(_.template($template.html())).promise();
                    }
                }

                var resolver = IoC.getResolver('xhr');

                return resolver(key).then(function(data) {
                    return xin.Deferred().resolve(_.template(data));
                }, null);
            },

            model: function(key) {
                var deferred = xin.Deferred();

                xin.when(this.resolve(key)).then(function(model) {
                    if (!(model instanceof Backbone.Model)) {
                        model = new Backbone.Model(model);
                    }
                    deferred.resolve(model);
                });

                return deferred.promise();
            },

            collection: function(key) {
                var deferred = xin.Deferred();

                xin.when(this.resolve(key)).then(function(collection) {
                    if (!(collection instanceof Backbone.Collection)) {
                        collection = new Backbone.Collection(collection);
                    }
                    deferred.resolve(collection);
                });

                return deferred.promise();
            }

        },

        addResolver: function(type, resolver) {
            this.resolvers[type] = resolver;
        },

        getResolver: function(type) {
            var resolver = this.resolvers[type];
            if (typeof resolver == 'function') {
                return resolver;
            } else if (typeof resolver == 'object' && typeof resolver.resolve == 'function') {
                return resolver.resolve;
            }
            throw new Error('IoC Resolver [' + type + '] not found!');
        }
    });

    xin.set('xin.IoC', IoC);

})(window.xin);
<commit_msg>fixing get current active view<commit_after>/**
 * XIN SPA Framework
 *
 * MIT LICENSE
 *
 * Copyright (c) 2014 PT Sagara Xinix Solusitama
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * @author      Ganesha <reekoheek@gmail.com>
 * @copyright   2014 PT Sagara Xinix Solusitama
 * @link        http://xinix.co.id/products/xin
 * @license     https://raw.github.com/reekoheek/xin/master/LICENSE
 * @package     xin
 *
 */

;(function(xin) {
    "use strict";

    var IoC = function() {
        this.initialize.apply(this, arguments);
    };

    _.extend(IoC.prototype, {
        context: {},
        fallbackContext: null,

        initialize: function(options) {
            options = options || {};
            this.fallbackContext = options.fallbackContext || window;

            this.aliases = {
                'app': 'xin.App',
                'view': 'xin.ui.Outlet',
                'layout': 'xin.ui.Layout',
                'header': 'xin.ui.Header',
                'pane': 'xin.ui.Pane',
                'list': 'xin.ui.List',
                'list-item': 'xin.ui.List.Item',
                'drawer': 'xin.ui.Drawer',
                'navbar': 'xin.ui.Navbar'
            };
        },

        clear: function() {
            this.context = {};
            this.context.prototype = this.fallbackContext;
        },

        get: function(key) {
            if (!key) {
                var data = $('.xin-show').data();

                if(data.instance) return data.instance;
                if(!data.id){
                    console.error('IoC cannot get from key:', key);
                    return;
                }
                key = data.id;
            }

            var keys = key.split('.'),
                from = this.context,
                found;

            from = this.context;
            found = _.every(keys, function(k) {
                from = from[k] || undefined;
                return (from !== undefined);
            });

            if (found) return from;

            from = this.fallbackContext;
            found = _.every(keys, function(k) {
                from = from[k] || undefined;
                return (from !== undefined);
            });

            return (found) ? from : undefined;
        },

        set: function(key, value) {
            if (!key) {
                console.error('cannot set key:', key, 'value:', value);
                return;
            }

            var keys = key.split('.'),
                to = this.context,
                index = 0;

            var found = _.each(keys, function(k) {
                index++;
                if (index == keys.length) {
                    to[k] = value;
                } else {
                    if (!to[k]) {
                        to[k] = {};
                    }
                    to = to[k];
                }
            });
        },

        createObject: function(Constructor, options) {
            var deferred = xin.Deferred(),
                object,
                id = (options) ? options.id : null,
                args = [],
                f;

            for(var i in arguments) {
                args.push(arguments[i]);
            }

            if (Constructor.prototype instanceof Backbone.Collection || Constructor.prototype instanceof Backbone.Model) {
                args[0] = null;
            } else {
                args = args.slice(1);
            }

            // TODO if there is any other technique that works cross browser
            // for construct new object based on dynamic length arguments
            switch (args.length) {
                case 0:
                    object = new Constructor();
                    break;
                case 1:
                    object = new Constructor(args[0]);
                    break;
                case 2:
                    object = new Constructor(args[0], args[1]);
                    break;
                case 3:
                    object = new Constructor(args[0], args[1], args[2]);
                    break;
                case 4:
                    object = new Constructor(args[0], args[1], args[2], args[3]);
                    break;
                case 5:
                    object = new Constructor(args[0], args[1], args[2], args[3], args[4]);
                    break;
            }

            object.app = this.app;

            if (id) {
                this.set(id, object);
            }

            if (options.show) {
                f = this.get(options.show);
                if (f && typeof object.on == 'function') {
                    object.on('show', f);
                }
            }

            if (options.init) {
                f = this.get(options.init);
                f.apply(object);
            }

            // Backbone.trigger('xin-init', object);

            deferred.resolve(object);
            return deferred.promise();
        },

        resolveAlias: function(key) {
            var found;
            do {
                found = false;
                if (this.aliases[key]) {
                    key = this.aliases[key];
                    found = true;
                }
            } while(found);
            return key;
        },

        resolve: function(key) {
            var index,
                resolver,
                type = '',
                deferred = xin.Deferred(),
                args = [],
                value;

            key = this.resolveAlias(key);

            if (!key) return deferred.reject(new Error('Key not found or cannot resolve!')).promise();

            for(var i in arguments) {
                args.push(arguments[i]);
            }

            if (typeof key == 'function') {

                // if type of key is function, we assume it as a Class constructor
                // so we will create object based on that Class constructor
                this.createObject.apply(this, args).then(deferred.resolve, deferred.reject);

            } else if (typeof key == 'string') {

                // if type of key is string, we parse it, is there any type of
                // resolver ?
                index = key.indexOf(':');
                if (index >= 0) {
                    type = key.substr(0, index);
                    key = key.substr(index + 1);
                }

                if (type) {
                    // if there is any type of resolver, try to resolve
                    try {
                        resolver = IoC.getResolver(type);
                        args[0] = key;
                        resolver.apply(this, args).then(deferred.resolve, deferred.reject);
                    } catch(e) {
                        deferred.resolve(key);
                    }
                } else {
                    // no type of resolver, then resolve the key as it is
                    value = this.get(key);
                    if (!value) {
                        throw new Error('Key: ' + key + ' not found!');
                    }

                    value.xrole = key;
                    if (typeof value == 'function') {
                        var first = _.last(key.split('.'))[0];
                        if (first.toUpperCase() == first) {
                            args[0] = value;
                            this.createObject(args[0], args[1]).then(deferred.resolve, deferred.reject);
                        } else {
                            deferred.resolve(value.apply(undefined, args.slice(1)));
                        }
                    } else {
                        deferred.resolve(value);
                    }
                }

            } else {
                // return the object as is
                deferred.resolve(key);
            }

            return deferred.promise();
        }
    });

    _.extend(IoC, {
        resolvers: {

            xhr: function(key) {
                // key is in URL form
                return xin.$.get(key).then(null, function(xhr, state, message) {
                    return xin.Deferred().reject(new Error(message)).promise();
                }).promise();
            },

            template: function(key) {
                if (key.indexOf('/') < 0) {
                    var $template = xin.$('script#' + key);
                    if ($template.length > 0) {
                        return xin.Deferred().resolve(_.template($template.html())).promise();
                    }
                }

                var resolver = IoC.getResolver('xhr');

                return resolver(key).then(function(data) {
                    return xin.Deferred().resolve(_.template(data));
                }, null);
            },

            model: function(key) {
                var deferred = xin.Deferred();

                xin.when(this.resolve(key)).then(function(model) {
                    if (!(model instanceof Backbone.Model)) {
                        model = new Backbone.Model(model);
                    }
                    deferred.resolve(model);
                });

                return deferred.promise();
            },

            collection: function(key) {
                var deferred = xin.Deferred();

                xin.when(this.resolve(key)).then(function(collection) {
                    if (!(collection instanceof Backbone.Collection)) {
                        collection = new Backbone.Collection(collection);
                    }
                    deferred.resolve(collection);
                });

                return deferred.promise();
            }

        },

        addResolver: function(type, resolver) {
            this.resolvers[type] = resolver;
        },

        getResolver: function(type) {
            var resolver = this.resolvers[type];
            if (typeof resolver == 'function') {
                return resolver;
            } else if (typeof resolver == 'object' && typeof resolver.resolve == 'function') {
                return resolver.resolve;
            }
            throw new Error('IoC Resolver [' + type + '] not found!');
        }
    });

    xin.set('xin.IoC', IoC);

})(window.xin);
<|endoftext|>


<commit_before>/*
Copyright 2017 New Vector Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import EventEmitter from 'events';
import Promise from 'bluebird';

const BULK_REQUEST_DEBOUNCE_MS = 200;

// Does the server support groups? Assume yes until we receive M_UNRECOGNIZED.
// If true, flair can function and we should keep sending requests for groups and avatars.
let groupSupport = true;

const USER_GROUPS_CACHE_BUST_MS = 1800000; // 30 mins
const GROUP_PROFILES_CACHE_BUST_MS = 1800000; // 30 mins

/**
 * Stores data used by <Flair/>
 */
class FlairStore extends EventEmitter {
    constructor(matrixClient) {
        super();
        this._matrixClient = matrixClient;
        this._userGroups = {
            // $userId: ['+group1:domain', '+group2:domain', ...]
        };
        this._groupProfiles = {
            //  $groupId: {
            //      avatar_url: 'mxc://...'
            //  }
        };
        this._usersPending = {
            //  $userId: {
            //      prom: Promise
            //      resolve: () => {}
            //      reject: () => {}
            //  }
        };

        this._debounceTimeoutID = null;
    }

    groupSupport() {
        return groupSupport;
    }

    invalidatePublicisedGroups(userId) {
        delete this._userGroups[userId];
    }

    getPublicisedGroupsCached(matrixClient, userId) {
        if (this._userGroups[userId]) {
            return Promise.resolve(this._userGroups[userId]);
        }

        // Bulk lookup ongoing, return promise to resolve/reject
        if (this._usersPending[userId]) {
            return this._usersPending[userId].prom;
        }

        this._usersPending[userId] = {};
        this._usersPending[userId].prom = new Promise((resolve, reject) => {
            this._usersPending[userId].resolve = resolve;
            this._usersPending[userId].reject = reject;
        }).then((groups) => {
            this._userGroups[userId] = groups;
            setTimeout(() => {
                delete this._userGroups[userId];
            }, USER_GROUPS_CACHE_BUST_MS);
            return this._userGroups[userId];
        }).catch((err) => {
            // Indicate whether the homeserver supports groups
            if (err.errcode === 'M_UNRECOGNIZED') {
                console.warn('Cannot display flair, server does not support groups');
                groupSupport = false;
                // Return silently to avoid spamming for non-supporting servers
                return;
            }
            console.error('Could not get groups for user', this.props.userId, err);
            throw err;
        }).finally(() => {
            delete this._usersPending[userId];
        });

        // This debounce will allow consecutive requests for the public groups of users that
        // are sent in intervals of < BULK_REQUEST_DEBOUNCE_MS to be batched and only requested
        // when no more requests are received within the next BULK_REQUEST_DEBOUNCE_MS. The naive
        // implementation would do a request that only requested the groups for `userId`, leading
        // to a worst and best case of 1 user per request. This implementation's worst is still
        // 1 user per request but only if the requests are > BULK_REQUEST_DEBOUNCE_MS apart and the
        // best case is N users per request.
        //
        // This is to reduce the number of requests made whilst trading off latency when viewing
        // a Flair component.
        if (this._debounceTimeoutID) clearTimeout(this._debounceTimeoutID);
        this._debounceTimeoutID = setTimeout(() => {
            this._batchedGetPublicGroups(matrixClient);
        }, BULK_REQUEST_DEBOUNCE_MS);

        return this._usersPending[userId].prom;
    }

    async _batchedGetPublicGroups(matrixClient) {
        // Take the userIds from the keys of this._usersPending
        const usersInFlight = Object.keys(this._usersPending);
        let resp = {
            users: [],
        };
        try {
            resp = await matrixClient.getPublicisedGroups(usersInFlight);
        } catch (err) {
            // Propagate the same error to all usersInFlight
            usersInFlight.forEach((userId) => {
                this._usersPending[userId].reject(err);
            });
            return;
        }
        const updatedUserGroups = resp.users;
        usersInFlight.forEach((userId) => {
            if (this._usersPending[userId]) {
                this._usersPending[userId].resolve(updatedUserGroups[userId] || []);
            } else {
                console.error("Promise vanished for resolving groups for " + userId);
            }
        });
    }

    async getGroupProfileCached(matrixClient, groupId) {
        if (this._groupProfiles[groupId]) {
            return this._groupProfiles[groupId];
        }

        const profile = await matrixClient.getGroupProfile(groupId);
        this._groupProfiles[groupId] = {
            groupId,
            avatarUrl: profile.avatar_url,
            name: profile.name,
            shortDescription: profile.short_description,
        };
        setTimeout(() => {
            delete this._groupProfiles[groupId];
        }, GROUP_PROFILES_CACHE_BUST_MS);

        return this._groupProfiles[groupId];
    }
}

if (global.singletonFlairStore === undefined) {
    global.singletonFlairStore = new FlairStore();
}
export default global.singletonFlairStore;
<commit_msg>Fix multiple requests for publicised groups of given user<commit_after>/*
Copyright 2017 New Vector Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import EventEmitter from 'events';
import Promise from 'bluebird';

const BULK_REQUEST_DEBOUNCE_MS = 200;

// Does the server support groups? Assume yes until we receive M_UNRECOGNIZED.
// If true, flair can function and we should keep sending requests for groups and avatars.
let groupSupport = true;

const USER_GROUPS_CACHE_BUST_MS = 1800000; // 30 mins
const GROUP_PROFILES_CACHE_BUST_MS = 1800000; // 30 mins

/**
 * Stores data used by <Flair/>
 */
class FlairStore extends EventEmitter {
    constructor(matrixClient) {
        super();
        this._matrixClient = matrixClient;
        this._userGroups = {
            // $userId: ['+group1:domain', '+group2:domain', ...]
        };
        this._groupProfiles = {
            //  $groupId: {
            //      avatar_url: 'mxc://...'
            //  }
        };
        this._usersPending = {
            //  $userId: {
            //      prom: Promise
            //      resolve: () => {}
            //      reject: () => {}
            //  }
        };

        this._debounceTimeoutID = null;
    }

    groupSupport() {
        return groupSupport;
    }

    invalidatePublicisedGroups(userId) {
        delete this._userGroups[userId];
    }

    getPublicisedGroupsCached(matrixClient, userId) {
        if (this._userGroups[userId]) {
            return Promise.resolve(this._userGroups[userId]);
        }

        // Bulk lookup ongoing, return promise to resolve/reject
        if (this._usersPending[userId] || this._usersInFlight[userId]) {
            return this._usersPending[userId].prom;
        }

        this._usersPending[userId] = {};
        this._usersPending[userId].prom = new Promise((resolve, reject) => {
            this._usersPending[userId].resolve = resolve;
            this._usersPending[userId].reject = reject;
        }).then((groups) => {
            this._userGroups[userId] = groups;
            setTimeout(() => {
                delete this._userGroups[userId];
            }, USER_GROUPS_CACHE_BUST_MS);
            return this._userGroups[userId];
        }).catch((err) => {
            // Indicate whether the homeserver supports groups
            if (err.errcode === 'M_UNRECOGNIZED') {
                console.warn('Cannot display flair, server does not support groups');
                groupSupport = false;
                // Return silently to avoid spamming for non-supporting servers
                return;
            }
            console.error('Could not get groups for user', this.props.userId, err);
            throw err;
        }).finally(() => {
            delete this._usersInFlight[userId];
        });

        // This debounce will allow consecutive requests for the public groups of users that
        // are sent in intervals of < BULK_REQUEST_DEBOUNCE_MS to be batched and only requested
        // when no more requests are received within the next BULK_REQUEST_DEBOUNCE_MS. The naive
        // implementation would do a request that only requested the groups for `userId`, leading
        // to a worst and best case of 1 user per request. This implementation's worst is still
        // 1 user per request but only if the requests are > BULK_REQUEST_DEBOUNCE_MS apart and the
        // best case is N users per request.
        //
        // This is to reduce the number of requests made whilst trading off latency when viewing
        // a Flair component.
        if (this._debounceTimeoutID) clearTimeout(this._debounceTimeoutID);
        this._debounceTimeoutID = setTimeout(() => {
            this._batchedGetPublicGroups(matrixClient);
        }, BULK_REQUEST_DEBOUNCE_MS);

        return this._usersPending[userId].prom;
    }

    async _batchedGetPublicGroups(matrixClient) {
        // Move users pending to users in flight
        this._usersInFlight = this._usersPending;
        this._usersPending = {};

        let resp = {
            users: [],
        };
        try {
            resp = await matrixClient.getPublicisedGroups(Object.keys(this._usersInFlight));
        } catch (err) {
            // Propagate the same error to all usersInFlight
            Object.keys(this._usersInFlight).forEach((userId) => {
                this._usersInFlight[userId].reject(err);
            });
            return;
        }
        const updatedUserGroups = resp.users;
        Object.keys(this._usersInFlight).forEach((userId) => {
            this._usersInFlight[userId].resolve(updatedUserGroups[userId] || []);
        });
    }

    async getGroupProfileCached(matrixClient, groupId) {
        if (this._groupProfiles[groupId]) {
            return this._groupProfiles[groupId];
        }

        const profile = await matrixClient.getGroupProfile(groupId);
        this._groupProfiles[groupId] = {
            groupId,
            avatarUrl: profile.avatar_url,
            name: profile.name,
            shortDescription: profile.short_description,
        };
        setTimeout(() => {
            delete this._groupProfiles[groupId];
        }, GROUP_PROFILES_CACHE_BUST_MS);

        return this._groupProfiles[groupId];
    }
}

if (global.singletonFlairStore === undefined) {
    global.singletonFlairStore = new FlairStore();
}
export default global.singletonFlairStore;
<|endoftext|>